🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

YouTube End Time Display

Displays the actual end time of YouTube videos next to the time display

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name YouTube End Time Display
// @namespace YouTube End Time Display
// @version 1.0.0
// @description Displays the actual end time of YouTube videos next to the time display
// @author DumbGPT
// @match https://www.youtube.com/*
// @grant none
// ==/UserScript==

(function() {
'use strict';

let etaElement = null;
let updateInterval = null;

function init() {
if (location.href.includes('youtube.com/watch')) {
const timeDisplay = document.querySelector('.ytp-time-display');
if (timeDisplay) {
setupEtaDisplay(timeDisplay);
} else {
setTimeout(init, 1000);
}
}
}

function setupEtaDisplay(timeDisplay) {
if (etaElement) {
etaElement.remove();
}

etaElement = document.createElement('span');
etaElement.id = 'yt-eta-display';

const timeDisplayStyleRef = timeDisplay.querySelector('.ytp-time-current') || timeDisplay;
const timeStyles = window.getComputedStyle(timeDisplayStyleRef);

etaElement.style.fontFamily = timeStyles.fontFamily;
etaElement.style.fontSize = timeStyles.fontSize;
etaElement.style.color = timeStyles.color;
etaElement.style.fontWeight = timeStyles.fontWeight;
etaElement.style.pointerEvents = 'none'; 

timeDisplay.appendChild(etaElement);

const video = document.querySelector('video');
if (video) {
updateEtaDisplay(video);

if (updateInterval) {
clearInterval(updateInterval);
}
updateInterval = setInterval(() => updateEtaDisplay(video), 1000);

video.addEventListener('seeking', () => updateEtaDisplay(video));
video.addEventListener('ratechange', () => updateEtaDisplay(video));
} else {
setTimeout(() => {
const newVideo = document.querySelector('video');
if (newVideo) setupEtaDisplay(timeDisplay);
}, 1000);
}
}

function updateEtaDisplay(video) {
if (!video || !video.duration || isNaN(video.duration) || !etaElement) return;

const isLive = document.querySelector('.ytp-live') !== null || video.duration === Infinity;
if (isLive) {
etaElement.textContent = "";
return;
}

const remainingTime = video.duration - video.currentTime;

const now = new Date();
const endTime = new Date(now.getTime() + (remainingTime * 1000 / video.playbackRate));

const hours = endTime.getHours().toString().padStart(2, '0');
const minutes = endTime.getMinutes().toString().padStart(2, '0');

etaElement.textContent = ` | ETA ${hours}:${minutes}`;
}

let lastUrl = location.href;
const observer = new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;

if (updateInterval) {
clearInterval(updateInterval);
updateInterval = null;
}

if (etaElement) {
etaElement.remove();
etaElement = null;
}

setTimeout(init, 1000);
}
});

observer.observe(document.body, { childList: true, subtree: true });

init();

window.addEventListener('beforeunload', () => {
if (updateInterval) {
clearInterval(updateInterval);
}
if (observer) {
observer.disconnect();
}
});
})();