YouTube Permanent ProgressBar

Keeps YouTube progress bar visible all the time.

// ==UserScript==
// @name         YouTube Permanent ProgressBar
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Keeps YouTube progress bar visible all the time.
// @author       Can Kurt
// @match        *://www.youtube.com/*
// @license      MIT
// @repository   https://github.com/cccaaannn/YouTubePermanentProgressBar
// ==/UserScript==

var permanentProgressBar = {

    options: {
        UPDATE_INTERVAL: 500,                    // Update interval in milliseconds, decrease for smoother progress movement or increase for performance.
        PROGRESSBAR_OPACITY_WINDOW: 1,           // Progress bar opacity on window mode between 0 - 1.
        PROGRESSBAR_OPACITY_FULLSCREEN: 0.5,     // Progress bar opacity on fullscreen mode between 0 - 1.

        UPDATE_VIDEO_TIMER: true,
        UPDATE_PROGRESSBAR: true,
        UPDATE_BUFFERBAR: true,
        UPDATE_SCRUBBER: true
    },

    prettifyVideoTime: function (video) {
        let seconds = "" + Math.floor(video.currentTime % 60);
        let minutes = "" + Math.floor((video.currentTime % 3600) / 60);
        let hours = "" + Math.floor(video.currentTime / 3600);
        if (video.currentTime / 60 > 60) {
            return `${hours}:${minutes.padStart(2, '0')}:${seconds.padStart(2, '0')}`;
        }
        else {
            return `${minutes}:${seconds.padStart(2, '0')}`;
        }
    },

    getDuration: function (video, type) {
        if (type === "PROGRESSBAR") {
            return video.currentTime;
        }
        else if (type === "BUFFERBAR") {
            return video.buffered.end(video.buffered.length - 1);
        }
    },

    updateCurrentTimeField: function (player) {
        const video = player.querySelector("video");
        const currentTime = player.querySelector(".ytp-time-current");
        if (!video || !currentTime) {
            return;
        }

        currentTime.innerText = permanentProgressBar.prettifyVideoTime(video);
    },

    updateScrubber: function (passedChapterCount, progressBarLastChapter, chaptersPixelWidthUntilCurrentChapter) {
        const scrubber = document.getElementsByClassName("ytp-scrubber-container");

        if (!scrubber || scrubber.length !== 1) {
            return;
        }

        const positionInfo = progressBarLastChapter.getBoundingClientRect();

        // Chapters has hidden margin and its different on fullscreen
        let chapterMarginFix = passedChapterCount * 2.05;
        if (document.fullscreenElement) {
            chapterMarginFix = passedChapterCount * 3.05;
        }

        const chaptersWithPadding = chaptersPixelWidthUntilCurrentChapter + chapterMarginFix;

        const distanceFromLeft = positionInfo.width + chaptersWithPadding;

        scrubber[0].style.transform = `translateX(${distanceFromLeft}px)`;
    },

    // Deprecated
    updateProgressBar: function (player) {
        // works only on chapterless (old) videos
        const video = player.querySelector("video");
        const progressBar = player.querySelector(".ytp-play-progress");
        const bufferBar = player.querySelector(".ytp-load-progress");
        if (!video || !progressBar || !bufferBar) {
            return;
        }

        progressBar.style.transform = `scaleX(${video.currentTime / video.duration})`;
        bufferBar.style.transform = `scaleX(${video.buffered.end(video.buffered.length - 1) / video.duration})`;
    },

    updateProgressBarWithChapters: function (player, type) {
        // YouTube api does not provides current time in chapters
        // this function finds current time in the chapter by finding the ratio between total video duration and total width of the chapters div

        const video = player.querySelector("video");
        if (video == null || isNaN(video.duration)) {
            return;
        }

        // there can be multiple chapters
        const progressBarWidthsCollection = player.getElementsByClassName("ytp-progress-bar-padding");

        // select progress or bufferBar
        let progressBarChaptersCollection;
        if (type === "PROGRESSBAR") {
            progressBarChaptersCollection = player.getElementsByClassName("ytp-play-progress");
        }
        if (type === "BUFFERBAR") {
            progressBarChaptersCollection = player.getElementsByClassName("ytp-load-progress");
        }

        // quit if elements does not exists
        if (!video || !progressBarWidthsCollection || !progressBarChaptersCollection) {
            return;
        }

        // find the ratio between total video duration and total width of the chapters div
        let totalProgressBarWidth = 0;
        for (let i = 0; i < progressBarWidthsCollection.length; i++) {
            totalProgressBarWidth += progressBarWidthsCollection[i].offsetWidth;
        }
        const durationWidthRatio = video.duration / totalProgressBarWidth;

        // loop inside chapters
        let chaptersPixelWidthUntilCurrentChapter = 0;
        let passedChapterCount = 0;
        for (let i = 0; i < progressBarWidthsCollection.length; i++) {

            // if current time is bigger than durationWidthRatio * (chapters pixel width including current one) scale the current chapter to 1 because we passed it
            if (permanentProgressBar.getDuration(video, type) > durationWidthRatio * (chaptersPixelWidthUntilCurrentChapter + progressBarWidthsCollection[i].offsetWidth)) {
                progressBarChaptersCollection[i].style.transform = "scaleX(1)";

                // increase the current chapters location by adding last watched chapter
                chaptersPixelWidthUntilCurrentChapter += progressBarWidthsCollection[i].offsetWidth;

                passedChapterCount++;
            }

            // If not, it means that we are on this chapter.
            // Find the appropriate size for the chapter and scale it
            else {
                // current time
                let currentTimeInChapterInSeconds = permanentProgressBar.getDuration(video, type) - (durationWidthRatio * chaptersPixelWidthUntilCurrentChapter);

                // total chapter time
                let currentChapterLengthInSeconds = durationWidthRatio * progressBarWidthsCollection[i].offsetWidth;

                let currentChapterTimeRatio = currentTimeInChapterInSeconds / currentChapterLengthInSeconds

                progressBarChaptersCollection[i].style.transform = `scaleX(${currentChapterTimeRatio})`;

                // Update scrubber position to align with progressbar
                if (type === "PROGRESSBAR" && permanentProgressBar.options.UPDATE_SCRUBBER) {
                    const progressBarLastChapter = progressBarChaptersCollection[i];

                    permanentProgressBar.updateScrubber(
                        passedChapterCount,
                        progressBarLastChapter,
                        chaptersPixelWidthUntilCurrentChapter
                    );
                }

                break;
            }

        }

    },

    update: function () {
        // Get video element
        const player = document.querySelector(".html5-video-player");
        if (player == null) {
            return;
        }

        // update css
        if (document.fullscreenElement) {
            document.querySelector(".ytp-chrome-bottom").style.opacity = permanentProgressBar.options.PROGRESSBAR_OPACITY_FULLSCREEN;
        }
        else {
            document.querySelector(".ytp-chrome-bottom").style.opacity = permanentProgressBar.options.PROGRESSBAR_OPACITY_WINDOW;
        }

        // update video timer
        if (permanentProgressBar.options.UPDATE_VIDEO_TIMER) {
            permanentProgressBar.updateCurrentTimeField(player);
        }

        // update PROGRESSBAR
        if (permanentProgressBar.options.UPDATE_PROGRESSBAR) {
            permanentProgressBar.updateProgressBarWithChapters(player, "PROGRESSBAR");
        }

        // update BUFFERBAR
        if (permanentProgressBar.options.UPDATE_BUFFERBAR) {
            permanentProgressBar.updateProgressBarWithChapters(player, "BUFFERBAR");
        }

    },

    start: function () {
        setInterval(permanentProgressBar.update, permanentProgressBar.options.UPDATE_INTERVAL);
    }

};


permanentProgressBar.start();

QingJ © 2025

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