您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Keeps YouTube progress bar visible all the time.
// ==UserScript== // @name YouTube Permanent ProgressBar // @namespace http://tampermonkey.net/ // @version 0.3.8 // @description Keeps YouTube progress bar visible all the time. // @author ChromiaCat // @match *://www.youtube.com/* // @license MIT // ==/UserScript== (function() { "use strict"; var style = document.createElement('style'); var to = { createHTML: s => s }, tp = window.trustedTypes?.createPolicy ? trustedTypes.createPolicy("", to) : to, html = s => tp.createHTML(s); style.type = 'text/css'; style.innerHTML = html( '.ytp-autohide .ytp-chrome-bottom{opacity:1!important;display:block!important}' + '.ytp-autohide .ytp-chrome-bottom .ytp-progress-bar-container{bottom:-1px!important}' + '.ytp-autohide .ytp-chrome-bottom .ytp-chrome-controls{opacity:0!important}' + '.ytp-progress-bar-container:not(:hover) .ytp-scrubber-container{display:none!important}' ); document.getElementsByTagName('head')[0].appendChild(style); var permanentProgressBar = { options: { // To change bar update rate, go to line 161. UPDATE_VIDEO_TIMER: true, UPDATE_PROGRESSBAR: true, UPDATE_BUFFERBAR: true, }, // Converts current video time to a human-readable format. 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')}`; } }, // For progress mode, return current time; for buffer mode, return the end of the buffered range that contains currentTime. getDuration: function(video, type) { if (type === "PROGRESSBAR") { return video.currentTime; } else if (type === "BUFFERBAR") { if (video.buffered.length > 0) { for (let i = 0; i < video.buffered.length; i++) { if (video.currentTime >= video.buffered.start(i) && video.currentTime <= video.buffered.end(i)) { return video.buffered.end(i); } } } return 0; } }, // Updates the current time display on the player. updateCurrentTimeField: function(player) { const video = player.querySelector("video"); const currentTimeEl = player.querySelector(".ytp-time-current"); if (!video || !currentTimeEl) return; currentTimeEl.innerText = permanentProgressBar.prettifyVideoTime(video); }, // For non-chaptered videos, update the progress and buffer bars directly. updateOverallProgressBar: function(player) { const video = player.querySelector("video"); const progressBar = player.querySelector(".ytp-play-progress"); const bufferBar = player.querySelector(".ytp-load-progress"); if (!video || !progressBar || !bufferBar) return; if (!video.duration) return; let progressRatio = video.currentTime / video.duration; let bufferRatio = this.getDuration(video, "BUFFERBAR") / video.duration; progressBar.style.transform = `scaleX(${Math.min(1, progressRatio.toFixed(5))})`; bufferBar.style.transform = `scaleX(${Math.min(1, bufferRatio.toFixed(5))})`; }, // For chaptered videos, update each chapter element directly based on current time. updateProgressBarWithChapters: function(player, type) { const video = player.querySelector("video"); if (!video || isNaN(video.duration)) return; // Get the chapter elements and corresponding progress elements. const chapterElements = player.getElementsByClassName("ytp-progress-bar-padding"); let chapterProgressEls; if (type === "PROGRESSBAR") { chapterProgressEls = player.getElementsByClassName("ytp-play-progress"); } else if (type === "BUFFERBAR") { chapterProgressEls = player.getElementsByClassName("ytp-load-progress"); } if (!chapterElements || !chapterProgressEls) return; // Compute total width of the progress bar (sum of all chapter widths) let totalWidth = 0; for (let i = 0; i < chapterElements.length; i++) { totalWidth += chapterElements[i].offsetWidth; } const durationWidthRatio = video.duration / totalWidth; let accumulatedWidth = 0; for (let i = 0; i < chapterElements.length; i++) { const chapterWidth = chapterElements[i].offsetWidth; const chapterEndTime = durationWidthRatio * (accumulatedWidth + chapterWidth); const chapterStartTime = durationWidthRatio * accumulatedWidth; let currentTimeForType = this.getDuration(video, type); let ratio; if (currentTimeForType >= chapterEndTime) { ratio = 1; } else if (currentTimeForType < chapterStartTime) { ratio = 0; } else { ratio = (currentTimeForType - chapterStartTime) / (chapterEndTime - chapterStartTime); } chapterProgressEls[i].style.transform = `scaleX(${Math.min(1, ratio.toFixed(5))})`; accumulatedWidth += chapterWidth; } }, // The main update function which selects chapter-mode or overall mode. update: function() { const player = document.querySelector(".html5-video-player"); if (!player) return; if (this.options.UPDATE_VIDEO_TIMER) { this.updateCurrentTimeField(player); } // If chapter elements exist, update chapter-mode; otherwise use overall mode. let chapterElements = player.getElementsByClassName("ytp-progress-bar-padding"); if (chapterElements.length > 0) { if (this.options.UPDATE_PROGRESSBAR) { this.updateProgressBarWithChapters(player, "PROGRESSBAR"); } if (this.options.UPDATE_BUFFERBAR) { this.updateProgressBarWithChapters(player, "BUFFERBAR"); } } else { this.updateOverallProgressBar(player); } }, // The update loop runs on each animation frame. // Update loop code with update rate settings improved by https://greasyfork.org/en/users/475505-tettedev updateLoopThrottled: function() { const loop = (ups) => { setTimeout(() => { permanentProgressBar.update(); requestAnimationFrame(permanentProgressBar.updateLoop); }, 1000 / ups); }; const updateRate = 20; // How many per second. Floats are allowed e.g. 4.20 loop(updateRate); }, updateLoop: function() { permanentProgressBar.update(); requestAnimationFrame(permanentProgressBar.updateLoop); }, start: function() { requestAnimationFrame(this.updateLoopThrottled); // (this.updateLoop) Happens every aniamtion frame, // (this.updateLoopThrottled) is configurable to run slower but may cause jittering if YT's code updates the bar at the same time e.g. when exposing the player's controls. // I'm not sure how to disable YT's bar updating code to allow flawless bar updating for slower loop code, if you know how let me know. } }; permanentProgressBar.start(); })();