Refresh when streamer goes live

Also unmutes and refreshes on video player error.

  1. // ==UserScript==
  2. // @name Refresh when streamer goes live
  3. // @namespace https://github.com/Dragosarus/Userscripts/
  4. // @version 2025-04-16
  5. // @description Also unmutes and refreshes on video player error.
  6. // @author Dragosarus
  7. // @match https://www.twitch.tv/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=twitch.tv
  9. // @grant GM_registerMenuCommand
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @compatible firefox
  13. // @license Unlicense
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. // Time to wait for the Twitch UI to load
  20. const INITIAL_DELAY_MS = 10000;
  21.  
  22. // Check interval
  23. const CHECK_INTERVAL_MS = 1000;
  24.  
  25. // Enables console logs ("rwsgl: ...")
  26. const ENABLE_LOGS = true;
  27.  
  28. // Unmute after reloading
  29. const UNMUTE_AFTER_RELOAD = true;
  30.  
  31. // Refresh if the player encounters an error
  32. const RELOAD_ON_ERROR = true;
  33.  
  34. // Will refresh the page regardless of live status.
  35. const DEBUG = false;
  36.  
  37. const SELECTORS = {
  38. // The "LIVE" label under the streamer's profile picture
  39. "live": "ScChannelStatusTextIndicatorMask-sc-qtgrnb-1",
  40. // The "↗ Chat" button
  41. "chat": "p.ScTitleText-sc-d9mj2s-0:nth-child(2)",
  42. // The text when hovering over the volume button when muted
  43. // NOTE: may depend on language settings
  44. "unmute": '[aria-label="Unmute (m)"]',
  45. // Class that is only present when the video player encountered
  46. // an error (e.g. #5000)
  47. "playerError": ".jjYHlC",
  48. };
  49.  
  50. // Reload only if the streamer wasn't live (and later is)
  51. let wasLive = true;
  52.  
  53. function log(...args) {
  54. if (ENABLE_LOGS) {
  55. args.unshift("rwsgl:");
  56. console.log.apply(this, args);
  57. }
  58. }
  59.  
  60. function clickIfPresent(selector) {
  61. if (document.querySelector(selector)) {
  62. document.querySelector(selector).click();
  63. }
  64. }
  65.  
  66. function isLive() {
  67. // BUG: will react to the LIVE label in the "While {streamer} is offline, check out:" window.
  68. return DEBUG || document.querySelector(SELECTORS.live) != null;
  69. };
  70.  
  71. function playerError() {
  72. return document.querySelector(SELECTORS.playerError) != null;
  73. }
  74.  
  75. function reload() {
  76. GM.setValue("reload", true);
  77. window.location.reload();
  78. }
  79.  
  80. function checkLive() {
  81. let live = isLive();
  82. if (isLive() && !wasLive) {
  83. reload();
  84. } else if (playerError() && RELOAD_ON_ERROR) {
  85. log("Player error:", document.querySelector(SELECTORS.playerError));
  86. reload();
  87. }
  88. setLive();
  89. setTimeout(checkLive, CHECK_INTERVAL_MS);
  90. }
  91.  
  92. function setLive() {
  93. // Do not reload if the streamer is already live
  94. wasLive = isLive() && !DEBUG;
  95. log("Live? " + wasLive);
  96. }
  97.  
  98. function init() {
  99. setLive();
  100.  
  101. // If we just reloaded, ...
  102. let reload = GM_getValue("reload", false);
  103. if (reload) {
  104. GM_setValue("reload", false);
  105.  
  106. // Go to the chat view
  107. clickIfPresent(SELECTORS.chat);
  108.  
  109. // Unmute if the stream is muted
  110. if (UNMUTE_AFTER_RELOAD) {
  111. clickIfPresent(SELECTORS.unmute);
  112. }
  113. }
  114.  
  115. setTimeout(checkLive, INITIAL_DELAY_MS + CHECK_INTERVAL_MS);
  116. }
  117.  
  118. GM_registerMenuCommand('Reset live status', function() {
  119. setLive();
  120. }, 'r');
  121.  
  122. setTimeout(init, INITIAL_DELAY_MS);
  123. })();