视频左上显示剩余时长

在视频播放时显示剩余时间和进度条, 包括已加载但未播放的部分

当前为 2025-01-20 提交的版本,查看 最新版本

// ==UserScript==
// @name         视频左上显示剩余时长
// @author        He
// @version      1.1
// @description  在视频播放时显示剩余时间和进度条, 包括已加载但未播放的部分
// @match        *://*/*
// @exclude      *://*live*/*
// @exclude     *://www.huya.com/*
// @exclude     *://www.douyu.com/*
// @namespace https://greasyfork.org/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);
})();