YouTube Permanent ProgressBar

Keeps YouTube progress bar visible all the time.

  1. // ==UserScript==
  2. // @name YouTube Permanent ProgressBar
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.4
  5. // @description Keeps YouTube progress bar visible all the time.
  6. // @author Can Kurt
  7. // @match *://www.youtube.com/*
  8. // @license MIT
  9. // @repository https://github.com/cccaaannn/YouTubePermanentProgressBar
  10. // ==/UserScript==
  11.  
  12. var permanentProgressBar = {
  13.  
  14. options: {
  15. UPDATE_INTERVAL: 500, // Update interval in milliseconds, decrease for smoother progress movement or increase for performance.
  16. PROGRESSBAR_OPACITY_WINDOW: 1, // Progress bar opacity on window mode between 0 - 1.
  17. PROGRESSBAR_OPACITY_FULLSCREEN: 0.5, // Progress bar opacity on fullscreen mode between 0 - 1.
  18.  
  19. UPDATE_VIDEO_TIMER: true,
  20. UPDATE_PROGRESSBAR: true,
  21. UPDATE_BUFFERBAR: true,
  22. UPDATE_SCRUBBER: true
  23. },
  24.  
  25. prettifyVideoTime: function (video) {
  26. let seconds = "" + Math.floor(video.currentTime % 60);
  27. let minutes = "" + Math.floor((video.currentTime % 3600) / 60);
  28. let hours = "" + Math.floor(video.currentTime / 3600);
  29. if (video.currentTime / 60 > 60) {
  30. return `${hours}:${minutes.padStart(2, '0')}:${seconds.padStart(2, '0')}`;
  31. }
  32. else {
  33. return `${minutes}:${seconds.padStart(2, '0')}`;
  34. }
  35. },
  36.  
  37. getDuration: function (video, type) {
  38. if (type === "PROGRESSBAR") {
  39. return video.currentTime;
  40. }
  41. else if (type === "BUFFERBAR") {
  42. return video.buffered.end(video.buffered.length - 1);
  43. }
  44. },
  45.  
  46. updateCurrentTimeField: function (player) {
  47. const video = player.querySelector("video");
  48. const currentTime = player.querySelector(".ytp-time-current");
  49. if (!video || !currentTime) {
  50. return;
  51. }
  52.  
  53. currentTime.innerText = permanentProgressBar.prettifyVideoTime(video);
  54. },
  55.  
  56. updateScrubber: function (passedChapterCount, progressBarLastChapter, chaptersPixelWidthUntilCurrentChapter) {
  57. const scrubber = document.getElementsByClassName("ytp-scrubber-container");
  58.  
  59. if (!scrubber || scrubber.length !== 1) {
  60. return;
  61. }
  62.  
  63. const positionInfo = progressBarLastChapter.getBoundingClientRect();
  64.  
  65. // Chapters has hidden margin and its different on fullscreen
  66. let chapterMarginFix = passedChapterCount * 2.05;
  67. if (document.fullscreenElement) {
  68. chapterMarginFix = passedChapterCount * 3.05;
  69. }
  70.  
  71. const chaptersWithPadding = chaptersPixelWidthUntilCurrentChapter + chapterMarginFix;
  72.  
  73. const distanceFromLeft = positionInfo.width + chaptersWithPadding;
  74.  
  75. scrubber[0].style.transform = `translateX(${distanceFromLeft}px)`;
  76. },
  77.  
  78. // Deprecated
  79. updateProgressBar: function (player) {
  80. // works only on chapterless (old) videos
  81. const video = player.querySelector("video");
  82. const progressBar = player.querySelector(".ytp-play-progress");
  83. const bufferBar = player.querySelector(".ytp-load-progress");
  84. if (!video || !progressBar || !bufferBar) {
  85. return;
  86. }
  87.  
  88. progressBar.style.transform = `scaleX(${video.currentTime / video.duration})`;
  89. bufferBar.style.transform = `scaleX(${video.buffered.end(video.buffered.length - 1) / video.duration})`;
  90. },
  91.  
  92. updateProgressBarWithChapters: function (player, type) {
  93. // YouTube api does not provides current time in chapters
  94. // this function finds current time in the chapter by finding the ratio between total video duration and total width of the chapters div
  95.  
  96. const video = player.querySelector("video");
  97. if (video == null || isNaN(video.duration)) {
  98. return;
  99. }
  100.  
  101. // there can be multiple chapters
  102. const progressBarWidthsCollection = player.getElementsByClassName("ytp-progress-bar-padding");
  103.  
  104. // select progress or bufferBar
  105. let progressBarChaptersCollection;
  106. if (type === "PROGRESSBAR") {
  107. progressBarChaptersCollection = player.getElementsByClassName("ytp-play-progress");
  108. }
  109. if (type === "BUFFERBAR") {
  110. progressBarChaptersCollection = player.getElementsByClassName("ytp-load-progress");
  111. }
  112.  
  113. // quit if elements does not exists
  114. if (!video || !progressBarWidthsCollection || !progressBarChaptersCollection) {
  115. return;
  116. }
  117.  
  118. // find the ratio between total video duration and total width of the chapters div
  119. let totalProgressBarWidth = 0;
  120. for (let i = 0; i < progressBarWidthsCollection.length; i++) {
  121. totalProgressBarWidth += progressBarWidthsCollection[i].offsetWidth;
  122. }
  123. const durationWidthRatio = video.duration / totalProgressBarWidth;
  124.  
  125. // loop inside chapters
  126. let chaptersPixelWidthUntilCurrentChapter = 0;
  127. let passedChapterCount = 0;
  128. for (let i = 0; i < progressBarWidthsCollection.length; i++) {
  129.  
  130. // if current time is bigger than durationWidthRatio * (chapters pixel width including current one) scale the current chapter to 1 because we passed it
  131. if (permanentProgressBar.getDuration(video, type) > durationWidthRatio * (chaptersPixelWidthUntilCurrentChapter + progressBarWidthsCollection[i].offsetWidth)) {
  132. progressBarChaptersCollection[i].style.transform = "scaleX(1)";
  133.  
  134. // increase the current chapters location by adding last watched chapter
  135. chaptersPixelWidthUntilCurrentChapter += progressBarWidthsCollection[i].offsetWidth;
  136.  
  137. passedChapterCount++;
  138. }
  139.  
  140. // If not, it means that we are on this chapter.
  141. // Find the appropriate size for the chapter and scale it
  142. else {
  143. // current time
  144. let currentTimeInChapterInSeconds = permanentProgressBar.getDuration(video, type) - (durationWidthRatio * chaptersPixelWidthUntilCurrentChapter);
  145.  
  146. // total chapter time
  147. let currentChapterLengthInSeconds = durationWidthRatio * progressBarWidthsCollection[i].offsetWidth;
  148.  
  149. let currentChapterTimeRatio = currentTimeInChapterInSeconds / currentChapterLengthInSeconds
  150.  
  151. progressBarChaptersCollection[i].style.transform = `scaleX(${currentChapterTimeRatio})`;
  152.  
  153. // Update scrubber position to align with progressbar
  154. if (type === "PROGRESSBAR" && permanentProgressBar.options.UPDATE_SCRUBBER) {
  155. const progressBarLastChapter = progressBarChaptersCollection[i];
  156.  
  157. permanentProgressBar.updateScrubber(
  158. passedChapterCount,
  159. progressBarLastChapter,
  160. chaptersPixelWidthUntilCurrentChapter
  161. );
  162. }
  163.  
  164. break;
  165. }
  166.  
  167. }
  168.  
  169. },
  170.  
  171. update: function () {
  172. // Get video element
  173. const player = document.querySelector(".html5-video-player");
  174. if (player == null) {
  175. return;
  176. }
  177.  
  178. // update css
  179. if (document.fullscreenElement) {
  180. document.querySelector(".ytp-chrome-bottom").style.opacity = permanentProgressBar.options.PROGRESSBAR_OPACITY_FULLSCREEN;
  181. }
  182. else {
  183. document.querySelector(".ytp-chrome-bottom").style.opacity = permanentProgressBar.options.PROGRESSBAR_OPACITY_WINDOW;
  184. }
  185.  
  186. // update video timer
  187. if (permanentProgressBar.options.UPDATE_VIDEO_TIMER) {
  188. permanentProgressBar.updateCurrentTimeField(player);
  189. }
  190.  
  191. // update PROGRESSBAR
  192. if (permanentProgressBar.options.UPDATE_PROGRESSBAR) {
  193. permanentProgressBar.updateProgressBarWithChapters(player, "PROGRESSBAR");
  194. }
  195.  
  196. // update BUFFERBAR
  197. if (permanentProgressBar.options.UPDATE_BUFFERBAR) {
  198. permanentProgressBar.updateProgressBarWithChapters(player, "BUFFERBAR");
  199. }
  200.  
  201. },
  202.  
  203. start: function () {
  204. setInterval(permanentProgressBar.update, permanentProgressBar.options.UPDATE_INTERVAL);
  205. }
  206.  
  207. };
  208.  
  209.  
  210. permanentProgressBar.start();