- // ==UserScript==
- // @name Add YouTube Video Progress
- // @namespace https://gf.qytechs.cn/en/users/85671-jcunews
- // @version 1.20.139
- // @license GNU AGPLv3
- // @author jcunews
- // @description Adds a progress bars (or dots) at bottom of video, a progress text which includes video quality and subtitle indicators, and a chapter title box on the YouTube video page.
- // @match https://www.youtube.com/*
- // @grant GM_getValue
- // @grant GM_registerMenuCommand
- // @grant GM_setValue
- // @grant unsafeWindow
- // @inject-into page
- // @run-at document-start
- // ==/UserScript==
-
- /*
- Changelog (this version only. Full list is gf.qytechs.cn):
- - Updated media formats support.
-
- Notes:
- - 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.
- - A "[C]" text may be shown to indicate subtitle availability, and hovering the mouse over it will show the available subtitle languages.
- - For videos which have 60fps and/or HDR video qualities, the listed available video qualities only includes the highest quality ones.
- The video player may present a lower video quality depending on the network speed, web browser capability, and user account setting.
- - Hovering the chapter box will display the previous and/or next chapter titles.
- - Hovering the mouse over the publication date will show a tooltip containing the complete publication date in local time, considering
- that YouTube may only shows how long the video was published. If the video was live streamed, it will contain the starting and
- ending recording time.
- - For new YouTube layout only.
- - TamperMonkey users should change the TamperMonkey's "Inject Mode" setting to "Instant" for more reliable script result.
- */
-
- (window => {
-
- //===== CONFIGURATION BEGIN =====
-
- //*** Default settings (configurable via GUI) ***
- var progressbarAutohide = true; //autohide progressbar when YouTube video controls is visible
- var progressbarBelowVideo = false; //show progressbar below video frame instead of within video frame
- var progressbarDotStyle = false; //show graphical progress as dots instead of bars
- var progressbarHeight = 3; //in pixels
- var progressbarColor = "rgb(0,0,0,0.2)"; //e.g. opaque: "#fff" or "#e0e0e0" or "cyan"; or semi-transparent: "rgb(0,0,0,0.3)" (i.e. 30% opaque)
- var progressbarElapsedColor = "rgb(255,0,0,0.7)";
- var progressbarBufferColor = "rgb(51,119,255,0.7)";
- var progressbarDotStyleSize = 4; //Dot size (for width & height) in pixels if dot style is enabled
- var progressTextEnabled = true; //enable showing progress text including chapter title
- var progressTextInDocTitle = false; //also include progress text in document (browser tab) title
- var subtitleLanguageCode = "en"; //2-letters language code for select preferred subtitle language hotkey
-
- //*** Internal settings (not configurable via GUI) ***
- var contentLoadProcessDelay = 0; //number of milliseconds before processing dynamically loaded contents (increase if slow network/browser)
- var progressTextStyles = "display:inline-block;vertical-align:top;border:1px solid #ccc;border-radius:4px;padding:2px;background:#eee;text-align:center;white-space:nowrap;font-size:9pt;line-height:normal";
- //styles override for progress text in YouTube Dark Mode
- var progressTextStylesDark = "border:1px solid #bbb;background:#111;color:#bbb";
- //style rules override for configuration GUI in YouTube Dark Mode
- var configGuiStylesDark = `
- #ayvpPopup{border-color:#bbb;background:#111;color:#bbb}
- #ayvpCaption{background:#333;color:#ddd;font-weight:bold}
- #ayvpContent input,
- #ayvpControls button{background:#333;color:#bbb}
- `;
- //swap functionality of Click and Ctrl+Click on subtitle indicator
- //false: Click = toggle subtitle; Ctrl+Click = use preferred subtitle
- //true : Click = use preferred subtitle; Ctrl+Click = toggle subtitle
- var swapSubtitleFunction = true;
-
- //===== CONFIGURATION END =====
-
- var timerWaitInfo, timerProgressMonitor, timerWaitPlayer, timerDoubleCheck, vplayer, eleProgressText, eleChapter, fmtMaps = {};
- 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\xa0~QCIF)", //(old ID)
- "tiny" : "tiny\xa0(144p\xa0~QCIF)",
- "small" : "small\xa0(240p\xa0~SIF)",
- "medium" : "medium\xa0(360p\xa0nHD)",
- "large" : "large\xa0(480p\xa0WNTSC)",
- "hd720" : "hd720\xa0(720p\xa0HD\xa01K)",
- "hd1080" : "hd1080\xa0(1080p\xa0FHD\xa02K)",
- "hd1440" : "hd1440\xa0(1440p\xa0QHD)",
- "hd2160" : "hd2160\xa0(2160p\xa0UHD\xa04K)",
- "hd2880" : "hd2880\xa0(2880p\xa0UHD+\xa05K)",
- "highres": "highres\xa0(4320p\xa0FUHD 8K)", //YouTube's highest resolution [2019 April]
- "hd6480" : "hd6480\xa0(6480p\xa012K)", //fictional ID for 12K. Just in case...
- "hd8640" : "hd8640\xa0(8640p\xa0QUHD\xa016K)" //fictional ID for QUHD 16K. Just in case...
- };
- 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,216,229,230,231,232,264,266,269,270,298,299,304,305,311,312,597]],
- ['MP4', 'AV1', [394,395,396,397,398,399,400,401,402,571,694,695,696,697,698,699,700,701,702]],
- ['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,598,602,603,604,605,606,609,612,614,616,617,620,623,625,628]],
- ['WebM', 'VP9.2', [330,331,332,333,334,335,336,337,338,339]], //HDR. 388 & 339 for 2880p & 4320p are assumed. may actually be incorrect.
- ['M4A', 'AAC', [139,140,141,256,258]],
- ['M4A', 'DTS-ES', [325]],
- ['M4A', 'AC-3', [328]],
- ['WebM', 'Vorbis', [171,172]],
- ['WebM', 'Opus', [249,250,251]]
- ];
- fmts.forEach(a => a[2].forEach(f => fmtMaps[f] = [a[0], a[1]]));
-
- var langList = ("\
- Afrikaans,af;Albanian,sq;Amharic,am;Arabic,ar;Armenian,hy;Azerbaijani,az;Bangla,bn;Basque,eu;Belarusian,be;Bosnian,bs;\
- Bulgarian,bg;Burmese,my;Catalan,ca;Cebuano,ceb;Chinese (Simplified),zh-Hans;Chinese (Traditional),zh-Hant;Corsican,co;\
- Croatian,hr;Czech,cs;Danish,da;Dutch,nl;English,en;Esperanto,eo;Estonian,et;Filipino,fil;Finnish,fi;French,fr;Galician,gl;\
- Georgian,ka;German,de;Greek,el;Gujarati,gu;Haitian Creole,ht;Hausa,ha;Hawaiian,haw;Hebrew,iw;Hindi,hi;Hmong,hmn;Hungarian,hu;\
- Icelandic,is;Igbo,ig;Indonesian,id;Irish,ga;Italian,it;Japanese,ja;Javanese,jv;Kannada,kn;Kazakh,kk;Khmer,km;Kinyarwanda,rw;\
- Korean,ko;Kurdish,ku;Kyrgyz,ky;Lao,lo;Latin,la;Latvian,lv;Lithuanian,lt;Luxembourgish,lb;Macedonian,mk;Malagasy,mg;Malay,ms;\
- Malayalam,ml;Maltese,mt;Maori,mi;Marathi,mr;Mongolian,mn;Nepali,ne;Norwegian,no;Nyanja,ny;Odia,or;Pashto,ps;Persian,fa;\
- Polish,pl;Portuguese,pt;Punjabi,pa;Romanian,ro;Russian,ru;Samoan,sm;Scottish Gaelic,gd;Serbian,sr;Shona,sn;Sindhi,sd;\
- Sinhala,si;Slovak,sk;Slovenian,sl;Somali,so;Southern Sotho,st;Spanish,es;Sundanese,su;Swahili,sw;Swedish,sv;Tajik,tg;\
- Tamil,ta;Tatar,tt;Telugu,te;Thai,th;Turkish,tr;Turkmen,tk;Ukrainian,uk;Urdu,ur;Uyghur,ug;Uzbek,uz;Vietnamese,vi;Welsh,cy;\
- Western Frisian,fy;Xhosa,xh;Yiddish,yi;Yoruba,yo;Zulu,zu").split(";").map(a => a.split(","));
-
- var subtitleTooltips = {"false": "Use preferred subtitle language", "true": "Toggle subtitle"};
-
- function loadConfig(r, d, z) {
- d = {
- id: "ayvp", ver: 1, pbAutohide: progressbarAutohide, pbPosBelow: progressbarBelowVideo, pbDotStyle: progressbarDotStyle, pbHeight: progressbarHeight,
- pbColor: progressbarColor, pbElapsedColor: progressbarElapsedColor, pbBufferColor: progressbarBufferColor,
- pbDotStyleSize: progressbarDotStyleSize, ptEnabled: progressTextEnabled, ptDocTitle: progressTextInDocTitle, subLangCode: subtitleLanguageCode
- };
- r = GM_getValue("cfg", "");
- if (r) {
- try {
- r = JSON.parse(r);
- } catch(z) {
- r = {};
- }
- if (r.id === "ayvp") {
- Object.keys(d).forEach(k => {
- if (!(k in r)) r[k] = d[k];
- });
- } else {
- GM_setValue("cfg", JSON.stringify(d));
- alert("YouTube Video Progressbar configuration has been reset to default due to data corruption.");
- }
- } else {
- GM_setValue("cfg", JSON.stringify(r = d));
- }
- return r;
- }
-
- var cfg = loadConfig();
-
- function updProgressTextPos(a) {
- window["info-text"].classList.remove("floatingProgress");
- if (eleProgressText.offsetTop) window["info-text"].classList.add("floatingProgress");
- }
-
- var ytpr, ql, resDescs2, orgTitle, chaps;
-
- function getPlayer() {
- if (ytpr && window.movie_player) {
- return vplayer = window.movie_player;
- } else return (vplayer = document.querySelector(".html5-video-player"));
- }
-
- function selectCaption(ev, v, o, c, a) {
- if ((v = getPlayer()) && (o = v.getPlayerResponse().captions) && (o = o.playerCaptionsTracklistRenderer) && (o = o.captionTracks)) {
- if (ev && (ev.ctrlKey === swapSubtitleFunction)) {
- ev.preventDefault();
- ev.stopPropagation();
- ev.stopImmediatePropagation();
- v.toggleSubtitles();
- return;
- }
- if ((c = v.getOption("captions", "track")) && c.vss_id) {
- if (c.vss_id === ("." + cfg.subLangCode)) {
- a = o.find(ct => ct.vssId === ("a." + cfg.subLangCode));
- if (!a) a = o.find(ct => ct.isTranslatable && (ct.vssId[0] === ".") && (ct.vssId.substr(1) !== cfg.subLangCode));
- if (!a) a = o.find(ct => ct.isTranslatable && (ct.vssId[1] === ".") && (ct.vssId.substr(2) !== cfg.subLangCode));
- }
- if (!a && (c.vss_id === ("a." + cfg.subLangCode))) {
- a = o.find(ct => ct.isTranslatable && (ct.vssId[0] === ".") && (ct.vssId.substr(1) !== cfg.subLangCode));
- if (!a) a = o.find(ct => ct.isTranslatable && (ct.vssId[1] === ".") && (ct.vssId.substr(2) !== cfg.subLangCode));
- }
- if (!a && c.is_translateable && (c.vss_id[0] === ".") && (c.vss_id.substr(1) !== cfg.subLangCode)) {
- a = o.find(ct => ct.isTranslatable && (ct.vssId[1] === ".") && (ct.vssId.substr(2) !== cfg.subLangCode));
- }
- }
- if (!a) {
- a = o.find(ct => ct.vssId === ("." + cfg.subLangCode));
- if (!a) a = o.find(ct => ct.vssId === ("a." + cfg.subLangCode));
- if (!a) a = o.find(ct => ct.isTranslatable && (ct.vssId[0] === ".") && (ct.vssId.substr(1) !== cfg.subLangCode));
- if (!a) a = o.find(ct => ct.isTranslatable && (ct.vssId[1] === ".") && (ct.vssId.substr(2) !== cfg.subLangCode));
- if (!a) {
- a = o.find(ct => ct.isTranslatable && (
- ((ct.vssId[0] === ".") && (ct.vssId.substr(1) !== cfg.subLangCode)) ||
- ((ct.vssId[1] === ".") && (ct.vssId.substr(2) !== cfg.subLangCode))
- ));
- }
- if (!a) return;
- }
- a = {languageCode: a.languageCode, vss_id: a.vssId};
- if (a.languageCode !== cfg.subLangCode) {
- v.getPlayerResponse().captions.playerCaptionsTracklistRenderer.translationLanguages.some(l => {
- if (l.languageCode === cfg.subLangCode) {
- a.translationLanguage = {languageCode: cfg.subLangCode};
- a.translationLanguage.languageName = l.languageName.simpleText;
- return true;
- }
- });
- }
- if (!c.languageCode) v.toggleSubtitles();
- v.setOption("captions", "track", a);
- }
- }
-
- var to = {createHTML: s => s}, tp = window.trustedTypes?.createPolicy ? trustedTypes.createPolicy("", to) : to, html = s => tp.createHTML(s);
-
- function processInfo(ev) {
- ql = null;
- if (window.vidprogress || !/^\/watch|\/shorts\//.test(location.pathname)) return;
- clearTimeout(timerWaitInfo);
- if (!eleProgressText) {
- eleProgressText = document.createElement("DIV");
- eleProgressText.id = "vidprogress";
- eleProgressText.style.cssText = `display:${cfg.ptEnabled ? "inline-block" : "none"}!important`;
- eleProgressText.innerHTML = html(`<span id="curq" style="font-weight:500"></span>
- <span id="subs" style="display:none"></span><span id="curtime" style="display:inline-block;margin-left:1ex"></span>
- <style>
- ytd-watch-grid[theater]{--ytd-watch-flexy-sidebar-width:36vw!important}
- #full-bleed-container.ytd-watch-flexy{z-index:2}
- #ytd-player{overflow:visible!important}
- .html5-video-player{overflow:visible}
- video[style*="top: -"]{top:-9999px!important}
- ytd-watch-flexy #below.ytd-watch-flexy{position:relative}
- ytd-watch-flexy ytd-watch-metadata{padding-top:.6em}
- ytd-watch-grid ytd-watch-metadata[title-headline-m] h1.ytd-watch-metadata{margin-top:-.2em;font-size:2.3em;line-height:normal}
- ytd-watch-grid ytd-watch-metadata[skinny-mode] #ytd-watch-info-text.ytd-watch-metadata{margin-bottom:0}
- ytd-watch-metadata :is(#sponsor-button,#subscribe-button,#actions-inner){align-self:end}
- ytd-watch-metadata :is(#owner,#actions-inner) .yt-spec-button-shape-next--size-m,
- ytd-watch-metadata yt-icon-button.ytd-subscription-notification-toggle-button-renderer{height:18pt;line-height:18pt}
- ytd-watch-metadata yt-icon-button.ytd-subscription-notification-toggle-button-renderer{padding-top:0;padding-bottom:0}
- ytd-segmented-like-dislike-button-renderer .yt-spec-button-shape-next--size-m.yt-spec-button-shape-next--segmented-start::after{top:0;height:18pt;line-height:18pt}
- ytd-watch-metadata #snippet{max-height:4rem!important}
- ytd-watch-metadata #plain-snippet-text{display:none}
- ytd-watch-metadata #snippet-text>yt-formatted-string,
- ytd-watch-metadata #snippet-text>yt-formatted-string>.yt-formatted-string.style-scope:nth-child(5){display:block!important}
- ytd-watch-metadata #description-inline-expander>yt-formatted-string>.yt-formatted-string.style-scope:nth-child(5){display:block!important}
- #info :is(ytd-toggle-button-renderer,ytd-button-renderer):not(:first-child),
- yt-icon-button:is(.ytd-toggle-button-renderer,.ytd-button-renderer):not(:first-child){margin-left:8px}
- #info ytd-toggle-button-renderer, #info ytd-button-renderer{font-size:9pt!important}
- #info ytd-toggle-button-renderer a.ytd-toggle-button-renderer,
- #info ytd-button-renderer a.ytd-button-renderer,
- ytd-button-renderer.force-icon-button a.ytd-button-renderer,
- ytd-button-renderer.force-icon-button a.ytd-button-renderer{padding-right:0}
- #menu-container{white-space:nowrap}
- #menu-container>#menu{display:inline-block}
- #description-and-actions #description,
- #description-and-actions #actions{min-width:0!important}
- #description-and-actions #actions{flex-grow:unset}
- #info yt-icon-button.ytd-toggle-button-renderer,
- #info yt-icon-button.ytd-button-renderer,
- :is(#description-and-actions,#actions) yt-icon-button:is(.ytd-toggle-button-renderer,.ytd-button-renderer){
- width:calc(var(--yt-button-icon-size,var(--yt-icon-width,40px)) - 10px)!important;
- height:calc(var(--yt-button-icon-size,var(--yt-icon-height,40px)) - 10px)!important;
- padding:0 4px 0 0!important;
- }
- #actions ytd-menu-renderer{padding-bottom:0}
- #description-and-actions .ytd-menu-renderer[style-target="button"]{width:calc(var(--yt-icon-width) - 14px);height:calc(var(--yt-icon-height) - 14px)}
- #actions ytd-toggle-button-renderer.force-icon-button a.ytd-toggle-button-renderer,
- #actions ytd-button-renderer.force-icon-button a.ytd-button-renderer{padding-right:0}
- #actions yt-icon-button.ytd-toggle-button-renderer,
- #actions yt-icon-button.ytd-button-renderer{padding:8px 0;width:24px}
- #vidprogress{${progressTextStyles};margin-left:10px;margin-right:1em;min-width:27ex;max-height:1.2em}
- #menu-container #vidprogress{margin-top:.6em}
- ytd-watch-metadata #vidprogress{position:absolute;z-index:2;left:50%;transform:translate(-50%,.6em)}
- ytd-watch-grid ytd-watch-metadata #vidprogress{margin-top:0}
- ytd-watch-metadata[skinny-mode] #vidprogress{margin-top:-.5em}
- ytd-watch-grid[swatcheroo-binary-layout] ytd-watch-metadata #vidprogress{transform:translate(-50%,-2em)}
- #description-and-actions #vidprogress{position:static;margin-left:0;margin-top:.9em}
- html[dark] #vidprogress{${progressTextStylesDark}}
- #info-text{position:relative;margin-top:1rem}
- #info-text.floatingProgress{overflow:visible!important}
- #info-text.floatingProgress>#vidprogress{position:absolute;right:0;bottom:1.7rem}
- #vidprogress #subs{cursor:pointer}
- #vidprogress2b,#vidprogress2c{transition:width 100ms linear}
- #vidchap{position:relative;z-index:1}
- ytd-watch-flexy[theater][is-two-columns_] #vidchap{top:-1.2em}
- #chapwrap{
- ${progressTextStyles};display:none;position:absolute;right:0;border:none;margin-left:0;
- padding:0;height:valv(1.2em + 6px);background:transparent;overflow:hidden;
- }
- #chap{
- ${progressTextStyles};position:relative;margin-left:0;margin-top:.4em;border:none;padding:0;transition:margin-top 200ms linear;
- }
- ytd-watch-grid[swatcheroo-binary-layout] #chap{margin-top:0}
- #chapprogresswrap{position:absolute;z-index:0;left:1px;top:1px;width:calc(100% - 2px);height:calc(100% - 2px)}
- #chapprogress{
- ${progressTextStyles};display:block;margin-left:0;border:none;border-radius:0;padding:0;
- height:100%;background:rgb(0,192,0,0.3);pointer-events:none;transition:width 100ms linear;
- }
- #chaptext{${progressTextStyles};position:relative;z-index:1;min-width:4em;margin-left:0;background:transparent}
- html[dark] #chap{${progressTextStylesDark};border:none}
- #chap.chide{margin-top:-2em}
- </style>`);
- }
- (function waitInfo(a, b, c, d) {
- a = (a = document.querySelector("ytd-watch-metadata")) && (getComputedStyle(a).display !== "none") ?
- "ytd-watch-metadata #description-content,ytd-watch-metadata #actions" : "#info-text #date,#menu-container #menu";
- if (b = document.querySelector(a)) {
- if (
- b.matches("ytd-watch-metadata #description") && (c = b.querySelector("#plain-snippet-text")) && !c.tcf && c.firstChild &&
- (d = c.firstChild.data.match(/(^.*?\u2022.*?\u2022\s+)(.*)/))
- ) {
- c.firstChild.data = `${d[1]}\n${d[2]}`;
- c.tcf = true;
- }
- if ((c = b.closest('#secondary')) && (c = c.querySelector('ytd-watch-metadata[skinny-mode] #owner'))) {
- c.parentNode.insertBefore(eleProgressText, c);
- } else b.parentNode.insertBefore(eleProgressText, b);
- subs.addEventListener("click", selectCaption);
- addEventListener("resize", updProgressTextPos);
- addEventListener("yt-navigate-finish", updProgressTextPos);
- updProgressTextPos();
- eleChapter = document.createElement("DIV");
- eleChapter.id = "vidchap";
- eleChapter.innerHTML = html(`<div id="chapwrap" style="display:${cfg.ptEnabled ? "block" : "none"}"><div id="chap" class="chide">
- <div id="chapprogresswrap"><div id="chapprogress"></div></div><div id="chaptext"></div>
- </div></div>`);
- (b = document.querySelector("#primary #player")).parentNode.insertBefore(eleChapter, b.nextSibling);
- setTimeout(updProgressTextPos, 0);
- } else timerWaitInfo = setTimeout(waitInfo, 200);
- })();
- }
-
- function processPlayer(ev) {
- 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 dateElapsed(c, t, r, a, b) {
- r = Math.floor((((t = new Date).getFullYear() * 12 + t.getMonth()) - (c.getFullYear() * 12 + c.getMonth())) / 12);
- if (!r) {
- r = Math.floor(t.getTime() / 86400000) - Math.floor(c.getTime() / 86400000);
- if (!(b = Math.floor(r / 30))) {
- if (!r) {
- return "today"
- } else if (b = Math.floor(r / 7)) {
- return b > 1 ? b + " weeks ago" : "a week ago"
- } else return r > 1 ? r + " days ago" : "a day ago"
- } else return b > 1 ? b + " months ago" : "a month ago"
- } else return r > 1 ? r + " years ago" : "a year ago"
- }
-
- function updProgress(a, b, c, d, e, f, g, h, l){
- a = getPlayer();
- if (a && window.vidprogress2b && a.getCurrentTime) try {
- if (window.curtime) try {
- b = a.getPlaybackQuality();
- if (!ql) {
- if (window["page-manager"] && (c = window["page-manager"].getCurrentData()) &&
- (d = c.playerResponse) && (!ytpr || (d.trackingParams !== ytpr.trackingParams))) {
- dispatchEvent(new Event("resize"));
- setTimeout(() => dispatchEvent(new Event("resize")), 200);
- orgTitle = document.title;
- ytpr = (
- c.player && c.player.args && (
- (c.player.args.player_response && JSON.parse_(c.player.args.player_response)) || c.player.args.raw_player_response
- )
- ) || d;
- if (window.date) window.date.title = "";
- chaps = [];
- chaps.last = 0;
- chapwrap.style.display = "";
- chap.className = "chide"
- if (ytpr && ytpr.videoDetails && ytpr.videoDetails.shortDescription) {
- if (h = ytpr.videoDetails.shortDescription.match(/^(?:\s*\d+\.)?\s*(\d{1,2}:)?\d{1,2}:\d{1,2}\s+\S+.*/gm)) {
- chaps = h.map(s => {
- s = s.match(/^(?:\s*\d+\.)?\s*(\d{1,2}:)?(\d{1,2}):(\d{1,2})\s+(?:(?:-|\u2013|\u2014|\u2015)\s+(?:\d{1,2}:)?\d{1,2}:\d{1,2}\s+)?(?:(?:-|\u2013|\u2014|\u2015)\s+)?(.*?)\s*$/u);
- s[1] = s[1] ? parseInt(s[1]) : 0;
- s[2] = s[2] ? parseInt(s[2]) : 0;
- s[3] = s[3] ? parseInt(s[3]) : 0;
- return {time: (s[1] * 3600) + (s[2] * 60) + s[3], txt: s[4]}
- });
- chaps.some((o, i) => {
- if ((i > 0) && (o.time < chaps[i - 1].time)) {
- chaps.splice(i);
- return true
- }
- });
- }
- }
- if (!chaps.length) {
- if (
- (h = c) && (h = h.response) && (h = h.playerOverlays) && (h = h.playerOverlayRenderer) && (h = h.decoratedPlayerBarRenderer) &&
- (h = h.decoratedPlayerBarRenderer) && (h = h.playerBar) && (h = h.multiMarkersPlayerBarRenderer) && (h = h.markersMap)
- ) {
- h.some(m => {
- if (["AUTO_CHAPTERS", "DESCRIPTION_CHAPTERS"].includes(m.key)) {
- if ((m = m.value) && (m = m.chapters) && m.length && m[0] && m[0].chapterRenderer && m[0].chapterRenderer) {
- chaps = m.map(a => ({time: Math.floor(a.chapterRenderer.timeRangeStartMillis / 1000), txt: a.chapterRenderer.title.simpleText}))
- }
- return true
- }
- })
- }
- }
- if (chaps.length && chaps[0].time) chaps.unshift({time: 0, txt: "(Untitled)"});
- }
- if (ytpr) {
- if ((h = document.querySelector("#info-strings>yt-formatted-string")) && !h.title && d && (e = d.microformat) && (e = e.playerMicroformatRenderer)) {
- if ((f = e.liveBroadcastDetails) && f.startTimestamp) {
- g = "Started: " + (c = new Date(f.startTimestamp)).toLocaleString() + " (" + dateElapsed(c) + ")";
- if (f.endTimestamp) g += "; Ended: " + new Date(f.endTimestamp).toLocaleString();
- } else g = "";
- if (!g) {
- g = ((c = new Date(e.publishDate || e.uploadDate)).toLocaleDateString());
- if (!h.textContent.includes("ago")) g += " (" + dateElapsed(c) + ")";
- }
- h.title = g;
- }
- if (ytpr.streamingData.adaptiveFormats) {
- ql = {};
- resDescs2 = {};
- ytpr.streamingData.adaptiveFormats.forEach((o, i) => {
- if (!o.audioQuality) ql[o.quality] = [o.quality, o.qualityLabel, o.bitrate];
- });
- Object.keys(ql).forEach(k => {
- resDescs2[k] = resDescs[k].replace("(" + resNums[k], "(" + (ql[k] = (ql[k][1] || ql[k][0])));
- });
- }
- }
- }
- if (ql) {
- c = ql[b] || b;
- } else c = resNums[b] || b;
- (d = a.getAvailableQualityLevels()).pop();
- curq.textContent = c + (d.indexOf(b) > 0 ? "+" : "");
- c = 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 = "";
- f = a.querySelector("video");
- g = ql ? resDescs2 : resDescs;
- if ((h = a.getPlayerResponse()?.streamingData?.adaptiveFormats)?.forEach) {
- curq.title = `Current: ${g[b] || b}${e} (${f.videoWidth}x${f.videoHeight} video, ${f.offsetWidth}x${f.offsetHeight} viewport, ${
- ((f, t) => {
- f = [];
- c.fmt && f.push(parseInt(c.fmt));
- c.afmt && f.push(parseInt(c.afmt));
- t = 0;
- h.forEach((o, i) => f.includes(o.itag) && !o.isDrc && (t += parseInt(o.contentLength)));
- return parseFloat((t / 1048576).toFixed(2))
- })()
- }MB estimated size)\nAvailable: ${d.map(b => g[b] || b).join(", ")}`
- } else curq.title = `Current: ${g[b] || b}${e} (${f.videoWidth}x${f.videoHeight} video, ${f.offsetWidth}x${f.offsetHeight} viewport)\nAvailable: ${d.map(b => g[b] || b).join(", ")}`;
- } catch(b) {
- curq.textContent = "???";
- curq.title = b.message;
- }
- if (window.subs) {
- if (ytpr && ytpr.captions && ytpr.captions.playerCaptionsTracklistRenderer && ytpr.captions.playerCaptionsTracklistRenderer.captionTracks) {
- b = (d = ytpr.captions.playerCaptionsTracklistRenderer.captionTracks).map(v => v.name.simpleText);
- if (b.length) {
- c = a.getOption("captions", "track");
- b = [
- c.vss_id &&
- (c.vss_id.substr(0, 2) === "a.") ? "[c]" : ((d.length === 1) && (d[0].vssId[1] === ".") ? "[c]" : "[C]"),
- `Current subtitle: ${
- c && c.languageCode ?
- c.languageName + (c.translationLanguage && c.translationLanguage.languageName ? " >> " + c.translationLanguage.languageName : "") :
- "(none)"
- }\nAvailable subtitles: ${b.join(", ")}\nClick: ${subtitleTooltips[!swapSubtitleFunction]}\nCtrl+Click: ${subtitleTooltips[swapSubtitleFunction]}`,
- `display:inline-block${c && c.languageCode ? ";font-weight:bold" : ""}`
- ];
- } else b = ["", "", ""];
- } else b = ["", "", ""];
- subs.textContent = b[0];
- subs.title = b[1];
- subs.style.cssText = b[2];
- }
- 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(parseFloat((b * 100 / l).toFixed(3))) + "%)";
- if (chaps.length) {
- chaps.some((c, i, d) => {
- if ((b >= c.time) && (((i + 1) === chaps.length) || (b < chaps[i + 1].time))) {
- d = (i < (chaps.length - 1) ? chaps[i + 1].time : l) - c.time;
- chapprogress.style.width = ((b - c.time) * 100 / d) + "%";
- if (i !== chaps.last) {
- function doshow(d) {
- chap.ontransitionend = null;
- chaptext.textContent = c.txt;
- chap.title = `Duration: ${sec2hms(Math.floor(((i + 1) < chaps.length ? chaps[i + 1].time : l) - c.time))}\n` +
- (i ? `Previous: ${chaps[i - 1].txt}` : "") + ((i + 1) < chaps.length ? (i ? "\n" : "") + `Next: ${chaps[i + 1].txt}` : "");
- chapwrap.style.display = "block";
- chap.className = "";
- }
- chaps.last = i;
- if (chap.className) {
- doshow();
- } else {
- chap.ontransitionend = doshow;
- chap.className = "chide";
- }
- return true;
- }
- }
- });
- } else {
- chapwrap.style.display = "";
- chap.ontransitionend = null;
- chap.className = "chide"
- }
- }
- if (cfg.pbDotStyle) {
- vidprogress2b.style.left = ((b / l) * vidprogress2.offsetWidth) + "px";
- vidprogress2c.style.left = ((a.getVideoBytesLoaded() / a.getVideoBytesTotal()) * vidprogress2.offsetWidth) + "px";
- } else {
- vidprogress2b.style.width = ((b / l) * vidprogress2.offsetWidth) + "px";
- vidprogress2c.style.width = ((a.getVideoBytesLoaded() / a.getVideoBytesTotal()) * vidprogress2.offsetWidth) + "px";
- }
- } else {
- if (window.curtime) curtime.textContent = "LIVE";
- if (cfg.pbDotStyle) {
- vidprogress2b.style.left = "100%";
- } else vidprogress2b.style.width = "100%";
- }
- } else throw 0;
- vidprogress2.style.display = cfg.pbAutohide && !a.classList.contains("ytp-autohide") ? "none" : "";
- vidprogress2.style.transform = cfg.pbPosBelow && (a = document.querySelector("ytd-watch-flexy,ytd-watch-grid")) && !a.attributes.fullscreen ? "translateY(100%)" : "";
- if (cfg.ptDocTitle) document.title = `${curq.textContent}${subs.textContent ? " " + subs.textContent : ""} ${curtime.textContent} ${orgTitle}`;
- } catch(a) {
- if (window.curtime) {
- if (cfg.ptDocTitle) document.title = "??? " + orgTitle;
- curtime.textContent = "???";
- }
- if (cfg.pbDotStyle) {
- vidprogress2b.style.left = "0px";
- vidprogress2c.style.left = "0px";
- } else {
- vidprogress2b.style.width = "0px";
- vidprogress2c.style.width = "0px";
- }
- if (window.chapwrap) {
- chapwrap.style.display = "";
- chap.ontransitionend = null;
- chap.className = "chide"
- }
- }
- }
-
- function resumeProgressMonitor() {
- if (timerProgressMonitor) return;
- // dispatchEvent(new Event("resize"));qwe
- 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() && (vplayer.id !== "c4-player") && (a = vplayer.parentNode.querySelector("video"))) {
- b = document.createElement("DIV");
- b.id = "vidprogress2";
- b.style.cssText = `position:absolute;z-index:50;bottom:0;width:100%;height:${
- cfg.pbDotStyle ? cfg.pbDotStyleSize : cfg.pbHeight}px;background:${cfg.pbColor}`;
- v = cfg.pbDotStyle ? "width:" + cfg.pbDotStyleSize + "px;margin-left:-" + Math.floor(cfg.pbDotStyleSize / 2) + "px;" : "";
- b.innerHTML = html(`<div id="vidprogress2c" style="position:absolute;${v}height:100%;background:${cfg.pbBufferColor}"></div>
- <div id="vidprogress2b" style="position:absolute;${v}height:100%;background:${cfg.pbElapsedColor}"></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?
- (v = vplayer.querySelector("video")).addEventListener("loadedmetadata", resumeProgressMonitor);
- v.addEventListener("play", resumeProgressMonitor);
- v.addEventListener("pause", 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);
- }
-
- function init() {
- orgTitle = document.title;
- processInfo();
- processPlayer();
- }
-
- function parseColor(c, e) {
- (e = document.createElement("DIV")).style.color = c;
- c = getComputedStyle(document.documentElement.appendChild(e)).color;
- e.remove();
- if (e = c.match(/(\d+), (\d+), (\d+)(?:, (\d+\.\d+))?/)) {
- return [("0" + parseInt(e[1]).toString(16).toUpperCase()).substr(-2) + ("0" + parseInt(e[2]).toString(16).toUpperCase()).substr(-2) +
- ("0" + parseInt(e[3]).toString(16).toUpperCase()).substr(-2), e[4] ? parseFloat(e[4]).toFixed(1) : "1"];
- }
- return ["000000", "1"];
- }
-
- function toRgba(rgb, opacity) {
- return rgb + ("0" + Math.floor(opacity * 255).toString(16)).substr(-2);
- }
-
- GM_registerMenuCommand("Configuration", (elePop, a) => {
- if (window.ayvpConfig) return;
- (elePop = document.createElement("DIV")).id = "ayvpConfig";
- elePop.innerHTML = html(`<style>
- #ayvpConfig{
- position:fixed;z-index:999999999;left:0;top:0;right:0;bottom:0;background:rgb(0,0,0,0.5);
- color:#000;font:normal normal normal 10pt/normal sans-serif;cursor:pointer
- }
- #ayvpPopup{position:fixed;top:.5em;right:.5em;border:.1em solid #000;border-radius:.5em;padding:.3em;background:#fff;cursor:auto}
- #ayvpCaption{border-radius:.3em;padding:.2em .3em;background:#444;color:#fff;font-weight:bold}
- #ayvpContent{margin-top:1em}
- #ayvpContent tr+tr td{padding-top:.5em}
- #ayvpContent td+td{padding-left:.5em}
- #ayvpContent .radio:last-child,
- #ayvpContent .radio:last-child{margin-left:1em}
- #ayvpContent .radio input{vertical-align:middle;margin-top:-.1em}
- #ayvpPbh,
- #ayvpPbds{margin-left:.5em;width:4em}
- #ayvpContent .preview{display:inline-block;margin-left:.5em;white-space:nowrap}
- #ayvpContent .preview .barContainer{
- display:inline-block;position:relative;vertical-align:bottom;width:70pt;height:13pt;overflow:hidden;background:#000;
- white-space:normal;word-break:break-all;color:#fff;font-size:7pt;line-height:1em;
- }
- #ayvpContent .preview .barBase{position:absolute;left:0;right:0;bottom:0}
- #ayvpContent .preview .barBase *{height:100%}
- #ayvpContent .preview .barBuf,
- #ayvpContent .preview .barElap{width:66%}
- #ayvpContent .hrgb{width:4em;text-transform:uppercase}
- #ayvpContent .picker{margin-right:1em;vertical-align:top;width:2em;height:1.5em;padding:0}
- #ayvpContent .opacity{width:3em}
- #ayvpContent #ayvpPslc{margin-left:1ex}
- #ayvpControls{margin:1em 0 .5em 0;text-align:center}
- #ayvpControls button{margin:0 2em;width:5em}
- #ayvpControls #ayvpExp{float:left;margin-left:.3em}
- #ayvpControls #ayvpImp{float:right;margin-right:.3em}
- #ayvpControls #ayvpImpFile{display:none}
- ${document.documentElement.attributes["dark"] ? configGuiStylesDark : ""}
- </style>
- <form id="ayvpPopup">
- <div id="ayvpCaption">YouTube Video Progress Configuration</div>
- <table id="ayvpContent">
- <tr>
- <td>Progressbar Auto Hide: <span title="Auto hide when YouTube video controls is visible">[?]</span></td>
- <td>
- <label class="radio"><input id="ayvpPbae" type="radio" name="ahide" value="0" /> Disabled</label>
- <label class="radio"><input id="ayvpPbad" type="radio" name="ahide" value="1" /> Enabled</label>
- </td>
- </tr>
- <tr>
- <td>Show Progressbar Below Video: <span title="Applies only when not in fullscreen mode">[?]</span></td>
- <td>
- <label class="radio"><input id="ayvpPbpi" type="radio" name="ppos" value="0" /> No</label>
- <label class="radio"><input id="ayvpPbpo" type="radio" name="ppos" value="1" /> Yes</label>
- </td>
- </tr>
- <tr>
- <td>Progressbar Style:</td>
- <td>
- <label class="radio"><input id="ayvpPbsl" type="radio" name="style" value="0" /> Line</label>
- <label class="radio"><input id="ayvpPbsd" type="radio" name="style" value="1" /> Dot</label>
- </td>
- </tr>
- <tr>
- <td>Progressbar Height:</td>
- <td>
- <input id="ayvpPbh" type="number" min="0" max="999" maxlength="3" value="${cfg.pbHeight}" required /> pixels
- <div class="preview">
- Preview:
- <div class="barContainer" title="Preview for line style progress only">
- .
- <div class="barBase" style="height:${cfg.pbHeight}px;background:${cfg.pbColor}">
- <div class="barBuf" style="background:${cfg.pbBufferColor}">
- <div class="barElap" style="background:${cfg.pbElapsedColor}"></div>
- </div>
- </div>
- </div>
- </div>
- </td>
- </tr>
- ${
- (() => {
- return [
- ["Background", "b", parseColor(cfg.pbColor), "Base"],
- ["Elapsed", "e", parseColor(cfg.pbElapsedColor), "Elap"],
- ["Buffer", "u", parseColor(cfg.pbBufferColor), "Buf"]
- ].map(t => {
- return `<tr>
- <td>Progressbar ${t[0]} Color:</td>
- <td bar="${t[3]}">#<input class="hrgb" maxlength="6" pattern="(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})" value="${t[2][0]}" required />
- <input class="picker" type="color" value="#${t[2][0]}" prevvalue="#${t[2][0]}" />
- Opacity: <input class="opacity" type="number" min="0.0" max="1.0" step="0.1" value="${t[2][1]}" required /> (0.0 - 1.0)</td>
- </tr>`
- }).join("");
- })()
- }
- <tr>
- <td>Progressbar Dot-style Size:</td>
- <td>
- <input id="ayvpPbds" type="number" min="0" max="999" maxlength="3" value="${cfg.pbDotStyleSize}" required /> pixels
- </td>
- </tr>
- <tr>
- <td>Progress Text: <span title="If disabled, chapter box will also be disabled">[?]</span></td>
- <td>
- <label class="radio"><input id="ayvpPtds" type="radio" name="ptext" value="0" /> Disabled</label>
- <label class="radio"><input id="ayvpPten" type="radio" name="ptext" value="1" /> Enabled</label>
- </td>
- </tr>
- <tr>
- <td>Progress Text In Document Title: <span title="Include progress text in page title (browser tab title)">[?]</span></td>
- <td>
- <label class="radio"><input id="ayvpPttd" type="radio" name="dtitle" value="0" /> Disabled</label>
- <label class="radio"><input id="ayvpPtte" type="radio" name="dtitle" value="1" /> Enabled</label>
- </td>
- </tr>
- <tr>
- <td>Preferred Subtitle Language:</td>
- <td>
- <select id="ayvpPslc"></select>
- </td>
- </tr>
- </table>
- <div id="ayvpControls">
- <button id="ayvpExp">Export</button>
- <button id="ayvpImp">Import</button><input id="ayvpImpFile" type="file" />
- <button id="ayvpOk" type="submit">OK</button>
- <button id="ayvpCancel">Cancel</button>
- </div>
- </form>`);
- elePop.querySelectorAll("input[name=ahide]")[cfg.pbAutohide ? 1 : 0].checked = true;
- elePop.querySelectorAll("input[name=ppos]")[cfg.pbPosBelow ? 1 : 0].checked = true;
- elePop.querySelectorAll("input[name=style]")[cfg.pbDotStyle ? 1 : 0].checked = true;
- elePop.querySelector(".barContainer").firstChild.data = "Sample content ".repeat(5);
- elePop.querySelectorAll("input[name=ptext]")[cfg.ptEnabled ? 1 : 0].checked = true;
- elePop.querySelectorAll("input[name=dtitle]")[cfg.ptDocTitle ? 1 : 0].checked = true;
- a = elePop.querySelector("#ayvpPslc");
- langList.forEach((l, e) => {
- (e = document.createElement("OPTION")).value = l[1];
- e.textContent = l[0];
- a.appendChild(e);
- });
- a.value = cfg.subLangCode;
- function applyConfig(a) {
- if (window.vidprogress) {
- if (window.vidprogress2) {
- vidprogress2.style.height = (cfg.pbDotStyle ? cfg.pbDotStyleSize : cfg.pbHeight) + "px";
- vidprogress2.style.background = cfg.pbColor;
- vidprogress2b.style.background = cfg.pbElapsedColor;
- vidprogress2c.style.background = cfg.pbBufferColor;
- if (cfg.pbDotStyle) {
- vidprogress2c.style.marginLeft = vidprogress2b.style.marginLeft = Math.floor(cfg.pbDotStyleSize / 2) + "px;";
- vidprogress2c.style.width = vidprogress2b.style.width = cfg.pbDotStyleSize + "px";
- } else {
- vidprogress2c.style.left = vidprogress2b.style.left = "";
- vidprogress2c.style.marginLeft = vidprogress2b.style.marginLeft = "";
- vidprogress2c.style.width = vidprogress2b.style.width = "";
- }
- }
- if (window.subs && subs.style.fontWeight) selectCaption();
- vidprogress.style.setProperty("display", cfg.ptEnabled ? "inline-block" : "none", "important");
- if (window.vidchap) vidchap.style.display = !cfg.ptEnabled ? "none" : "";
- }
- }
- function saveConfig(ev, a) {
- if (ev) ev.preventDefault();
- if (JSON.parse(GM_getValue("cfg", JSON.stringify(cfg))).ver > cfg.ver) {
- alert(`Can not save configuration changes.
- Current script instance is older than the installed script.
- Please reload the page to use the newer script then try again.`);
- elePop.remove();
- return;
- }
- cfg.pbAutohide = elePop.querySelector('input[name=ahide]:checked').value === "1";
- cfg.pbPosBelow = elePop.querySelector('input[name=ppos]:checked').value === "1";
- cfg.pbDotStyle = elePop.querySelector('input[name=style]:checked').value === "1";
- cfg.pbHeight = parseInt(ayvpPbh.value);
- [["pbColor", "Base"], ["pbElapsedColor", "Elap"], ["pbBufferColor", "Buf"]].forEach(v => {
- cfg[v[0]] = toRgba(elePop.querySelector(`td[bar=${v[1]}] .picker`).value, elePop.querySelector(`td[bar=${v[1]}] .opacity`).value);
- });
- cfg.pbDotStyleSize = parseInt(ayvpPbds.value);
- cfg.ptEnabled = elePop.querySelector('input[name=ptext]:checked').value === "1";
- if (!(cfg.ptDocTitle = elePop.querySelector('input[name=dtitle]:checked').value === "1")) document.title = orgTitle;
- cfg.subLangCode = ayvpPslc.value;
- GM_setValue("cfg", JSON.stringify(cfg));
- applyConfig();
- if (ev) elePop.remove();
- }
- elePop.lastElementChild.addEventListener("submit", saveConfig);
- elePop.addEventListener("keydown", ev => ev.stopPropagation(), true);
- elePop.addEventListener("input", (ev, e, b) => {
- if ((e = ev.target).id) { //height. line style
- if (e.id === "ayvpPbh") elePop.querySelector(".barBase").style.height = e.value + "px";
- } else { //colors
- b = elePop.querySelector(".bar" + e.parentNode.getAttribute("bar"));
- if (e.type === "color") { //picker
- b.style.background = e.value + ("0" + Math.floor(e.nextElementSibling.value * 255).toString(16)).substr(-2);
- } else if (e.type === "number") { //opacity
- b.style.background = e.previousElementSibling.value + ("0" + Math.floor(e.value * 255).toString(16)).substr(-2);
- } else { //hrgb
- e.nextElementSibling.value = "#" + e.value;
- b.style.background = e.nextElementSibling.value + ("0" + Math.floor(e.nextElementSibling.nextElementSibling.value * 255).toString(16)).substr(-2);
- }
- }
- });
- elePop.addEventListener("change", (ev, e, r) => {
- if ((e = ev.target).className === "picker") {
- if (e.value.substr(1).toUpperCase() !== e.getAttribute("prevvalue")) {
- e.setAttribute("prevvalue", e.previousElementSibling.value = e.value.substr(1).toUpperCase());
- elePop.querySelector(".bar" + e.parentNode.getAttribute("bar")).style.background =
- e.value + ("0" + Math.floor(e.nextElementSibling.value * 255).toString(16)).substr(-2);
- }
- } else if (e.id === "ayvpImpFile") {
- r = new FileReader();
- r.onload = (o, t, z) => {
- try {
- o = JSON.parse(r.result);
- if (!o.id === "ayvp") throw 0;
- if (o.ver > cfg.ver) {
- alert(`Can not import configuration changes.
- Current script instance is older than the installed script.
- Please reload the page to use the newer script then try again.`);
- return;
- }
- cfg = o;
- GM_setValue(cfg, r.result);
- applyConfig();
- elePop.remove();
- } catch(z) {
- alert(z.message ? "Invalid configuration file." : z);
- }
- };
- r.readAsText(e.files[0]);
- }
- });
- elePop.addEventListener("click", (ev, a) => {
- switch (ev.target.id) {
- case "ayvpExp":
- if (JSON.parse(GM_getValue("cfg", JSON.stringify(cfg))).ver > cfg.ver) {
- alert(`Can not export configuration changes.
- Current script instance is older than the installed script.
- Please reload the page to use the newer script then try again.`);
- elePop.remove();
- break;
- }
- saveConfig();
- JSON.stringify(cfg);
- document.body.appendChild(a = document.createElement("A")).href = URL.createObjectURL(new Blob([JSON.stringify(cfg)], {type: "application/json"}));
- a.download = `YouTubeVideoProgressbar.json`;
- a.click();
- a.remove();
- URL.revokeObjectURL(a.href);
- break;
- case "ayvpImp":
- ayvpImpFile.click();
- break;
- case "ayvpConfig":
- case "ayvpCancel":
- elePop.remove();
- break;
- default:
- return;
- }
- ev.preventDefault();
- });
- document.documentElement.append(elePop);
- });
-
- addEventListener("yt-page-data-updated", processInfo);
- addEventListener("yt-player-released", processPlayer);
- addEventListener("load", init);
- addEventListener("spfprocess", () => {
- setTimeout(init, contentLoadProcessDelay);
- });
-
- })(unsafeWindow);