Greasy Fork 还支持 简体中文。

YouTube Always show progress bar - Forked

Always show progress bar

  1. // ==UserScript==
  2. // @name YouTube Always show progress bar - Forked
  3. // @version 2024.02.14
  4. // @author Xiao
  5. // @match *://www.youtube.com/*
  6. // @allFrames true
  7. // @grant unsafeWindow
  8. // @namespace https://greasyfork.org/users/5802
  9. // @description Always show progress bar
  10. // ==/UserScript==
  11.  
  12. //original: https://greasyfork.org/en/scripts/30046-youtube-always-show-progress-bar
  13.  
  14. let css = `
  15. .ytp-autohide .ytp-chrome-bottom {
  16. opacity: 1 !important;
  17. }
  18.  
  19. .ytp-autohide .ytp-chrome-bottom .ytp-progress-bar-container[alwaysshow="true"] {
  20. bottom:-1px;
  21. }
  22.  
  23. .ytp-autohide .ytp-chrome-bottom > :not(.ytp-progress-bar-container[alwaysshow="true"]) {
  24. display: none;
  25. }
  26. `;
  27.  
  28. let style = document.createElement('style');
  29. style.textContent = css;
  30. document.head.appendChild(style);
  31.  
  32. let eventHandlers = [];
  33.  
  34. let timeupdateListener;
  35. let progressListener;
  36. let video;
  37.  
  38. setTimeout(run, 2000);
  39.  
  40. function isFinished() {
  41. video = document.querySelector('video');
  42. if (video) {
  43. eventHandlers[0]?.removeEventListener('timeupdate', eventHandlers[1]);
  44. eventHandlers[0]?.removeEventListener('progress', eventHandlers[2]);
  45. setTimeout(run, 2000);
  46. } else if (location.pathname === '/watch') {
  47. setTimeout(isFinished, 1000);
  48. }
  49. }
  50.  
  51. addEventListener('yt-navigate-finish', isFinished);
  52.  
  53. function run () {
  54. if (document.querySelector('#movie_player.ad-showing')) {
  55. setTimeout(run, 2000);
  56. return;
  57. }
  58.  
  59. video = document.querySelector('video');
  60. if (!document.querySelector('.html5-video-player:not(.addedupdateevents)'))
  61. return;
  62.  
  63. let ytdApp = unsafeWindow.document.querySelector('ytd-app');
  64.  
  65. let chap, isLive;
  66. if (ytdApp) {
  67. let data = ytdApp.__data.data.response;
  68. chap = data.playerOverlays.playerOverlayRenderer.decoratedPlayerBarRenderer?.decoratedPlayerBarRenderer.playerBar.multiMarkersPlayerBarRenderer.markersMap?.find(exportFunction(a => a.key == 'AUTO_CHAPTERS' || a.key == 'DESCRIPTION_CHAPTERS', unsafeWindow)).value.chapters;
  69. isLive = data.contents.twoColumnWatchNextResults.results.results.contents[0].videoPrimaryInfoRenderer.viewCount.videoViewCountRenderer?.isLive;
  70. } else {
  71. let data = unsafeWindow.ytPubsubPubsubInstance.h[33].player.app.mediaElement.ra.videoData;
  72. chap = data.Ua?.playerOverlays.playerOverlayRenderer.decoratedPlayerBarRenderer?.decoratedPlayerBarRenderer.playerBar.multiMarkersPlayerBarRenderer.markersMap?.find(exportFunction(a => a.key == 'AUTO_CHAPTERS' || a.key == 'DESCRIPTION_CHAPTERS', unsafeWindow)).value.chapters;
  73. isLive = data.isLivePlayback;
  74. }
  75.  
  76. document.querySelector('.ytp-progress-bar-container').setAttribute('alwaysshow', !isLive);
  77.  
  78. video.className += ' addedupdateevents';
  79. let player = document.getElementById('movie_player');
  80. let progressbars = document.querySelectorAll('.ytp-play-progress');
  81. let loadbars = document.querySelectorAll('.ytp-load-progress');
  82.  
  83. if (chap?.length) {
  84. let chars = [];
  85. let cap;
  86. let capstart;
  87. let capduration;
  88. let lastusedcapprog;
  89. let lastusedcapbuf;
  90.  
  91. for (let i = 0; i < chap.length; i++) {
  92. chars.push(chap[i].chapterRenderer.timeRangeStartMillis / 1000);
  93. }
  94.  
  95. timeupdateListener = () => {
  96. if (!player.classList.contains('ytp-autohide'))
  97. return;
  98.  
  99. for (let i = 0; i < chars.length; i++) {
  100. let ts = chars[i];
  101. if (ts < video.currentTime || !capduration) {
  102. cap = i;
  103. capstart = ts;
  104. capduration = ((chars[i + 1] ? chars[i + 1] : video.duration) - ts);
  105. } else {
  106. break;
  107. }
  108. }
  109.  
  110. if (cap > lastusedcapprog)
  111. progressbars[lastusedcapprog].style.transform = 'scaleX(1)';
  112.  
  113. lastusedcapprog = cap;
  114.  
  115. progressbars[cap].style.transform = 'scaleX(' + ((video.currentTime - capstart) / capduration) + ')';
  116. };
  117. video.addEventListener('timeupdate', timeupdateListener);
  118.  
  119. progressListener = () => {
  120. if (!player.classList.contains('ytp-autohide') || !video.buffered.length)
  121. return;
  122.  
  123. let buff = video.buffered.end(video.buffered.length - 1);
  124.  
  125. for (let i = 0; i < chars.length; i++) {
  126. let ts = chars[i];
  127. if (ts < buff || !capduration) {
  128. cap = i;
  129. capstart = ts;
  130. capduration = ((chars[i + 1] ? chars[i + 1] : video.duration) - ts);
  131. } else {
  132. break;
  133. }
  134. }
  135.  
  136. if (cap > lastusedcapbuf)
  137. loadbars[lastusedcapbuf].style.transform = 'scaleX(1)';
  138.  
  139. lastusedcapbuf = cap;
  140.  
  141. loadbars[cap].style.transform = 'scaleX(' + ((buff - capstart) / capduration) + ')';
  142. };
  143. video.addEventListener('progress', progressListener);
  144. eventHandlers = [video, timeupdateListener, progressListener];
  145. } else {
  146. timeupdateListener = function () {
  147. if (player.classList.contains('ytp-autohide'))
  148. progressbars[0].style.transform = 'scaleX(' + (video.currentTime / video.duration) + ')';
  149. };
  150. video.addEventListener('timeupdate', timeupdateListener);
  151.  
  152. progressListener = function () {
  153. if (player.classList.contains('ytp-autohide') && video.buffered.length)
  154. loadbars[0].style.transform = 'scaleX(' + (video.buffered.end(video.buffered.length - 1) / video.duration) + ')';
  155. };
  156. video.addEventListener('progress', progressListener);
  157. eventHandlers = [video, timeupdateListener, progressListener];
  158. }
  159. }