YouTube - 剩余时间指示器

在视频时长旁边显示YouTube视频的剩余时长,考虑播放速度。

当前为 2025-05-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube - Remaining Time Indicator
  3. // @name:fr YouTube - Indicateur du temps restant
  4. // @name:es YouTube - Indicador de tiempo restante
  5. // @name:de YouTube - Anzeige der verbleibenden Zeit
  6. // @name:it YouTube - Indicatore del tempo rimanente
  7. // @name:zh-CN YouTube - 剩余时间指示器
  8. // @namespace https://gist.github.com/4lrick/cf14cf267684f06c1b7bc559ddf2b943
  9. // @version 1.9
  10. // @description Displays the remaining duration of a YouTube video next to the video duration, taking into account the playback rate.
  11. // @description:fr Affiche la durée restante d'une vidéo YouTube à côté de la durée de la vidéo, en tenant compte de la vitesse de lecture.
  12. // @description:es Muestra la duración restante de un video de YouTube junto a la duración del video, teniendo en cuenta la velocidad de reproducción.
  13. // @description:de Zeigt die verbleibende Dauer eines YouTube-Videos neben der Videodauer an und berücksichtigt dabei die Wiedergabegeschwindigkeit.
  14. // @description:it Mostra la durata rimanente di un video di YouTube accanto alla durata del video, 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.  
  27. function createTimeDisplayElement() {
  28. const timeDisplayElement = document.createElement('span');
  29.  
  30. timeDisplayElement.style.display = 'inline-block';
  31. timeDisplayElement.style.marginLeft = '10px';
  32. timeDisplayElement.style.color = '#ddd';
  33.  
  34. return timeDisplayElement;
  35. }
  36.  
  37. function displayRemainingTime() {
  38. const videoElement = document.querySelector('video');
  39. const isLive = document.querySelector('.ytp-time-display')?.classList.contains('ytp-live');
  40. const timeContainer = document.querySelector('.ytp-time-contents');
  41.  
  42. if (videoElement && !isLive && timeContainer) {
  43. if (!timeDisplay) {
  44. timeDisplay = createTimeDisplayElement();
  45. timeContainer.appendChild(timeDisplay);
  46. }
  47.  
  48. const timeRemaining = (videoElement.duration - videoElement.currentTime) / videoElement.playbackRate;
  49. const hours = Math.floor(timeRemaining / 3600);
  50. const minutes = Math.floor((timeRemaining % 3600) / 60);
  51. const seconds = Math.floor(timeRemaining % 60);
  52.  
  53. timeDisplay.textContent = `(${hours > 0 ? `${hours}:` : ''}${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')})`;
  54. } else if (timeDisplay) {
  55. timeDisplay.remove();
  56. timeDisplay = null;
  57. }
  58.  
  59. requestAnimationFrame(displayRemainingTime);
  60. }
  61.  
  62.  
  63. function initRemainingCounter() {
  64. const timeContainer = document.querySelector('.ytp-time-contents');
  65.  
  66. if (timeContainer) {
  67. timeDisplay = createTimeDisplayElement();
  68. timeContainer.appendChild(timeDisplay);
  69. requestAnimationFrame(displayRemainingTime);
  70. observer.disconnect();
  71. }
  72. }
  73.  
  74. function checkVideoExists() {
  75. const videoElement = document.querySelector('video');
  76.  
  77. if (videoElement) {
  78. initRemainingCounter();
  79. }
  80. }
  81.  
  82. const observer = new MutationObserver(checkVideoExists);
  83. observer.observe(document.body, { childList: true, subtree: true });
  84. })();