Refresh Video

Auto refresh youtube if video freezed/stopped downloading

  1. // ==UserScript==
  2. // @name Refresh Video
  3. // @description Auto refresh youtube if video freezed/stopped downloading
  4. // @version 0.1.20
  5. // @author 0vC4
  6. // @namespace https://greasyfork.org/users/670183
  7. // @match *://*.youtube.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  9. // @run-at document-start
  10. // @license MIT
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. // refresh if no reply during these actions
  15. const maxLoadingTime = 1500;
  16. const maxSeekingTime = 1500;
  17.  
  18. let created = false;
  19. let refreshing = false;
  20. let loaded = false;
  21. let navigating = true;
  22. let seeking = false;
  23. let seekingId = 0;
  24.  
  25. const policy = window.trustedTypes && window.trustedTypes.createPolicy ? window.trustedTypes.createPolicy('timeout', {createScriptURL: str => str}) : {createScriptURL: str => str};
  26. const timeout = delay => URL.createObjectURL(new Blob([`setTimeout(() => postMessage(0), ${delay});`]));
  27. const refreshOnLoad = policy.createScriptURL(timeout(maxLoadingTime));
  28. const seekingDebounce = policy.createScriptURL(timeout(500));
  29.  
  30. document.addEventListener('yt-navigate-start', () => {
  31. if (!location.href.includes('watch') && !location.href.includes('shorts')) return;
  32. navigating = true;
  33. loaded = false;
  34. });
  35. document.addEventListener('yt-navigate-finish', () => {
  36. if (!location.href.includes('watch') && !location.href.includes('shorts')) return;
  37. navigating = false;
  38. });
  39.  
  40. let focused = false;
  41. window.setInterval(()=>{
  42. if (!location.href.includes('watch') && !location.href.includes('shorts')) {
  43. focused = false;
  44. created = false;
  45. refreshing = false;
  46. loaded = false;
  47. navigating = true;
  48. seeking = false;
  49. seekingId = 0;
  50. return;
  51. }
  52.  
  53. if (!focused) {
  54. focused = document.hasFocus();
  55. return;
  56. }
  57.  
  58. const vid = window.document.querySelector('video.html5-main-video');
  59. if (!vid) return;
  60. if (!created) {
  61. created = true;
  62. vid.addEventListener('seeking', () => {
  63. clearTimeout(seekingId);
  64. seeking = true;
  65. seekingId = setTimeout(() => {
  66. seeking = false;
  67. }, maxSeekingTime);
  68. });
  69.  
  70. // refresh if no data for half sec
  71. const callback = () => {
  72. if (!loaded && vid.readyState === window.HTMLMediaElement.HAVE_NOTHING) {
  73. window.location.href = window.location.href;
  74. }
  75. };
  76. new Worker(refreshOnLoad).onmessage = callback;
  77. }
  78.  
  79. // to prevent early page refresh
  80. if (vid.readyState === window.HTMLMediaElement.HAVE_CURRENT_DATA || vid.readyState === window.HTMLMediaElement.HAVE_ENOUGH_DATA) loaded = !navigating;
  81. const noData = vid.readyState === window.HTMLMediaElement.HAVE_CURRENT_DATA || vid.readyState === window.HTMLMediaElement.HAVE_METADATA;
  82. if (loaded && !refreshing && noData && !seeking && !vid.paused && +vid.currentTime.toFixed(0) >= +vid.buffered.end(0).toFixed(0) - 2 && +vid.currentTime.toFixed(0) < +vid.duration.toFixed(0) - 2) {
  83. refreshing = true;
  84. const callback = () => {
  85. if (seeking) {
  86. refreshing = false;
  87. return;
  88. }
  89. window.location.href = window.location.href.split('?')[0] + '?t=' + (+vid.currentTime.toFixed(0)) + '&' + window.location.href.split('?')[1];
  90. };
  91. new Worker(seekingDebounce).onmessage = callback;
  92. }
  93. });