Displays the remaining duration of a YouTube video next to the video duration, taking into account the playback rate.
当前为
// ==UserScript==
// @name YouTube - Remaining time
// @namespace https://gist.github.com/4lrick/cf14cf267684f06c1b7bc559ddf2b943
// @version 1.0
// @description Displays the remaining duration of a YouTube video next to the video duration, taking into account the playback rate.
// @author 4lrick
// @match https://www.youtube.com/*
// @grant none
// @license GNU GPLv3
// ==/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();
})();