YouTube - 时间指示器(多种)

在 YouTube 播放器中显示剩余时间或结束时间,并考虑播放速度。

安装此脚本
作者推荐脚本

您可能也喜欢YouTube - 播放速度滑块

安装此脚本
  1. // ==UserScript==
  2. // @name YouTube - Time Indicators
  3. // @name:fr YouTube - Indicateurs de temps
  4. // @name:es YouTube - Indicadores de tiempo
  5. // @name:de YouTube - Zeitindikatoren
  6. // @name:it YouTube - Indicatori del tempo
  7. // @name:zh-CN YouTube - 时间指示器(多种)
  8. // @namespace https://gist.github.com/4lrick/cf14cf267684f06c1b7bc559ddf2b943
  9. // @version 2.2
  10. // @description Shows remaining or end time in the YouTube player, taking into account the playback speed.
  11. // @description:fr Affiche le temps restant ou l'heure de fin dans le lecteur YouTube, en tenant compte de la vitesse de lecture.
  12. // @description:es Muestra el tiempo restante o la hora de finalización en el reproductor de YouTube, teniendo en cuenta la velocidad de reproducción.
  13. // @description:de Zeigt im YouTube-Player die verbleibende Zeit oder die Endzeit an und berücksichtigt dabei die Wiedergabegeschwindigkeit.
  14. // @description:it Mostra il tempo rimanente o l'orario di fine nel riproduttore YouTube, tenendo conto della velocità di riproduzione.
  15. // @description:zh-CN 在 YouTube 播放器中显示剩余时间或结束时间,并考虑播放速度。
  16. // @author 4lrick
  17. // @match https://www.youtube.com/*
  18. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  19. // @grant none
  20. // @license GPL-3.0-only
  21. // ==/UserScript==
  22.  
  23. (function() {
  24. 'use strict';
  25. let timeDisplay;
  26. let lastRenderedText = '';
  27. let showEndTime = localStorage.getItem('yt-player-remaining-time-mode') === 'true';
  28.  
  29. function createTimeDisplayElement() {
  30. const timeDisplayElement = document.createElement('span');
  31.  
  32. timeDisplayElement.style.display = 'inline-block';
  33. timeDisplayElement.style.marginLeft = '10px';
  34. timeDisplayElement.style.color = '#ddd';
  35. timeDisplayElement.style.cursor = 'pointer';
  36. timeDisplayElement.title = 'Click to toggle between remaining time and end time';
  37.  
  38. timeDisplayElement.addEventListener('click', () => {
  39. showEndTime = !showEndTime;
  40. localStorage.setItem('yt-player-remaining-time-mode', showEndTime);
  41. });
  42.  
  43. return timeDisplayElement;
  44. }
  45.  
  46. function formatTimeDisplay(videoElement) {
  47. if (showEndTime) {
  48. const endTime = new Date(Date.now() + (videoElement.duration - videoElement.currentTime) * 1000 / videoElement.playbackRate);
  49. return `(${endTime.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })})`;
  50. } else {
  51. const timeRemaining = (videoElement.duration - videoElement.currentTime) / videoElement.playbackRate;
  52. const hours = Math.floor(timeRemaining / 3600);
  53. const minutes = Math.floor((timeRemaining % 3600) / 60);
  54. const seconds = Math.floor(timeRemaining % 60);
  55. return `(${hours > 0 ? `${hours}:` : ''}${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')})`;
  56. }
  57. }
  58.  
  59. function displayRemainingTime() {
  60. const videoElement = document.querySelector('video');
  61. const isLive = document.querySelector('.ytp-time-display')?.classList.contains('ytp-live');
  62. const miniplayerUI = document.querySelector('.ytp-miniplayer-ui');
  63. const isMiniplayerVisible = miniplayerUI && getComputedStyle(miniplayerUI).display !== 'none';
  64. const currentTime = videoElement?.currentTime;
  65. const timeContainer = document.querySelector(
  66. isMiniplayerVisible ? '.ytp-miniplayer-ui .ytp-time-contents' : '.ytp-chrome-controls .ytp-time-contents'
  67. );
  68.  
  69. if (!videoElement || isLive || !timeContainer || isNaN(videoElement.duration)) {
  70. if (timeDisplay) {
  71. timeDisplay.remove();
  72. timeDisplay = null;
  73. }
  74. requestAnimationFrame(displayRemainingTime);
  75. return;
  76. }
  77.  
  78. if (!timeDisplay) {
  79. timeDisplay = createTimeDisplayElement();
  80. timeContainer.appendChild(timeDisplay);
  81. }
  82.  
  83. if (!timeContainer.contains(timeDisplay)) {
  84. timeDisplay.remove();
  85. timeContainer.appendChild(timeDisplay);
  86. }
  87.  
  88. const text = formatTimeDisplay(videoElement);
  89.  
  90. if (text !== lastRenderedText) {
  91. timeDisplay.textContent = text;
  92. lastRenderedText = text;
  93. }
  94.  
  95. requestAnimationFrame(displayRemainingTime);
  96. }
  97.  
  98. function initRemainingCounter() {
  99. const timeContainer = document.querySelector('.ytp-time-contents');
  100.  
  101. if (timeContainer) {
  102. timeDisplay = createTimeDisplayElement();
  103. timeContainer.appendChild(timeDisplay);
  104. requestAnimationFrame(displayRemainingTime);
  105. observer.disconnect();
  106. }
  107. }
  108.  
  109. function checkVideoExists() {
  110. const videoElement = document.querySelector('video');
  111.  
  112. if (videoElement) {
  113. initRemainingCounter();
  114. }
  115. }
  116.  
  117. const observer = new MutationObserver(checkVideoExists);
  118. observer.observe(document.body, { childList: true, subtree: true });
  119. })();