YouTube Remaining Time

Show the remaining time of a YouTube video inside the player.

  1. // ==UserScript==
  2. // @name YouTube Remaining Time
  3. // @namespace http://the6p4c.com/
  4. // @version 1.3
  5. // @description Show the remaining time of a YouTube video inside the player.
  6. // @author The6P4C
  7. // @match *://www.youtube.com/*
  8. // @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. // ***********************************************************************
  13. // CHANGE TO true TO DISPLAY REMAINING TIME COMPENSATING FOR PLAYBACK RATE
  14. // ***********************************************************************
  15. var SHOW_REAL_TIME_REMAINING = false;
  16.  
  17. var TIME_KEYS_ORDER = ["days", "hours", "minutes", "seconds"];
  18. var TIME_KEYS_ORDER_REV = [].concat(TIME_KEYS_ORDER).reverse();
  19.  
  20. function parseTime(timeString) {
  21. // reversed so seconds first
  22. var parts = timeString.split(":").reverse();
  23. var time = {};
  24.  
  25. for (var i = 0; i < TIME_KEYS_ORDER_REV.length; ++i) {
  26. var key = TIME_KEYS_ORDER_REV[i];
  27.  
  28. if (i < parts.length) {
  29. time[key] = parseInt(parts[i]);
  30. } else {
  31. time[key] = 0;
  32. }
  33. }
  34.  
  35. return time;
  36. }
  37.  
  38. function pad2(n) {
  39. if (n >= 10) {
  40. return n;
  41. } else {
  42. return "0" + n;
  43. }
  44. }
  45.  
  46. function stringifyTime(time) {
  47. var timeString = "";
  48. var nonZeroEncountered = false;
  49.  
  50. for (var i = 0; i < TIME_KEYS_ORDER.length; ++i) {
  51. var key = TIME_KEYS_ORDER[i];
  52. var currentValue = time[key];
  53.  
  54. // Only start adding values from the first non zero
  55. // But... always show minutes, even if they're zero.
  56. if (currentValue !== 0 || nonZeroEncountered || key == "minutes") {
  57. // We don't want to pad the first value:
  58. // If there's hours, don't pad days
  59. // If there's minutes, don't pad hours
  60. // If there's seconds, don't pad minutes
  61. if (nonZeroEncountered) {
  62. currentValue = pad2(currentValue);
  63. }
  64.  
  65. timeString += currentValue;
  66.  
  67. if (key != "seconds") {
  68. timeString += ":";
  69. }
  70.  
  71. nonZeroEncountered = true;
  72. }
  73. }
  74.  
  75. return timeString;
  76. }
  77.  
  78. function subtractTime(a, b) {
  79. var secondsDiff = a.seconds - b.seconds;
  80. var minutesDiff = a.minutes - b.minutes;
  81. var hoursDiff = a.hours - b.hours;
  82. var daysDiff = a.days - b.days;
  83.  
  84. if (secondsDiff < 0) {
  85. minutesDiff -= 1;
  86. secondsDiff += 60;
  87. }
  88.  
  89. if (minutesDiff < 0) {
  90. hoursDiff -= 1;
  91. minutesDiff += 60;
  92. }
  93.  
  94. if (hoursDiff < 0) {
  95. daysDiff -= 1;
  96. hoursDiff += 24;
  97. }
  98.  
  99. return {
  100. days: daysDiff,
  101. hours: hoursDiff,
  102. minutes: minutesDiff,
  103. seconds: secondsDiff
  104. };
  105. }
  106.  
  107. function divideTime(time, divisor) {
  108. var timeSeconds = time.seconds + ((time.minutes + (time.hours + time.days * 24) * 60) * 60);
  109. var newTimeSeconds = Math.floor(timeSeconds / divisor);
  110.  
  111. return {
  112. days: Math.floor(newTimeSeconds / (24 * 60 * 60)),
  113. hours: Math.floor(newTimeSeconds / (60 * 60)),
  114. minutes: Math.floor(newTimeSeconds / 60),
  115. seconds: newTimeSeconds % 60
  116. };
  117. }
  118.  
  119. function tryGetPlaybackRate($timeDisplay) {
  120. // Try and find the playback speed from the settings UI
  121. var $ytdPlayer = $timeDisplay.parents(".html5-video-player");
  122. if ($ytdPlayer.length != 1) {
  123. // If we can't find the player, fall back to a "safe" alternative
  124. return 1;
  125. }
  126.  
  127. $ytdPlayer = $ytdPlayer[0];
  128.  
  129. // We can't be sure that this will work - if YouTube changes their player API somehow
  130. // we don't want our entire script to break. Again, fall back to a "safe" alternative.
  131. try {
  132. return $ytdPlayer.getPlaybackRate();
  133. } catch (e) {
  134. return 1;
  135. }
  136. }
  137.  
  138. function onTimeChange() {
  139. var $this = $(this);
  140.  
  141. // If this event was called in the context of the .ytp-time-duration event,
  142. // we need to set $timeCurrent not to $this but to the actual
  143. // .ytp-time-current element.
  144. var $timeCurrent = $this;
  145. if (!$this.hasClass("ytp-time-current")) {
  146. $timeCurrent = $this.parent().find(".ytp-time-current");
  147. }
  148.  
  149. var $timeDisplay = $timeCurrent.parent();
  150.  
  151. // For some reason a time will show on a live video, but I don't think it's very meaningful
  152. if ($timeDisplay.hasClass("ytp-live")) {
  153. return;
  154. }
  155.  
  156. var $timeDuration = $timeDisplay.parent().find(".ytp-time-duration");
  157.  
  158. if ($timeDisplay.attr("____remaining-added") != "____remaining-added") {
  159. var $timeRemainingWrapper = $("<div style='display: inline-block; width: 5px;'></div><span style='color: #ddd; text-shadow: 0 0 2px rgba(0,0,0,.5)'>(&minus;<span class='____time-remaining'></span>)</span>");
  160. $timeDuration.after($timeRemainingWrapper);
  161.  
  162. $timeDisplay.attr("____remaining-added", "____remaining-added");
  163. }
  164.  
  165. var $timeRemaining = $timeDisplay.find(".____time-remaining");
  166.  
  167. var currentTime = parseTime($timeCurrent.text());
  168. var durationTime = parseTime($timeDuration.text());
  169. var remainingTime = subtractTime(durationTime, currentTime);
  170.  
  171. if (SHOW_REAL_TIME_REMAINING) {
  172. var playbackRate = tryGetPlaybackRate($timeDisplay);
  173. var remainingRealTime = divideTime(remainingTime, playbackRate);
  174.  
  175. var suffix = playbackRate == 1 ? "" : (" at x" + playbackRate);
  176. $timeRemaining.text(stringifyTime(remainingRealTime) + suffix);
  177. } else {
  178. $timeRemaining.text(stringifyTime(remainingTime));
  179. }
  180. }
  181.  
  182. $(document).ready(function() {
  183. $("body").on("DOMSubtreeModified", ".ytp-time-current", onTimeChange);
  184. $("body").on("DOMSubtreeModified", ".ytp-time-duration", onTimeChange);
  185. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址