-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.js
25 lines (23 loc) · 16.7 KB
/
content.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//
// DO NOT EDIT
// Generated by 'build.py'
//
(function() {
let scripts = new Map();
function injectAvailable() {
for (let [path, code] of Array.from(scripts)) {
if (!code) return;
let elem = document.createElement("script");
elem.innerHTML = code;
document.body.appendChild(elem);
console.log('[yt-magic] Injected:', elem);
scripts.delete(path);
}
}
scripts.set("src/util.js", `function UTIL(\u0024) {\u000A\u0009Object.assign(\u0024, {\u000A\u0009\u0009print: function(...args) {\u000A\u0009\u0009\u0009console.log(\u0022[yt\u002Dmagic]\u0022, ...args)\u003B\u000A\u0009\u0009},\u000A\u0009\u0009scalePx: function(str) {\u000A\u0009\u0009\u0009return parseInt(str.slice(0, \u002D2)) * \u0024.scaleFactor + \u0022px\u0022\u003B\u000A\u0009\u0009},\u000A\u0009\u0009prettyPercent: function(seek) {\u000A\u0009\u0009\u0009return (seek * 100).toFixed(2) + \u0022%\u0022\u003B\u000A\u0009\u0009},\u000A\u0009\u0009findNode: function(obj, testFn) {\u000A\u0009\u0009\u0009return rFindNode(obj, testFn, new Set(), [])\u003B\u000A\u0009\u0009}\u000A\u0009})\u003B\u000A\u000A\u0009function* rFindNode(obj, testFn, visited, path) {\u000A\u0009\u0009if (!obj || visited.has(obj)) return\u003B\u000A\u0009\u0009visited.add(obj)\u003B\u000A\u000A\u0009\u0009let entries\u003B\u000A\u0009\u0009if (Array.isArray(obj)) {\u000A\u0009\u0009\u0009entries \u003D arrayEntries(obj)\u003B\u000A\u0009\u0009} else if (typeof obj \u003D\u003D\u003D \u0022object\u0022) {\u000A\u0009\u0009\u0009entries \u003D objectEntries(Object(obj))\u003B\u000A\u0009\u0009} else {\u000A\u0009\u0009\u0009return\u003B\u000A\u0009\u0009}\u000A\u000A\u0009\u0009for (let [key, value] of entries) {\u000A\u0009\u0009\u0009let subPath \u003D [...path, key]\u003B\u000A\u0009\u0009\u0009if (testFn(key, value)) {\u000A\u0009\u0009\u0009\u0009yield { key, value, path: subPath }\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009\u0009let nodes \u003D rFindNode(value, testFn, visited, subPath)\u003B\u000A\u0009\u0009\u0009if (nodes) {\u000A\u0009\u0009\u0009\u0009for (let node of nodes) {\u000A\u0009\u0009\u0009\u0009\u0009yield node\u003B\u000A\u0009\u0009\u0009\u0009}\u000A\u0009\u0009\u0009}\u000A\u0009\u0009}\u000A\u0009}\u000A\u000A\u0009function* objectEntries(obj) {\u000A\u0009\u0009for (let prop in obj) {\u000A\u0009\u0009\u0009if (!Object.prototype.hasOwnProperty.call(obj, prop)) continue\u003B\u000A\u0009\u0009\u0009yield [prop, obj[prop]]\u003B\u000A\u0009\u0009}\u000A\u0009}\u000A\u000A\u0009function* arrayEntries(arr) {\u000A\u0009\u0009for (let i \u003D 0\u003B i \u003C arr.length\u003B i++) {\u000A\u0009\u0009\u0009yield [i, arr[i]]\u003B\u000A\u0009\u0009}\u000A\u0009}\u000A\u000A\u0009return \u0024\u003B\u000A}\u000A`);
scripts.set("src/main.js", `function MAIN(\u0024) {\u000A\u0009const tasks \u003D [\u000A\u0009\u0009{ loop: \u0024.seekLoop, delayMs: 500 },\u000A\u0009\u0009{ loop: \u0024.resizeLoop, delayMs: 100 }\u000A\u0009]\u003B\u000A\u0009const minDelayMs \u003D 100\u003B\u000A\u000A\u0009let loops \u003D []\u003B\u000A\u0009let n \u003D 1\u003B\u000A\u0009let max \u003D Math.floor(Math.max(...tasks.map(it \u003D\u003E it.delayMs)) / minDelayMs)\u003B\u000A\u0009let href \u003D window.location.href\u003B\u000A\u000A\u0009init()\u003B\u000A\u0009main()\u003B\u000A\u000A\u0009function init() {\u000A\u0009\u0009loops \u003D []\u003B\u000A\u0009\u0009for (let { loop, delayMs } of tasks) {\u000A\u0009\u0009\u0009let { init, main } \u003D loop\u003B\u000A\u0009\u0009\u0009loops.push({\u000A\u0009\u0009\u0009\u0009init,\u000A\u0009\u0009\u0009\u0009main,\u000A\u0009\u0009\u0009\u0009initDone: false,\u000A\u0009\u0009\u0009\u0009execAt: Math.floor(delayMs / minDelayMs)\u000A\u0009\u0009\u0009})\u003B\u000A\u0009\u0009}\u000A\u0009}\u000A\u000A\u0009function main() {\u000A\u0009\u0009let newHref \u003D window.location.href\u003B\u000A\u0009\u0009if (newHref !\u003D\u003D href) {\u000A\u0009\u0009\u0009init()\u003B\u000A\u0009\u0009\u0009href \u003D newHref\u003B\u000A\u0009\u0009}\u000A\u0009\u0009for (let loop of loops) {\u000A\u0009\u0009\u0009if (!loop.initDone) {\u000A\u0009\u0009\u0009\u0009try {\u000A\u0009\u0009\u0009\u0009\u0009loop.init()\u003B\u000A\u0009\u0009\u0009\u0009} catch (e) {\u000A\u0009\u0009\u0009\u0009\u0009\u0024.print(e)\u003B\u000A\u0009\u0009\u0009\u0009\u0009continue\u003B\u000A\u0009\u0009\u0009\u0009}\u000A\u0009\u0009\u0009\u0009loop.initDone \u003D true\u003B\u000A\u0009\u0009\u0009\u0009loop.main()\u003B\u000A\u0009\u0009\u0009\u0009continue\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009\u0009if (n % loop.execAt) continue\u003B\u000A\u0009\u0009\u0009loop.main()\u003B\u000A\u0009\u0009}\u000A\u0009\u0009n \u003D n \u003D\u003D\u003D max ? 1 : n + 1\u003B\u000A\u0009\u0009setTimeout(main, minDelayMs)\u003B\u000A\u0009}\u000A}\u000A`);
scripts.set("src/seek.js", `function SEEK(\u0024) {\u000A\u0009let progressBar, seekKey, videoLenCache, videoPosCache\u003B\u000A\u000A\u0009\u0024.seekLoop \u003D {\u000A\u0009\u0009init: function() {\u000A\u0009\u0009\u0009videoLenCache \u003D null\u003B\u000A\u0009\u0009\u0009videoPosCache \u003D null\u003B\u000A\u0009\u0009\u0009progressBar \u003D document.getElementsByClassName(\u0022ytp\u002Dprogress\u002Dbar\u0022)[0]\u003B\u000A\u0009\u0009\u0009if (!progressBar) {\u000A\u0009\u0009\u0009\u0009throw Error(\u0022Progress bar not loaded yet!\u0022)\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009},\u000A\u0009\u0009main: function() {\u000A\u0009\u0009\u0009let videoId \u003D getCurrentVideoId()\u003B\u000A\u0009\u0009\u0009if (!videoId) {\u000A\u0009\u0009\u0009\u0009seekKey \u003D null\u003B\u000A\u0009\u0009\u0009\u0009return null\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009\u0009let newSeekKey \u003D \u0022yt\u002Dmagic/\u0022 + videoId\u003B\u000A\u000A\u0009\u0009\u0009if (newSeekKey \u003D\u003D\u003D seekKey) {\u000A\u0009\u0009\u0009\u0009try {\u000A\u0009\u0009\u0009\u0009\u0009saveSeekPos()\u003B\u000A\u0009\u0009\u0009\u0009} catch (e) {\u000A\u0009\u0009\u0009\u0009\u0009\u0024.print(e)\u003B\u000A\u0009\u0009\u0009\u0009}\u000A\u0009\u0009\u0009\u0009return\u003B\u000A\u0009\u0009\u0009}\u000A\u000A\u0009\u0009\u0009let seekValue \u003D localStorage.getItem(newSeekKey)\u003B\u000A\u0009\u0009\u0009if (seekValue) {\u000A\u0009\u0009\u0009\u0009try {\u000A\u0009\u0009\u0009\u0009\u0009showSeekDialogIfRequired(seekValue)\u003B\u000A\u0009\u0009\u0009\u0009} catch (e) {\u000A\u0009\u0009\u0009\u0009\u0009\u0024.print(e)\u003B\u000A\u0009\u0009\u0009\u0009\u0009return\u003B\u000A\u0009\u0009\u0009\u0009}\u000A\u0009\u0009\u0009}\u000A\u000A\u0009\u0009\u0009seekKey \u003D newSeekKey\u003B\u000A\u0009\u0009}\u000A\u0009}\u003B\u000A\u000A\u0009function showSeekDialogIfRequired(seekValue) {\u000A\u0009\u0009let curSec \u003D getCurrentVideoPosSec()\u003B\u000A\u0009\u0009let savedSec \u003D getCurrentVideoDurationSec() * seekValue\u003B\u000A\u000A\u0009\u0009if (savedSec \u002D 2.5 \u003C\u003D curSec \u0026\u0026 curSec \u003C\u003D savedSec + 2.5) {\u000A\u0009\u0009\u0009\u0024.print(\u000A\u0009\u0009\u0009\u0009\u0022Not showing seek dialog, because video is already at a good position:\u0022,\u000A\u0009\u0009\u0009\u0009curSec,\u000A\u0009\u0009\u0009\u0009\u0022vs\u0022,\u000A\u0009\u0009\u0009\u0009savedSec\u000A\u0009\u0009\u0009)\u003B\u000A\u0009\u0009\u0009return\u003B\u000A\u0009\u0009}\u000A\u000A\u0009\u0009let duration \u003D Math.floor(savedSec / 60) + \u0022:\u0022 + Math.floor(savedSec % 60)\u003B\u000A\u000A\u0009\u0009document.getElementById(\u0022movie_player\u0022).insertAdjacentHTML(\u000A\u0009\u0009\u0009\u0022beforeend\u0022,\u000A\u0009\u0009\u0009\u0060\u003Cstyle\u003E\u000A\u0009\u0009\u0009#yt\u002Dmagic\u002Dconfirm\u002Ddialog {\u000A\u0009\u0009\u0009\u0009z\u002Dindex: 999\u003B \u000A\u0009\u0009\u0009\u0009position: absolute\u003B\u000A\u0009\u0009\u0009\u0009text\u002Dalign: center\u003B\u000A\u0009\u0009\u0009\u0009left: 50%\u003B\u000A\u0009\u0009\u0009\u0009transform: translate(\u002D50%, 20%)\u003B \u000A\u0009\u0009\u0009\u0009padding: 10px\u003B\u000A\u0009\u0009\u0009\u0009background: #424242\u003B\u000A\u0009\u0009\u0009\u0009border\u002Dradius: 10px\u003B\u000A\u0009\u0009\u0009\u0009box\u002Dshadow: 5px 10px\u003B\u000A\u0009\u0009\u0009\u0009font\u002Dsize: 20pt\u003B\u000A\u0009\u0009\u0009\u0009font\u002Dfamily: sans\u002Dserif\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009\u0009#yt\u002Dmagic\u002Dconfirm\u002Ddialog button { \u000A\u0009\u0009\u0009\u0009font\u002Dsize: 18pt\u003B\u000A\u0009\u0009\u0009\u0009padding: 2px 10px\u003B\u000A\u0009\u0009\u0009\u0009margin: 10px 5px 0 5px \u003B\u000A\u0009\u0009\u0009\u0009border\u002Dradius: 10px\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009\u003C/style\u003E\u000A\u0009\u0009\u003Cdiv data\u002Dlayer\u003D9 id\u003Dyt\u002Dmagic\u002Dconfirm\u002Ddialog\u003E\u000A\u0009\u0009\u0009\u003Cdiv\u003EHey! Would you like resume @ \u0024{duration}?\u003C/div\u003E\u000A\u0009\u0009\u0009\u003Cdiv style\u003D\u0027font\u002Dsize: 10pt\u003B\u0027\u003E(click elsewhere to hide me)\u003C/div\u003E\u000A\u0009\u0009\u0009\u003Cbutton style\u003D\u0022background\u002Dcolor: #ffab91\u003B\u0022 id\u003Dyt\u002Dmagic\u002Dconfirm\u002Dseek\u002Dno\u003E\u000A\u0009\u0009\u0009\u0009Nope\u000A\u0009\u0009\u0009\u003C/button\u003E\u000A\u0009\u0009\u0009\u003Cbutton style\u003D\u0022background\u002Dcolor: #80deea\u003B\u0022 id\u003Dyt\u002Dmagic\u002Dconfirm\u002Dseek\u002Dyes\u003E\u000A\u0009\u0009\u0009\u0009Sure\u000A\u0009\u0009\u0009\u003C/button\u003E\u000A\u0009\u0009\u003C/div\u003E\u0060\u000A\u0009\u0009)\u003B\u000A\u000A\u0009\u0009\u0024.print(\u0022Showed seek dialog @\u0022, \u0024.prettyPercent(seekValue))\u003B\u000A\u000A\u0009\u0009let confirmDialog \u003D document.getElementById(\u0022yt\u002Dmagic\u002Dconfirm\u002Ddialog\u0022)\u003B\u000A\u0009\u0009let confirmNo \u003D document.getElementById(\u0022yt\u002Dmagic\u002Dconfirm\u002Dseek\u002Dno\u0022)\u003B\u000A\u0009\u0009let confirmYes \u003D document.getElementById(\u0022yt\u002Dmagic\u002Dconfirm\u002Dseek\u002Dyes\u0022)\u003B\u000A\u000A\u0009\u0009let events \u003D [\u000A\u0009\u0009\u0009[window, \u0022popstate\u0022, no],\u000A\u0009\u0009\u0009[confirmNo, \u0022click\u0022, no],\u000A\u0009\u0009\u0009[confirmYes, \u0022click\u0022, yes],\u000A\u0009\u0009\u0009[window, \u0022click\u0022, onClickOutside],\u000A\u0009\u0009\u0009[document, \u0022keydown\u0022, anyKeyPress]\u000A\u0009\u0009]\u003B\u000A\u000A\u0009\u0009for (let event of events) {\u000A\u0009\u0009\u0009event[0].addEventListener(event[1], event[2])\u003B\u000A\u0009\u0009}\u000A\u000A\u0009\u0009function no() {\u000A\u0009\u0009\u0009confirmDialog.remove()\u003B\u000A\u0009\u0009\u0009for (let event of events) {\u000A\u0009\u0009\u0009\u0009event[0].removeEventListener(event[1], event[2])\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009}\u000A\u000A\u0009\u0009function yes() {\u000A\u0009\u0009\u0009doSeek(seekValue)\u003B\u000A\u0009\u0009\u0009no()\u003B\u000A\u0009\u0009}\u000A\u000A\u0009\u0009function onClickOutside(e) {\u000A\u0009\u0009\u0009if (confirmDialog.contains(e.target)) return\u003B\u000A\u0009\u0009\u0009no()\u003B\u000A\u0009\u0009}\u000A\u000A\u0009\u0009function anyKeyPress(e) {\u000A\u0009\u0009\u0009if (e.code \u003D\u003D\u003D \u0022Enter\u0022 || e.code \u003D\u003D\u003D \u0022NumpadEnter\u0022) {\u000A\u0009\u0009\u0009\u0009yes()\u003B\u000A\u0009\u0009\u0009} else if (e.code \u003D\u003D\u003D \u0022Escape\u0022) {\u000A\u0009\u0009\u0009\u0009no()\u003B\u000A\u0009\u0009\u0009}\u000A\u0009\u0009}\u000A\u0009}\u000A\u000A\u0009function doSeek(seekValue) {\u000A\u0009\u0009let mouseEventInit\u003B\u000A\u000A\u0009\u0009let rect \u003D progressBar.getBoundingClientRect()\u003B\u000A\u0009\u0009mouseEventInit \u003D {\u000A\u0009\u0009\u0009view: window,\u000A\u0009\u0009\u0009bubbles: true,\u000A\u0009\u0009\u0009cancelable: false,\u000A\u0009\u0009\u0009clientX: rect.x + seekValue * progressBar.offsetWidth,\u000A\u0009\u0009\u0009clientY: rect.y\u000A\u0009\u0009}\u003B\u000A\u000A\u0009\u0009for (let evtType of [\u0022mousedown\u0022, \u0022mouseup\u0022]) {\u000A\u0009\u0009\u0009progressBar.dispatchEvent(new MouseEvent(evtType, mouseEventInit))\u003B\u000A\u0009\u0009}\u000A\u0009\u0009\u0024.print(\u0022Did seek:\u0022, \u0024.prettyPercent(seekValue))\u003B\u000A\u0009}\u000A\u000A\u0009function saveSeekPos() {\u000A\u0009\u0009let seekValue\u003B\u000A\u0009\u0009try {\u000A\u0009\u0009\u0009seekValue \u003D getCurrentSeekValue()\u003B\u000A\u0009\u0009} catch (e) {\u000A\u0009\u0009\u0009\u0024.print(e)\u003B\u000A\u0009\u0009}\u000A\u0009\u0009if (!seekValue || seekValue \u003C\u003D 0 || seekValue \u003E\u003D 1) {\u000A\u0009\u0009\u0009return\u003B\u000A\u0009\u0009}\u000A\u0009\u0009localStorage.setItem(seekKey, seekValue)\u003B\u000A\u0009\u0009// \u0024.print(\u0022Save seek position:\u0022, \u0024.prettyPercent(seekValue), \u0022for\u0022, seekKey)\u003B\u000A\u0009}\u000A\u000A\u0009function getCurrentVideoId() {\u000A\u0009\u0009return new URLSearchParams(window.location.search).get(\u0022v\u0022)\u003B\u000A\u0009}\u000A\u000A\u0009function getCurrentSeekValue() {\u000A\u0009\u0009return getCurrentVideoPosSec() / getCurrentVideoDurationSec()\u003B\u000A\u0009}\u000A\u000A\u0009function getCurrentVideoPosSec() {\u000A\u0009\u0009if (!videoPosCache || Date.now() \u002D videoPosCache.timestamp \u003E 3000) {\u000A\u0009\u0009\u0009videoPosCache \u003D \u0024.findNode(\u000A\u0009\u0009\u0009\u0009\u0024.ytPlayer,\u000A\u0009\u0009\u0009\u0009(k, v) \u003D\u003E v \u0026\u0026 v.constructor \u0026\u0026 v.constructor.name \u003D\u003D\u003D \u0022Sea\u0022\u000A\u0009\u0009\u0009).next().value\u003B\u000A\u0009\u0009\u0009videoPosCache.timestamp \u003D Date.now()\u003B\u000A\u0009\u0009}\u000A\u0009\u0009let values \u003D Object.values(videoPosCache.value).filter(\u000A\u0009\u0009\u0009v \u003D\u003E typeof v \u003D\u003D\u003D \u0022number\u0022\u000A\u0009\u0009)\u003B\u000A\u0009\u0009let min \u003D Math.min(...values)\u003B\u000A\u0009\u0009if (!min) {\u000A\u0009\u0009\u0009throw Error(\u0022Current video position not found!\u0022)\u003B\u000A\u0009\u0009}\u000A\u0009\u0009return min\u003B\u000A\u0009}\u000A\u000A\u0009function getCurrentVideoDurationSec() {\u000A\u0009\u0009if (!videoLenCache || Date.now() \u002D videoLenCache.timestamp \u003E 5000) {\u000A\u0009\u0009\u0009videoLenCache \u003D \u0024.findNode(\u000A\u0009\u0009\u0009\u0009\u0024.ytPlayer,\u000A\u0009\u0009\u0009\u0009(k, v) \u003D\u003E v \u0026\u0026 typeof v \u003D\u003D\u003D \u0022number\u0022 \u0026\u0026 k \u003D\u003D\u003D \u0022lengthSeconds\u0022\u000A\u0009\u0009\u0009).next().value\u003B\u000A\u0009\u0009\u0009videoLenCache.timestamp \u003D Date.now()\u003B\u000A\u0009\u0009}\u000A\u0009\u0009return videoLenCache.value\u003B\u000A\u0009}\u000A\u000A\u0009return \u0024\u003B\u000A}\u000A`);
scripts.set("src/resize.js", `function RESIZE(\u0024) {\u000A\u0009let origTooltip, customTooltip, container, bg\u003B\u000A\u000A\u0009\u0024.resizeLoop \u003D {\u000A\u0009\u0009init: function() {\u000A\u0009\u0009\u0009if (origTooltip) return\u003B\u000A\u0009\u0009\u0009origTooltip \u003D document.getElementsByClassName(\u0022ytp\u002Dtooltip\u002Dbg\u0022)[0]\u003B\u000A\u000A\u0009\u0009\u0009customTooltip \u003D origTooltip.cloneNode()\u003B\u000A\u0009\u0009\u0009origTooltip.classList.add(\u0022display\u002Dnone\u0022)\u003B\u000A\u000A\u0009\u0009\u0009container \u003D origTooltip.parentNode\u003B\u000A\u0009\u0009\u0009container.insertBefore(customTooltip, origTooltip.nextSibling)\u003B\u000A\u0009\u0009},\u000A\u0009\u0009main: function() {\u000A\u0009\u0009\u0009if (origTooltip.style.display \u003D\u003D\u003D \u0022none\u0022) return\u003B\u000A\u000A\u0009\u0009\u0009let nextBg \u003D origTooltip.style.background\u003B\u000A\u0009\u0009\u0009if (!nextBg || bg \u003D\u003D\u003D nextBg) return\u003B\u000A\u000A\u0009\u0009\u0009let parts \u003D nextBg.split(\u0022 \u0022)\u003B\u000A\u0009\u0009\u0009for (let i of [1, 2, 4, 5]) {\u000A\u0009\u0009\u0009\u0009parts[i] \u003D \u0024.scalePx(parts[i])\u003B\u000A\u0009\u0009\u0009}\u000A\u000A\u0009\u0009\u0009container.style.height \u003D \u0024.scalePx(origTooltip.style.height)\u003B\u000A\u0009\u0009\u0009container.style.width \u003D \u0024.scalePx(origTooltip.style.width)\u003B\u000A\u0009\u0009\u0009customTooltip.style.background \u003D parts.join(\u0022 \u0022)\u003B\u000A\u0009\u0009\u0009// \u0024.print(\u0060Made preview \u0024{\u0024.scaleFactor}x larger\u0060)\u003B\u000A\u000A\u0009\u0009\u0009bg \u003D nextBg\u003B\u000A\u0009\u0009}\u000A\u0009}\u003B\u000A\u000A\u0009return \u0024\u003B\u000A}\u000A`);
scripts.set("src/index.js", `const YT_TRICKS \u003D {\u000A\u0009scaleFactor: 4,\u000A\u0009ytPlayer: _yt_player\u000A}\u003B\u000AMAIN(RESIZE(SEEK(UTIL(YT_TRICKS))))\u003B\u000A`);
injectAvailable();
})();