YouTube - Remaining time

Displays the remaining duration of a YouTube video next to the video duration, taking into account the playback rate.

目前為 2023-10-28 提交的版本,檢視 最新版本

// ==UserScript==
// @name            YouTube - Remaining time
// @namespace       https://gist.github.com/4lrick/cf14cf267684f06c1b7bc559ddf2b943
// @version         1.1
// @description     Displays the remaining duration of a YouTube video next to the video duration, taking into account the playback rate.
// @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.
// @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.
// @description:de  Zeigt die verbleibende Dauer eines YouTube-Videos neben der Videodauer an und berücksichtigt dabei die Wiedergabegeschwindigkeit.
// @description:it  Mostra la durata rimanente di un video di YouTube accanto alla durata del video, tenendo conto della velocità di riproduzione.
// @description:zh  在视频时长旁边显示YouTube视频的剩余时长,考虑播放速度。
// @author          4lrick
// @match           https://www.youtube.com/*
// @icon            https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant           none
// @license         GPL-3.0-only
// ==/UserScript==

(() => {
    'use strict';

    let timeDisplay;
    let videoIsInit = false;
    let activeVideoElement = null;
    let activeVideoInterval = null;

    const checkAndMonitorVideoPage = () => {
        const newVideoPageActive = window.location.href.startsWith('https://www.youtube.com/watch');
        if (newVideoPageActive && !videoIsInit) {
            initializeTimeDisplay();
            videoIsInit = true;
        }
    };

    const initializeTimeDisplay = () => {
        timeDisplay = document.createElement('div');
        timeDisplay.style.display = 'inline-block';
        timeDisplay.style.marginLeft = '10px';
        timeDisplay.style.color = '#ddd';

        const timeDuration = document.querySelector('.ytp-time-duration');
        const timeContainer = document.querySelector('.ytp-time-display');
        const appContainer = document.querySelector('ytd-app');

        if (timeDuration && timeContainer) {
            timeContainer.appendChild(timeDisplay);
        }
        setupMutationObserver(appContainer);
    };

    const setupMutationObserver = (appContainer) => {
        const observer = new MutationObserver((mutationsList, observer) => {
            updateActiveVideo();
        });
        observer.observe(appContainer, { subtree: true, childList: true });
    };

    const updateActiveVideo = () => {
        const isMiniplayerActive = document.querySelector('ytd-app').hasAttribute('miniplayer-is-active');
        const videoElement = document.querySelector('video');

        if (!isMiniplayerActive && videoElement) {
            if (activeVideoInterval) {
                clearInterval(activeVideoInterval);
            }

            activeVideoElement = videoElement;
            updateVideoTime();
            activeVideoInterval = setInterval(updateVideoTime, 1000);
        } else {
            clearInterval(activeVideoInterval);
            activeVideoElement = null;
            timeDisplay.textContent = '';
        }
    };

    const updateVideoTime = () => {
        const { currentTime, duration, playbackRate } = activeVideoElement;
        const timeRemaining = (duration - currentTime) / playbackRate;

        const hoursRemaining = Math.floor(timeRemaining / 3600);
        const minutesRemaining = Math.floor((timeRemaining % 3600) / 60);
        const secondsRemaining = Math.floor(timeRemaining % 60);

        let formattedTimeRemaining = '';
        if (hoursRemaining > 0) {
            formattedTimeRemaining += hoursRemaining.toString() + ':';
        }
        formattedTimeRemaining += minutesRemaining.toString().padStart(2, '0') + ':' + secondsRemaining.toString().padStart(2, '0');

        timeDisplay.textContent = '(' + formattedTimeRemaining + ')';
    };

    const checkVideoPageActiveInterval = () => {
        checkAndMonitorVideoPage();
        window.requestAnimationFrame(checkVideoPageActiveInterval);
    };

    checkVideoPageActiveInterval();
})();

QingJ © 2025

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