/** * @author Qwerty * * @name Enable 2/5/10 sec rewind (HUD) * * @description * This script allows you to precisely and more granularly rewind and fast forward videos on any website.\ * It also displays a HUD showing the total saved time and time spent seeking and buffering. * * - Use the `<` and `>` keys to rewind and fast forward by 5 seconds, respectively. * - Use the **Shift** or **RightAlt** modifier keys to skip by 10 and 2 seconds, respectively. * - When the video is **paused**, it will seek frame by frame instead at 30 fps *(configurable)* and double or half the speed with the modifier keys. * * @description * submit issues here: https://gist.github.com/ackvf/b180e9883069ad753969a30cb2622787 */ ((_ = window.q_skipCount = { /* change these values in devTools `q_skipCount.<...>` to your liking */ videoSelector: 'video', /* configure the querySelector for the video element */ expectedFramerate: 30, /* configure your video's framerate */ timeout: 2000, /* HUD automatically hides after this time. Requires restart. */ /* --- */ skippedTime: 0, spentLoading: 0, el: null, video: null, timeoutRef: null, handleKey(ev) { _.video = document.querySelector(_.videoSelector) ?? document.querySelector('video'); let d = 0; let f = _.expectedFramerate; switch (ev.key) { /* < > keys */ case ",": d = -5; break; case ",": d = -5; break; case ".": d = +5; break; /* Shift + < > keys */ case "?": d = -10; f /= 2; break; case ":": d = +10; f /= 2; break; /* RightAlt + < > keys */ case "<": d = -2; f *= 2; break; case ">": d = +2; f *= 2; break; case " ": togglePlayback(); break; } if (_.video && d) { /* framestep seek if video is paused */ if (_.video.paused) _.video.currentTime += Math.sign(d) / f; else { _.video.currentTime += d; _.skippedTime += d; _.lastDelta = d; const bufferingStartTime = performance.now(); _.video.addEventListener('canplaythrough', () => { _.spentLoading += performance.now() - bufferingStartTime; _.renderHUD(); }, { once: true }); } } if (!_.video || _.video !== document.querySelector(_.videoSelector)) console.warn(`Video ${_.videoSelector} not found`, { video: _.video }); }, renderHUD() { if (_.timeoutRef) _.removeHUD(); const ID = "skipCountContainer"; _.el ??= _.createHUD(ID, _.timeout); _.el.querySelector('#theSeek-m').textContent = ~~(_.skippedTime / 60); _.el.querySelector('#theSeek-s').textContent = _.skippedTime % 60; _.el.querySelector('#spentLoading').textContent = ~~(_.spentLoading / 100) / 10; _.el.classList.add(_.lastDelta > 0 ? 'positive' : 'negative'); setTimeout(() => _.el.classList.remove('positive', 'negative')); _.timeoutRef = setTimeout(_.removeHUD, _.timeout); document.body.insertAdjacentElement('afterbegin', _.el); }, removeHUD() { clearTimeout(_.timeoutRef); _.timeoutRef = null; _.el.remove(); _.el.classList.remove('positive', 'negative'); }, createHUD(ID, timeout) { const hud = document.createElement('div'); hud.id = ID; hud.className = `default transition`; const style = document.createElement('style'); style.textContent = ` #${ID} { position: absolute; z-index: 10000; left: 5px; top: 5px; font-size: 32px; padding: 5px 13px; opacity: 0.6; backdrop-filter: blur(5px) sepia(1); } #${ID}.default { border: 1px solid slategray; color: slategray; opacity: 0; box-shadow: inset 0 0 11px -1px slategray; } #${ID}.positive { border-color: dodgerblue; color: dodgerblue; opacity: 0.7; box-shadow: inset 0 0 11px -1px dodgerblue; } #${ID}.negative { border-color: tomato; color: tomato; opacity: 0.7; box-shadow: inset 0 0 11px -1px tomato; } #${ID}.transition { transition: all 800ms ease-out, opacity ${timeout}ms ease-in; } #${ID} #theSeek-m::after { content: 'm'; } #${ID} #theSeek-s::after, #${ID} #spentLoading::after { content: 's'; } `; hud.appendChild(style); const savedText = document.createTextNode('saved'); hud.appendChild(savedText); hud.appendChild(document.createElement('br')); const spanM = document.createElement('span'); spanM.id = 'theSeek-m'; hud.appendChild(spanM); const spanS = document.createElement('span'); spanS.id = 'theSeek-s'; hud.appendChild(spanS); hud.appendChild(document.createElement('br')); const spanL = document.createElement('span'); spanL.id = 'spentLoading'; hud.appendChild(spanL); return hud; }, }) => document.onkeypress = _.handleKey)(/* window.q_skipCount */);