您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add progress bars (or dots) at bottom of YouTube video and progress text on the video page. On the progress text, the current video quality will have a "+" suffix if there's a higher one available. Hovering the mouse cursor onto the video quality text will show the current and available video quality IDs and short description.
当前为
// ==UserScript== // @name Add YouTube Video Progress // @namespace https://gf.qytechs.cn/en/users/85671-jcunews // @version 1.6.17 // @license GNU AGPLv3 // @author jcunews // @description Add progress bars (or dots) at bottom of YouTube video and progress text on the video page. On the progress text, the current video quality will have a "+" suffix if there's a higher one available. Hovering the mouse cursor onto the video quality text will show the current and available video quality IDs and short description. // @match https://www.youtube.com/* // @grant none // @run-at document-start // ==/UserScript== //===== CONFIGURATION BEGIN ===== var progressbarDotStyle = false; //show graphical progress as dots instead of bars var progressbarDotStyleSize = 4; //Dot size (for width & height) in pixels if dot style is enabled var progressbarHeight = 2; //in pixels var progressbarColor = "rgb(0, 0, 0, 0.3)"; //e.g. opaque: "#fff" or "#e0e0e0" or "cyan"; or semi-transparent: "rgb(0, 0, 0, 0.3)" (i.e. 30% opaque) var progressbarElapsedColor = "#f00"; var progressbarBufferColor = "#37f"; var contentLoadProcessDelay = 0; //number of milliseconds before processing dynamically loaded contents (increase if slow network/browser) //styles for progress text (non dark mode) var progressTextStyles = "margin-left:1ex;border:1px solid #ccc;border-radius:4px;padding:2px;background:#eee;text-align:center;font-size:9pt"; //styles for progress text (dark mode) var progressTextStylesDark = "margin-left:1ex;border:1px solid #eee;border-radius:4px;padding:2px;background:#111;text-align:center;font-size:9pt;color:#eee"; //===== CONFIGURATION END ===== var timerWaitInfo, timerProgressMonitor, timerWaitPlayer, timerDoubleCheck, vplayer, eleProgressText; var resNums = { "light" : "144p", //(old ID) "tiny" : "144p", "small" : "240p", "medium" : "360p", //nHD "large" : "480p", //WNTSC "hd720" : "720p", //HD 1K "hd1080" : "1080p", //FHD 2K "hd1440" : "1440p", //QHD "hd2160" : "2160p", //UHD 4K "hd2880" : "2880p", //UHD+ 5K "highres": "4320p", //FUHD 8K (YouTube's highest resolution [2019 April]) "hd6480" : "6480p", //(fictional ID for 12K. Just in case...) "hd8640" : "8640p" //(fictional ID for QUHD 16K. Just in case...) }; var resDescs = { "light" : "light\xa0(144p)", "tiny" : "tiny\xa0(144p)", "small" : "small\xa0(240p)", "medium" : "medium\xa0(360p nHD)", "large" : "large\xa0(480p WNTSC)", "hd720" : "hd720\xa0(720p HD 1K)", "hd1080" : "hd1080\xa0(1080p FHD 2K)", "hd1440" : "hd1440\xa0(1440p QHD)", "hd2160" : "hd2160\xa0(2160p UHD 4K)", "hd2880" : "hd2880\xa0(2880p UHD+ 5K)", "highres": "highres\xa0(4320p FUHD 8K)", "hd6480" : "hd6480\xa0(6480p 12K)", "hd8640" : "hd8640\xa0(8640p QUHD 16K)" }; var fmts = [ ['3GP', 'MP4V', [13,17,36]], ['FLV', 'H263', [5,6]], ['FLV', 'H264', [34,35]], ['MP4', 'H264', [18,22,37,38,59,78,82,83,84,85,91,92,93,94,95,96,132,133,134,135,136,137,138,151,160,212,264,266,298,299]], ['WebM', 'VP8', [43,44,45,46,100,101,102,167,168,169,170,218,219]], ['WebM', 'VP9', [242,243,244,245,246,247,248,271,272,278,302,303,308,313,315]], ['M4A', 'AAC', [139,140,141,256,258]], ['M4A', 'DTS-ES', [325]], ['M4A', 'AC-3', [328]], ['WebM', 'Vorbis', [171,172]], ['WebM', 'Opus', [249,250,251]] ]; var fmtMaps = {}; fmts.forEach(a => a[2].forEach(f => fmtMaps[f] = [a[0], a[1]])); function processInfo() { if (window.vidprogress || (location.pathname !== "/watch")) return; clearTimeout(timerWaitInfo); (function waitInfo(a) { if (a = document.querySelector("#info-contents #info, #watch7-user-header")) { eleProgressText = document.createElement("SPAN"); eleProgressText.id = "vidprogress"; eleProgressText.innerHTML = '<span id="curq" style="font-weight:500"></span><span id="curtime" style="display:inline-block;margin-left:1ex"></span>'; eleProgressText.style.cssText = document.documentElement.attributes["dark"] ? progressTextStylesDark : progressTextStyles; if (window["body-container"]) { a.appendChild(eleProgressText); } else a.insertBefore(eleProgressText, a.querySelector("#flex")); } else timerWaitInfo = setTimeout(waitInfo, 200); })(); } function processPlayer() { function zerolead(n){ return n > 9 ? n : "0" + n; } function sec2hms(sec) { var c = sec % 60, d = Math.floor(sec / 60); return (d >= 60 ? zerolead(Math.floor(d / 60)) + ":" : "") + zerolead(d % 60) + ":" + zerolead(c); } function getPlayer() { return (vplayer = document.querySelector(".html5-video-player")); } function updProgress(a, b, c, d, e, f, g, l){ if (eleProgressText) { if (document.documentElement.attributes["dark"]) { if (eleProgressText.style.cssText !== progressTextStylesDark) eleProgressText.style.cssText = progressTextStylesDark; } else if (eleProgressText.style.cssText !== progressTextStyles) eleProgressText.style.cssText = progressTextStyles; } a = getPlayer(); if (a && window.vidprogress2b && a.getCurrentTime) try { if (window.curtime) try { b = a.getPlaybackQuality(); c = resNums[b] || b; (d = a.getAvailableQualityLevels()).pop(); curq.textContent = c + (d.indexOf(b) > 0 ? "+" : ""); e = a.getVideoStats(); g = fmtMaps[e.afmt] || ("a" + e.afmt); if (e.fmt) { //has video if (f = fmtMaps[e.fmt]) { f = `${f[0]} ${f[1]}`; } else f = "vid" + e.fmt; if (e.afmt) { //video & audio if (g = fmtMaps[e.afmt]) { e = ` [${f} ${g[1]}]`; } else e = ` [${f} aud${e.afmt}]`; } else { //no audio. video only e = ` [${f}]`; } } else if (e.afmt) { //no video. audio only if (f = fmtMaps[e.afmt]) { e = ` [${f[0]} ${f[1]}]`; } else e = ` [aud${e.afmt}]`; } else e = ""; curq.title = `Current: ${resDescs[b] || b}${e} (${a.offsetWidth}x${a.offsetHeight} viewport)\nAvailable: ${d.map(b => resDescs[b] || b).join(", ")}`; } catch(b) { curq.textContent = "???"; curq.title = ""; } b = a.getCurrentTime(); if (b >= 0) { l = a.getDuration(); if (!a.getVideoData().isLive) { if (window.curtime) { curtime.textContent = sec2hms(Math.floor(b)) + " / " + sec2hms(Math.floor(l)) + " (" + Math.floor(b * 100 / l) + "%)"; } if (progressbarDotStyle) { vidprogress2b.style.left = Math.ceil((b / l) * vidprogress2.offsetWidth) + "px"; vidprogress2c.style.left = Math.ceil((a.getVideoBytesLoaded() / a.getVideoBytesTotal()) * vidprogress2.offsetWidth) + "px"; } else { vidprogress2b.style.width = Math.ceil((b / l) * vidprogress2.offsetWidth) + "px"; vidprogress2c.style.width = Math.ceil((a.getVideoBytesLoaded() / a.getVideoBytesTotal()) * vidprogress2.offsetWidth) + "px"; } } else { if (window.curtime) curtime.textContent = "LIVE"; if (progressbarDotStyle) { vidprogress2b.style.left = "100%"; } else vidprogress2b.style.width = "100%"; } } else throw 0; } catch(a) { if (window.curtime) curtime.textContent = "???"; if (progressbarDotStyle) { vidprogress2b.style.left = "0px"; vidprogress2c.style.left = "0px"; } else { vidprogress2b.style.width = "0px"; vidprogress2c.style.width = "0px"; } } } function resumeProgressMonitor() { if (timerProgressMonitor) return; updProgress(); timerProgressMonitor = setInterval(updProgress, 200); } function pauseProgressMonitor() { clearInterval(timerProgressMonitor); timerProgressMonitor = 0; updProgress(); } clearInterval(timerProgressMonitor); timerProgressMonitor = 0; clearTimeout(timerWaitPlayer); timerWaitPlayer = 0; clearInterval(timerDoubleCheck); timerDoubleCheck = 0; (function waitPlayer(v) { if (!window.vidprogress2 && getPlayer() && (a = vplayer.parentNode.querySelector("video"))) { b = document.createElement("DIV"); b.id = "vidprogress2"; b.style.cssText = `opacity:.66;position:absolute;z-index:10;bottom:0;width:100%;height:${ progressbarDotStyle ? progressbarDotStyleSize : progressbarHeight}px;background:${progressbarColor}`; v = progressbarDotStyle ? "width:" + progressbarDotStyleSize + "px;margin-left:-" + Math.floor(progressbarDotStyleSize / 2) + "px;" : ""; b.innerHTML = `<div id="vidprogress2c" style="position:absolute;${v}height:100%;background:${progressbarBufferColor}"></div> <div id="vidprogress2b" style="position:absolute;${v}height:100%;background:${progressbarElapsedColor}"></div>`; vplayer.appendChild(b); if (vplayer.getPlayerState() === 1) resumeProgressMonitor(); //useful: onLoadedMetadata(), onStateChange(state), onPlayVideo(info), onReady(playerApi), onVideoAreaChange(), onVideoDataChange(info) //states: -1=notReady, 0=ended, 1=playing, 2=paused, 3=ready, 4=???, 5=notAvailable? vplayer.addEventListener("onLoadedMetadata", resumeProgressMonitor); vplayer.addEventListener("onStateChange", function(state) { if (state === 1) { resumeProgressMonitor(); } else pauseProgressMonitor(); }); } else timerWaitPlayer = setTimeout(waitPlayer, 200); })(); function doubleCheck() { if (getPlayer() && vplayer.getPlayerState) { if (vplayer.getPlayerState() === 1) { resumeProgressMonitor(); } else pauseProgressMonitor(); } } if (!timerDoubleCheck) timerDoubleCheck = setInterval(doubleCheck, 500); } addEventListener("yt-page-data-updated", processInfo); addEventListener("yt-player-released", processPlayer); addEventListener("load", function() { processInfo(); processPlayer(); }); addEventListener("spfprocess", function() { setTimeout(function() { processInfo(); processPlayer(); }, contentLoadProcessDelay); });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址