Greasy Fork is available in English.
在视频播放时显示剩余时间和进度条, 包括已加载但未播放的部分
当前为
// ==UserScript==
// @name 视频左上显示剩余时长
// @author He
// @version 1.1
// @description 在视频播放时显示剩余时间和进度条, 包括已加载但未播放的部分
// @match *://*/*
// @exclude *://*live*/*
// @exclude *://www.huya.com/*
// @exclude *://www.douyu.com/*
// @namespace http://greasyfork.icu/users/808960
// ==/UserScript==
(function() {
'use strict';
function setupVideoTimeDisplay(video) {
const container = document.createElement('div');
container.style.cssText = `
position: absolute;
left: 10px;
top: 10px;
z-index: 1000;
`;
const timeDisplay = document.createElement('div');
timeDisplay.id = 'timeDisplay';
timeDisplay.style.cssText = `
color: #C8DCC8;
background: rgba(0, 0, 0, 0.5);
padding: 1px;
font-family: 'DS-Digital', sans-serif;
font-size: 22px;
text-align: center;
border-radius: 5px; /* Add this line for rounded corners */
`;
const progressBar = document.createElement('div');
progressBar.id = 'progressBar';
progressBar.style.cssText = `
width: 100px;
height: 2px;
background: rgba(255, 255, 255, 0.3); /* Semi-transparent white background */
margin-top: 0px;
overflow: hidden;
position: relative;
`;
const bufferedBar = document.createElement('div');
bufferedBar.id = 'bufferedBar';
bufferedBar.style.cssText = `
width: 0%;
height: 100%;
background: #FF6A00;
position: absolute;
top: 0;
left: 0;
`;
const progressBarInner = document.createElement('div');
progressBarInner.id = 'progressBarInner';
progressBarInner.style.cssText = `
width: 0%;
height: 100%;
background: skyblue;
position: absolute;
top: 0;
left: 0;
`;
progressBar.appendChild(bufferedBar);
progressBar.appendChild(progressBarInner);
container.appendChild(timeDisplay);
container.appendChild(progressBar);
// If video has a parent with relative positioning, append to that for better overlay
let parent = video.parentElement;
while (parent && getComputedStyle(parent).position !== 'relative') {
parent = parent.parentElement;
}
if (parent) {
parent.appendChild(container);
} else {
document.body.appendChild(container);
}
function updateTimeAndProgress() {
const remainingTime = video.duration - video.currentTime;
if (isFinite(remainingTime)) {
const minutes = Math.floor(remainingTime / 60);
const seconds = Math.floor(remainingTime % 60);
timeDisplay.textContent = `${minutes}:${seconds < 10 ? '0' + seconds : seconds}`;
// Update played progress bar
const progress = (video.currentTime / video.duration) * 100;
progressBarInner.style.width = `${progress}%`;
// Update buffered progress bar
let buffered = 0;
for (let i = 0; i < video.buffered.length; i++) {
if (video.buffered.start(video.buffered.length - 1 - i) < video.currentTime && video.buffered.end(video.buffered.length - 1 - i) > video.currentTime) {
buffered = video.buffered.end(video.buffered.length - 1 - i) / video.duration * 100;
break;
}
}
bufferedBar.style.width = `${buffered}%`;
}
}
video.addEventListener('timeupdate', updateTimeAndProgress);
video.addEventListener('loadedmetadata', updateTimeAndProgress);
video.addEventListener('progress', updateTimeAndProgress); // For buffer updates
}
// Observes DOM changes for video elements
const observer = new MutationObserver((mutationsList) => {
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'video') {
setupVideoTimeDisplay(node);
}
});
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Check for existing videos on load
document.querySelectorAll('video').forEach(setupVideoTimeDisplay);
})();