Last active
April 24, 2026 01:22
-
-
Save affan2021shaikh/efa8b9f8c31b794d2d3b2a1b99928e0f to your computer and use it in GitHub Desktop.
Revisions
-
affan2021shaikh revised this gist
Apr 24, 2026 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ // @version 3.0.0 // @description Best-effort background play for YouTube desktop and mobile web using only standard userscript features. // @author ChatGPT 5.3 Thinking // @downloadURL https://gist.github.com/affan2021shaikh/efa8b9f8c31b794d2d3b2a1b99928e0f/raw/YouTube%2520Background%2520Play%2520Max.user.js // @match https://www.youtube.com/* // @match https://m.youtube.com/* // @run-at document-start -
affan2021shaikh revised this gist
Apr 24, 2026 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ // @version 1.0 // @description Use DarkReader in a cross-manager compatible way // @author ChatGPT 5.3 // @downloadURL https://gist.github.com/affan2021shaikh/efa8b9f8c31b794d2d3b2a1b99928e0f/raw/DarkReader%2520ES%2520Module%2520Equivalent.user.js // @match *://*/* // @grant none // @require https://unpkg.com/darkreader/darkreader.min.js -
affan2021shaikh revised this gist
Apr 24, 2026 . 3 changed files with 49 additions and 485 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,484 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,46 @@ // ==UserScript== // @name DarkReader ES Module Equivalent // @namespace affan2021shaikh // @version 1.0 // @description Use DarkReader in a cross-manager compatible way // @author ChatGPT 5.3 // @downloadURL test // @match *://*/* // @grant none // @require https://unpkg.com/darkreader/darkreader.min.js // ==/UserScript== (function () { 'use strict'; // DarkReader is exposed globally const { enable: enableDarkMode, disable: disableDarkMode, auto: followSystemColorScheme, exportGeneratedCSS: collectCSS, isEnabled: isDarkReaderEnabled } = DarkReader; // Enable dark mode enableDarkMode({ brightness: 100, contrast: 90, sepia: 10, }); // Disable (example call) disableDarkMode(); // Follow system scheme followSystemColorScheme(); // Async call wrapped properly (no top-level await) (async () => { const CSS = await collectCSS(); console.log('Generated CSS:', CSS); const isEnabled = isDarkReaderEnabled(); console.log('Is enabled:', isEnabled); })(); })(); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,10 @@ // ==UserScript== // @name YouTube Background Play Max // @namespace affan2021shaikh // @version 3.0.0 // @description Best-effort background play for YouTube desktop and mobile web using only standard userscript features. // @author ChatGPT 5.3 Thinking // @downloadURL test // @match https://www.youtube.com/* // @match https://m.youtube.com/* // @run-at document-start -
affan2021shaikh revised this gist
Apr 17, 2026 . 2 changed files with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ // ==UserScript== // @name Dark Reader Everywhere // @namespace https://affan2021shaikh/ // @version 2.0 // @description Apply Dark Reader to every webpage with a small built-in control panel. // @match *://*/* This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1 +0,0 @@ -
affan2021shaikh revised this gist
Apr 17, 2026 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ hello -
affan2021shaikh revised this gist
Apr 17, 2026 . 1 changed file with 484 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,484 @@ // ==UserScript== // @name Dark Reader Everywhere // @namespace https://example.com/ // @version 2.0 // @description Apply Dark Reader to every webpage with a small built-in control panel. // @match *://*/* // @run-at document-start // @noframes // @grant none // ==/UserScript== (() => { 'use strict'; const STORAGE_KEY = 'dr_everywhere_settings_v2'; const PANEL_ID = 'dr-everywhere-panel'; const BUTTON_ID = 'dr-everywhere-button'; const STYLE_ID = 'dr-everywhere-style'; const LIB_URLS = [ 'https://cdn.jsdelivr.net/npm/darkreader@latest/darkreader.min.js', 'https://unpkg.com/darkreader@latest/darkreader.min.js' ]; const DEFAULTS = { enabled: true, mode: 'enable', // "enable" or "auto" brightness: 100, contrast: 90, sepia: 10 }; let settings = loadSettings(); let libLoadPromise = null; let panelOpen = false; function loadSettings() { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return { ...DEFAULTS }; const parsed = JSON.parse(raw); return { ...DEFAULTS, ...parsed, enabled: !!parsed.enabled, mode: parsed.mode === 'auto' ? 'auto' : 'enable', brightness: clampNumber(parsed.brightness, 0, 200, DEFAULTS.brightness), contrast: clampNumber(parsed.contrast, 0, 200, DEFAULTS.contrast), sepia: clampNumber(parsed.sepia, 0, 100, DEFAULTS.sepia) }; } catch { return { ...DEFAULTS }; } } function saveSettings() { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); } function clampNumber(value, min, max, fallback) { const n = Number(value); if (!Number.isFinite(n)) return fallback; return Math.min(max, Math.max(min, n)); } function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function loadDarkReader() { if (window.DarkReader) return window.DarkReader; if (libLoadPromise) return libLoadPromise; libLoadPromise = new Promise((resolve, reject) => { let index = 0; const tryNext = () => { if (window.DarkReader) { resolve(window.DarkReader); return; } if (index >= LIB_URLS.length) { reject(new Error('Failed to load Dark Reader from CDN.')); return; } const src = LIB_URLS[index++]; const script = document.createElement('script'); script.src = src; script.async = true; script.referrerPolicy = 'no-referrer'; script.onload = () => { if (window.DarkReader) resolve(window.DarkReader); else tryNext(); }; script.onerror = () => { script.remove(); tryNext(); }; (document.head || document.documentElement).appendChild(script); }; tryNext(); }); return libLoadPromise; } async function applyDarkReader() { try { const DarkReader = await loadDarkReader(); const config = { brightness: settings.brightness, contrast: settings.contrast, sepia: settings.sepia }; if (!settings.enabled) { DarkReader.disable(); } else if (settings.mode === 'auto') { DarkReader.auto(config); } else { DarkReader.enable(config); } updateStatusText(); updateButtonText(); } catch (err) { console.warn('[Dark Reader Everywhere] Could not load Dark Reader:', err); updateStatusText('Load failed'); updateButtonText(); } } function setEnabled(value) { settings.enabled = !!value; saveSettings(); applyDarkReader(); syncPanelFields(); } function setMode(value) { settings.mode = value === 'auto' ? 'auto' : 'enable'; saveSettings(); applyDarkReader(); syncPanelFields(); } function setSetting(name, value) { settings[name] = value; saveSettings(); applyDarkReader(); syncPanelFields(); } function ensureStyles() { if (document.getElementById(STYLE_ID)) return; const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` #${BUTTON_ID} { position: fixed; right: 14px; bottom: 14px; z-index: 2147483647; border: 1px solid rgba(127,127,127,.35); border-radius: 999px; padding: 8px 12px; font: 13px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; cursor: pointer; background: rgba(20,20,20,.88); color: #fff; box-shadow: 0 6px 24px rgba(0,0,0,.25); user-select: none; } #${PANEL_ID} { position: fixed; right: 14px; bottom: 56px; z-index: 2147483647; width: 300px; max-width: calc(100vw - 28px); background: rgba(20,20,20,.96); color: #fff; border: 1px solid rgba(127,127,127,.35); border-radius: 16px; box-shadow: 0 14px 40px rgba(0,0,0,.35); padding: 14px; font: 13px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; } #${PANEL_ID} * { box-sizing: border-box; } #${PANEL_ID} .dr-row { margin: 10px 0; } #${PANEL_ID} .dr-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-weight: 700; font-size: 14px; margin-bottom: 10px; } #${PANEL_ID} .dr-close { border: 0; border-radius: 8px; padding: 4px 8px; cursor: pointer; background: rgba(255,255,255,.12); color: #fff; } #${PANEL_ID} label { display: block; margin-bottom: 5px; opacity: .9; } #${PANEL_ID} input[type="range"] { width: 100%; } #${PANEL_ID} select, #${PANEL_ID} input[type="checkbox"] { vertical-align: middle; } #${PANEL_ID} .dr-inline { display: flex; align-items: center; justify-content: space-between; gap: 10px; } #${PANEL_ID} .dr-small { opacity: .8; font-size: 12px; } #${PANEL_ID} .dr-actions { display: flex; gap: 8px; margin-top: 12px; } #${PANEL_ID} button { border: 0; border-radius: 10px; padding: 8px 10px; cursor: pointer; background: rgba(255,255,255,.12); color: #fff; } #${PANEL_ID} .dr-spacer { height: 1px; background: rgba(255,255,255,.1); margin: 10px 0; } #${PANEL_ID} .dr-value { font-variant-numeric: tabular-nums; opacity: .9; } `; (document.head || document.documentElement).appendChild(style); } function createUI() { if (document.getElementById(BUTTON_ID)) return; ensureStyles(); const button = document.createElement('button'); button.id = BUTTON_ID; button.type = 'button'; button.textContent = 'Dark Reader'; button.addEventListener('click', () => togglePanel()); document.documentElement.appendChild(button); const panel = document.createElement('div'); panel.id = PANEL_ID; panel.style.display = 'none'; panel.innerHTML = ` <div class="dr-title"> <span>Dark Reader</span> <button class="dr-close" type="button" aria-label="Close">✕</button> </div> <div class="dr-row dr-inline"> <label style="margin:0;"> <input id="dr-enabled" type="checkbox"> Enabled </label> <span id="dr-status" class="dr-small">Loading…</span> </div> <div class="dr-row"> <label for="dr-mode">Mode</label> <select id="dr-mode" style="width:100%;padding:8px;border-radius:10px;border:1px solid rgba(255,255,255,.15);background:rgba(255,255,255,.08);color:#fff;"> <option value="enable">Always on</option> <option value="auto">Follow system theme</option> </select> </div> <div class="dr-spacer"></div> <div class="dr-row"> <div class="dr-inline"> <label for="dr-brightness">Brightness</label> <span class="dr-value" id="dr-brightness-value"></span> </div> <input id="dr-brightness" type="range" min="0" max="200" step="1"> </div> <div class="dr-row"> <div class="dr-inline"> <label for="dr-contrast">Contrast</label> <span class="dr-value" id="dr-contrast-value"></span> </div> <input id="dr-contrast" type="range" min="0" max="200" step="1"> </div> <div class="dr-row"> <div class="dr-inline"> <label for="dr-sepia">Sepia</label> <span class="dr-value" id="dr-sepia-value"></span> </div> <input id="dr-sepia" type="range" min="0" max="100" step="1"> </div> <div class="dr-actions"> <button id="dr-toggle" type="button">Toggle</button> <button id="dr-reset" type="button">Reset</button> </div> <div class="dr-row dr-small" style="margin-top:10px;"> Shortcut: Alt+Shift+D </div> `; document.documentElement.appendChild(panel); const closeBtn = panel.querySelector('.dr-close'); const enabledEl = panel.querySelector('#dr-enabled'); const modeEl = panel.querySelector('#dr-mode'); const brightnessEl = panel.querySelector('#dr-brightness'); const contrastEl = panel.querySelector('#dr-contrast'); const sepiaEl = panel.querySelector('#dr-sepia'); const brightnessValue = panel.querySelector('#dr-brightness-value'); const contrastValue = panel.querySelector('#dr-contrast-value'); const sepiaValue = panel.querySelector('#dr-sepia-value'); const statusEl = panel.querySelector('#dr-status'); const toggleBtn = panel.querySelector('#dr-toggle'); const resetBtn = panel.querySelector('#dr-reset'); closeBtn.addEventListener('click', () => togglePanel(false)); enabledEl.addEventListener('change', () => setEnabled(enabledEl.checked)); modeEl.addEventListener('change', () => setMode(modeEl.value)); brightnessEl.addEventListener('input', () => setSetting('brightness', clampNumber(brightnessEl.value, 0, 200, DEFAULTS.brightness))); contrastEl.addEventListener('input', () => setSetting('contrast', clampNumber(contrastEl.value, 0, 200, DEFAULTS.contrast))); sepiaEl.addEventListener('input', () => setSetting('sepia', clampNumber(sepiaEl.value, 0, 100, DEFAULTS.sepia))); toggleBtn.addEventListener('click', () => setEnabled(!settings.enabled)); resetBtn.addEventListener('click', () => { settings = { ...DEFAULTS }; saveSettings(); syncPanelFields(); applyDarkReader(); }); function updateStatusText(text) { statusEl.textContent = text || (settings.enabled ? 'On' : 'Off'); } function updateButtonText() { button.textContent = settings.enabled ? 'Dark Reader ✓' : 'Dark Reader'; } function syncPanelFields() { enabledEl.checked = settings.enabled; modeEl.value = settings.mode; brightnessEl.value = String(settings.brightness); contrastEl.value = String(settings.contrast); sepiaEl.value = String(settings.sepia); brightnessValue.textContent = String(settings.brightness); contrastValue.textContent = String(settings.contrast); sepiaValue.textContent = String(settings.sepia); updateStatusText(); updateButtonText(); } window.__dr_everywhere_ui = { panel, button, syncPanelFields, updateStatusText, updateButtonText }; syncPanelFields(); } function syncPanelFields() { const ui = window.__dr_everywhere_ui; if (ui && ui.syncPanelFields) ui.syncPanelFields(); } function updateStatusText(text) { const ui = window.__dr_everywhere_ui; if (ui && ui.updateStatusText) ui.updateStatusText(text); } function updateButtonText() { const ui = window.__dr_everywhere_ui; if (ui && ui.updateButtonText) ui.updateButtonText(); } function togglePanel(force) { createUI(); const ui = window.__dr_everywhere_ui; if (!ui) return; panelOpen = typeof force === 'boolean' ? force : !panelOpen; ui.panel.style.display = panelOpen ? 'block' : 'none'; } function installKeyboardShortcuts() { window.addEventListener('keydown', (e) => { if (e.altKey && e.shiftKey && e.code === 'KeyD') { e.preventDefault(); setEnabled(!settings.enabled); } if (e.altKey && e.shiftKey && e.code === 'KeyS') { e.preventDefault(); togglePanel(); } if (e.key === 'Escape' && panelOpen) { togglePanel(false); } }, true); } async function init() { createUI(); installKeyboardShortcuts(); // Give the page a moment to build its DOM, then apply the theme. await wait(0); await applyDarkReader(); // Re-apply if the page changes heavily and the library is already loaded. const observer = new MutationObserver(() => { if (settings.enabled && window.DarkReader) { // Keep this lightweight: just ensure it stays on. if (!window.DarkReader.isEnabled || !window.DarkReader.isEnabled()) { applyDarkReader(); } } }); observer.observe(document.documentElement, { childList: true, subtree: true }); window.addEventListener('beforeunload', () => { try { observer.disconnect(); } catch {} }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init, { once: true }); } else { init(); } })(); -
affan2021shaikh revised this gist
Apr 16, 2026 . 2 changed files with 905 additions and 467 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,905 @@ // ==UserScript== // @name YouTube Background Play Max // @namespace https://example.invalid/ // @version 3.0.0 // @description Best-effort background play for YouTube desktop and mobile web using only standard userscript features. // @match https://www.youtube.com/* // @match https://m.youtube.com/* // @run-at document-start // @grant none // @noframes // ==/UserScript== (() => { 'use strict'; const CFG = { debug: false, manualGraceMs: 1200, hiddenRetryMs: 200, visibleRetryMs: 350, maxRetryMs: 5000, hiddenRetryWindowMs: 30000, visibilitySpoof: true, mediaSession: true, audioKeepalive: true, pipFallback: true, mutedFallback: true, pipRetryCooldownMs: 15000, mutedRetryCooldownMs: 10000, restoreMuteAfterMs: 1200, }; const log = (...args) => { if (CFG.debug) console.log('[YT BG Play Max]', ...args); }; const SEL = [ '#movie_player video', 'ytd-player video', 'video.html5-main-video', 'video', ]; const CONTROL_SEL = [ '.ytp-play-button', '.ytp-big-mode-play-button', 'button', '[role="button"]', 'tp-yt-paper-icon-button', 'yt-icon-button', ].join(','); const STATE = { currentVideo: null, desiredPlaying: false, lastGestureAt: 0, lastExplicitPlayPauseAt: 0, lastManualPauseAt: 0, hiddenSince: 0, retryTimer: 0, retryDelay: CFG.visibleRetryMs, playInFlight: false, booted: false, observerStarted: false, frameLoopStarted: false, failures: 0, lastPipAttemptAt: 0, lastMutedFallbackAt: 0, media: { ctx: null, oscillator: null, gain: null, unlocked: false, }, originals: { hidden: null, visibilityState: null, webkitHidden: null, webkitVisibilityState: null, hasFocus: null, }, }; const now = () => Date.now(); function isElement(v) { return v && v.nodeType === 1; } function isTypingTarget(target) { if (!isElement(target)) return false; const tag = target.tagName; return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || target.isContentEditable; } function getDescriptor(proto, prop) { let p = proto; while (p) { const d = Object.getOwnPropertyDescriptor(p, prop); if (d) return d; p = Object.getPrototypeOf(p); } return null; } function readActualHidden() { try { const d = STATE.originals.hidden; if (d && typeof d.get === 'function') return !!d.get.call(document); } catch (_) {} try { const d = STATE.originals.webkitHidden; if (d && typeof d.get === 'function') return !!d.get.call(document); } catch (_) {} try { return document.visibilityState !== 'visible'; } catch (_) { return false; } } function readActualVisibilityState() { try { const d = STATE.originals.visibilityState; if (d && typeof d.get === 'function') return String(d.get.call(document)); } catch (_) {} try { const d = STATE.originals.webkitVisibilityState; if (d && typeof d.get === 'function') return String(d.get.call(document)); } catch (_) {} return readActualHidden() ? 'hidden' : 'visible'; } function isActuallyHidden() { return readActualVisibilityState() !== 'visible'; } function installVisibilitySpoof() { if (!CFG.visibilitySpoof) return; const proto = Document.prototype; STATE.originals.hidden = getDescriptor(proto, 'hidden'); STATE.originals.visibilityState = getDescriptor(proto, 'visibilityState'); STATE.originals.webkitHidden = getDescriptor(proto, 'webkitHidden'); STATE.originals.webkitVisibilityState = getDescriptor(proto, 'webkitVisibilityState'); STATE.originals.hasFocus = getDescriptor(proto, 'hasFocus'); const installGetter = (prop, getter, original) => { try { if (original && original.configurable === false) return false; Object.defineProperty(proto, prop, { configurable: true, enumerable: original ? !!original.enumerable : false, get: getter, }); return true; } catch (_) { return false; } }; installGetter('hidden', function () { return false; }, STATE.originals.hidden); installGetter('visibilityState', function () { return 'visible'; }, STATE.originals.visibilityState); installGetter('webkitHidden', function () { return false; }, STATE.originals.webkitHidden); installGetter('webkitVisibilityState', function () { return 'visible'; }, STATE.originals.webkitVisibilityState); try { const original = STATE.originals.hasFocus; if (!original || original.configurable !== false) { Object.defineProperty(proto, 'hasFocus', { configurable: true, enumerable: original ? !!original.enumerable : false, writable: true, value: function () { return true; }, }); } } catch (_) {} } function setMediaSessionState() { if (!CFG.mediaSession) return; try { if (!('mediaSession' in navigator)) return; navigator.mediaSession.playbackState = STATE.desiredPlaying ? 'playing' : 'paused'; } catch (_) {} } function stopKeepaliveAudio() { const m = STATE.media; try { if (m.ctx && m.ctx.state === 'running') { m.ctx.suspend().catch(() => {}); } } catch (_) {} } function startKeepaliveAudio() { if (!CFG.audioKeepalive) return; if (!STATE.desiredPlaying) return; if (!isActuallyHidden()) { stopKeepaliveAudio(); return; } const m = STATE.media; try { const AudioCtx = window.AudioContext || window.webkitAudioContext; if (!AudioCtx) return; if (!m.ctx) { m.ctx = new AudioCtx(); m.gain = m.ctx.createGain(); m.gain.gain.value = 0.00001; m.oscillator = m.ctx.createOscillator(); m.oscillator.type = 'sine'; m.oscillator.frequency.value = 40; m.oscillator.connect(m.gain); m.gain.connect(m.ctx.destination); m.oscillator.start(); } if (m.ctx.state === 'suspended') { m.ctx.resume().catch(() => {}); } } catch (_) {} } function unlockKeepaliveAudio() { if (!CFG.audioKeepalive) return; const m = STATE.media; m.unlocked = true; if (m.ctx && m.ctx.state === 'suspended') { m.ctx.resume().catch(() => {}); } } function maybeStartKeepaliveAudio() { if (!CFG.audioKeepalive) return; if (!STATE.media.unlocked) return; startKeepaliveAudio(); } function setDesiredPlaying(next, reason) { const changed = STATE.desiredPlaying !== next; STATE.desiredPlaying = next; if (next) { setMediaSessionState(); maybeStartKeepaliveAudio(); if (changed) scheduleRetry(reason || 'intent', true); } else { setMediaSessionState(); stopKeepaliveAudio(); clearTimeout(STATE.retryTimer); } } function manualGraceActive() { const t = now(); const recent = Math.max(STATE.lastGestureAt, STATE.lastExplicitPlayPauseAt, STATE.lastManualPauseAt); return (t - recent) <= CFG.manualGraceMs; } function touchGesture(event) { STATE.lastGestureAt = now(); if (isTypingTarget(event && event.target)) return; unlockKeepaliveAudio(); } function inferControlIntent(event) { const target = event && event.target; if (!isElement(target)) return null; const control = target.closest('.ytp-play-button, .ytp-big-mode-play-button'); if (control) { const label = [ control.getAttribute('aria-label'), control.getAttribute('title'), control.getAttribute('data-title-no-tooltip'), control.textContent, ].filter(Boolean).join(' ').toLowerCase(); if (label.includes('pause') && !label.includes('play')) return 'pause'; if (label.includes('play') && !label.includes('pause')) return 'play'; if (STATE.currentVideo) { return STATE.currentVideo.paused ? 'play' : 'pause'; } } return null; } function onPointerLike(event) { touchGesture(event); const intent = inferControlIntent(event); if (intent === 'play') { STATE.lastExplicitPlayPauseAt = now(); setDesiredPlaying(true, 'user-play'); } else if (intent === 'pause') { STATE.lastExplicitPlayPauseAt = now(); STATE.lastManualPauseAt = now(); setDesiredPlaying(false, 'user-pause'); } } function onKeydown(event) { if (event.repeat) return; if (isTypingTarget(event.target)) return; touchGesture(event); const key = String(event.key || '').toLowerCase(); const mediaKey = key === 'mediaplaypause' || key === 'mediaplay' || key === 'play'; const pauseKey = key === 'mediapause'; if (mediaKey) { STATE.lastExplicitPlayPauseAt = now(); setDesiredPlaying(true, 'media-key-play'); return; } if (pauseKey) { STATE.lastExplicitPlayPauseAt = now(); STATE.lastManualPauseAt = now(); setDesiredPlaying(false, 'media-key-pause'); return; } if (key === ' ' || key === 'spacebar' || key === 'k') { STATE.lastExplicitPlayPauseAt = now(); if (STATE.currentVideo && !STATE.currentVideo.paused) { STATE.lastManualPauseAt = now(); setDesiredPlaying(false, 'toggle-pause'); } else { setDesiredPlaying(true, 'toggle-play'); } } } function scoreVideo(v) { if (!(v instanceof HTMLVideoElement) || !v.isConnected) return -1e18; const r = v.getBoundingClientRect(); const area = Math.max(0, r.width) * Math.max(0, r.height); let score = area; if (r.width > 0 && r.height > 0) score += 1_000_000; if (!v.paused) score += 250_000; if (v.readyState >= 2) score += 15_000; if (v.currentSrc) score += 5_000; if (document.body && document.body.contains(v)) score += 2_000; if (v.matches('.html5-main-video')) score += 250_000; if (v.closest('#movie_player')) score += 175_000; if (v.closest('ytd-player')) score += 125_000; if (v.closest('.html5-video-container')) score += 75_000; return score; } function pickVideo() { const seen = new Set(); const candidates = []; for (const sel of SEL) { for (const v of document.querySelectorAll(sel)) { if (!seen.has(v)) { seen.add(v); candidates.push(v); } } } let best = null; let bestScore = -1e18; for (const v of candidates) { const s = scoreVideo(v); if (s > bestScore) { bestScore = s; best = v; } } return best; } function attachVideo(v) { if (!(v instanceof HTMLVideoElement)) return; if (v === STATE.currentVideo) return; if (STATE.currentVideo) { STATE.currentVideo.removeEventListener('play', onPlay, true); STATE.currentVideo.removeEventListener('playing', onPlaying, true); STATE.currentVideo.removeEventListener('pause', onPause, true); STATE.currentVideo.removeEventListener('ended', onEnded, true); STATE.currentVideo.removeEventListener('emptied', onVideoSourceChange, true); STATE.currentVideo.removeEventListener('loadstart', onVideoSourceChange, true); STATE.currentVideo.removeEventListener('loadedmetadata', onVideoSourceChange, true); STATE.currentVideo.removeEventListener('stalled', onNeedRetry, true); STATE.currentVideo.removeEventListener('waiting', onNeedRetry, true); STATE.currentVideo.removeEventListener('suspend', onNeedRetry, true); STATE.currentVideo.removeEventListener('canplay', onNeedRetry, true); } STATE.currentVideo = v; try { STATE.currentVideo.setAttribute('playsinline', ''); STATE.currentVideo.setAttribute('webkit-playsinline', ''); } catch (_) {} STATE.currentVideo.addEventListener('play', onPlay, true); STATE.currentVideo.addEventListener('playing', onPlaying, true); STATE.currentVideo.addEventListener('pause', onPause, true); STATE.currentVideo.addEventListener('ended', onEnded, true); STATE.currentVideo.addEventListener('emptied', onVideoSourceChange, true); STATE.currentVideo.addEventListener('loadstart', onVideoSourceChange, true); STATE.currentVideo.addEventListener('loadedmetadata', onVideoSourceChange, true); STATE.currentVideo.addEventListener('stalled', onNeedRetry, true); STATE.currentVideo.addEventListener('waiting', onNeedRetry, true); STATE.currentVideo.addEventListener('suspend', onNeedRetry, true); STATE.currentVideo.addEventListener('canplay', onNeedRetry, true); log('attached video'); } function syncVideo() { const best = pickVideo(); if (best) attachVideo(best); } function resetRetryDelay() { STATE.retryDelay = isActuallyHidden() ? CFG.hiddenRetryMs : CFG.visibleRetryMs; } function bumpRetryDelay() { STATE.retryDelay = Math.min(Math.max(STATE.retryDelay * 1.5, 200), CFG.maxRetryMs); } async function tryPictureInPicture(reason) { if (!CFG.pipFallback) return false; if (!STATE.currentVideo || !STATE.desiredPlaying) return false; if (!STATE.currentVideo.isConnected) return false; if (!(location.hostname.startsWith('m.') || matchMedia('(pointer: coarse)').matches)) return false; if (!document.pictureInPictureEnabled) return false; if (document.pictureInPictureElement === STATE.currentVideo) return true; if (STATE.currentVideo.disablePictureInPicture) return false; const t = now(); if (t - STATE.lastPipAttemptAt < CFG.pipRetryCooldownMs) return false; STATE.lastPipAttemptAt = t; try { if (typeof STATE.currentVideo.requestPictureInPicture !== 'function') return false; await STATE.currentVideo.requestPictureInPicture(); log('pip ok', reason); return true; } catch (err) { log('pip failed', reason, err && err.name ? err.name : err); return false; } } async function tryMutedFallback(reason) { if (!CFG.mutedFallback) return false; if (!STATE.currentVideo || !STATE.desiredPlaying) return false; if (!STATE.currentVideo.isConnected) return false; if (STATE.currentVideo.muted) return false; const t = now(); if (t - STATE.lastMutedFallbackAt < CFG.mutedRetryCooldownMs) return false; STATE.lastMutedFallbackAt = t; const prevMuted = STATE.currentVideo.muted; const prevVolume = STATE.currentVideo.volume; try { STATE.currentVideo.muted = true; const p = STATE.currentVideo.play(); if (p && typeof p.then === 'function') { await p; } setTimeout(() => { try { if (!STATE.currentVideo || STATE.currentVideo.paused) return; if (!STATE.desiredPlaying) return; STATE.currentVideo.muted = prevMuted; STATE.currentVideo.volume = prevVolume; } catch (_) {} }, CFG.restoreMuteAfterMs); log('muted fallback ok', reason); return true; } catch (err) { try { if (STATE.currentVideo) { STATE.currentVideo.muted = prevMuted; STATE.currentVideo.volume = prevVolume; } } catch (_) {} log('muted fallback failed', reason, err && err.name ? err.name : err); return false; } } async function safePlay(reason) { if (!STATE.currentVideo || !STATE.desiredPlaying) return false; if (STATE.playInFlight) return true; STATE.playInFlight = true; try { if (!STATE.currentVideo.isConnected) { syncVideo(); if (!STATE.currentVideo) return false; } if (!STATE.currentVideo.paused && STATE.currentVideo.readyState >= 2) { STATE.failures = 0; resetRetryDelay(); maybeStartKeepaliveAudio(); setMediaSessionState(); return true; } try { const p = STATE.currentVideo.play(); if (p && typeof p.then === 'function') { await p; } STATE.failures = 0; resetRetryDelay(); maybeStartKeepaliveAudio(); setMediaSessionState(); log('play ok', reason); return true; } catch (err) { log('play failed', reason, err && err.name ? err.name : err); } STATE.failures += 1; bumpRetryDelay(); if (STATE.failures >= 2) { const pipOk = await tryPictureInPicture(reason); if (pipOk) { STATE.failures = 0; resetRetryDelay(); maybeStartKeepaliveAudio(); setMediaSessionState(); return true; } } if (STATE.failures >= 3) { const mutedOk = await tryMutedFallback(reason); if (mutedOk) { STATE.failures = 0; resetRetryDelay(); maybeStartKeepaliveAudio(); setMediaSessionState(); return true; } } return false; } finally { STATE.playInFlight = false; } } function scheduleRetry(reason, immediate = false) { if (!STATE.desiredPlaying) return; clearTimeout(STATE.retryTimer); const hidden = isActuallyHidden(); const delay = immediate ? 0 : (hidden ? STATE.hiddenRetryDelay || CFG.hiddenRetryMs : STATE.retryDelay); STATE.retryTimer = setTimeout(async () => { if (!STATE.desiredPlaying) return; const stillHidden = isActuallyHidden(); if (stillHidden && STATE.hiddenSince && (now() - STATE.hiddenSince) > CFG.hiddenRetryWindowMs) { return; } syncVideo(); if (!STATE.currentVideo) { bumpRetryDelay(); scheduleRetry(reason); return; } if (!STATE.currentVideo.paused && STATE.currentVideo.readyState >= 2) { STATE.failures = 0; resetRetryDelay(); maybeStartKeepaliveAudio(); return; } const ok = await safePlay(reason); if (!ok && STATE.desiredPlaying) { scheduleRetry(reason); } }, delay); } function onPlay() { setDesiredPlaying(true, 'play-event'); } function onPlaying() { setDesiredPlaying(true, 'playing-event'); } function onPause() { if (manualGraceActive()) { STATE.lastManualPauseAt = now(); setDesiredPlaying(false, 'manual-pause'); return; } if (STATE.desiredPlaying) { scheduleRetry('pause', true); } } function onEnded() { setDesiredPlaying(false, 'ended'); } function onVideoSourceChange() { setTimeout(() => { syncVideo(); if (STATE.desiredPlaying) scheduleRetry('source-change', true); }, 0); } function onNeedRetry() { if (STATE.desiredPlaying) scheduleRetry('buffering'); } function onVisibilityChange() { const hidden = isActuallyHidden(); if (hidden) { STATE.hiddenSince = now(); if (STATE.desiredPlaying) { maybeStartKeepaliveAudio(); scheduleRetry('hidden', true); } } else { STATE.hiddenSince = 0; if (STATE.desiredPlaying) { stopKeepaliveAudio(); scheduleRetry('visible', true); } } } function onPageHide() { STATE.hiddenSince = now(); if (STATE.desiredPlaying) scheduleRetry('pagehide', true); } function onPageShow() { STATE.hiddenSince = 0; if (STATE.desiredPlaying) scheduleRetry('pageshow', true); } function onFreeze() { if (STATE.desiredPlaying) scheduleRetry('freeze', true); } function onResume() { if (STATE.desiredPlaying) scheduleRetry('resume', true); } function patchPause() { const proto = HTMLMediaElement && HTMLMediaElement.prototype; if (!proto || typeof proto.pause !== 'function') return; const nativePause = proto.pause; proto.pause = function patchedPause() { try { if ( this === STATE.currentVideo && STATE.desiredPlaying && isActuallyHidden() && !manualGraceActive() ) { log('blocked hidden pause'); return; } } catch (_) {} return nativePause.apply(this, arguments); }; } function patchHistory() { const dispatch = () => window.dispatchEvent(new Event('ytbg-locationchange')); try { const pushState = history.pushState; if (typeof pushState === 'function') { history.pushState = function () { const ret = pushState.apply(this, arguments); dispatch(); return ret; }; } } catch (_) {} try { const replaceState = history.replaceState; if (typeof replaceState === 'function') { history.replaceState = function () { const ret = replaceState.apply(this, arguments); dispatch(); return ret; }; } } catch (_) {} window.addEventListener('popstate', dispatch, true); window.addEventListener('yt-navigate-finish', dispatch, true); window.addEventListener('yt-page-data-updated', dispatch, true); window.addEventListener('ytbg-locationchange', () => { setTimeout(() => { syncVideo(); if (STATE.desiredPlaying) scheduleRetry('navigation', true); }, 0); }, true); } function startObserver() { if (STATE.observerStarted) return; const root = document.documentElement; if (!root) return; STATE.observerStarted = true; const mo = new MutationObserver(() => { syncVideo(); if (STATE.desiredPlaying && (!STATE.currentVideo || !STATE.currentVideo.isConnected)) { scheduleRetry('mutation', true); } }); mo.observe(root, { childList: true, subtree: true }); syncVideo(); log('observer started'); } function startFrameLoop() { if (STATE.frameLoopStarted) return; STATE.frameLoopStarted = true; const tick = () => { if (STATE.desiredPlaying) { if (!STATE.currentVideo || !STATE.currentVideo.isConnected) { syncVideo(); } if (STATE.currentVideo) { if (isActuallyHidden()) { if (STATE.currentVideo.paused) scheduleRetry('frame-hidden'); } else if (STATE.currentVideo.paused) { scheduleRetry('frame-visible'); } } } requestAnimationFrame(tick); }; requestAnimationFrame(tick); } function installMediaSessionHandlers() { if (!CFG.mediaSession) return; try { if (!('mediaSession' in navigator)) return; if (typeof navigator.mediaSession.setActionHandler !== 'function') return; navigator.mediaSession.setActionHandler('play', () => { setDesiredPlaying(true, 'mediasession-play'); }); navigator.mediaSession.setActionHandler('pause', () => { STATE.lastManualPauseAt = now(); setDesiredPlaying(false, 'mediasession-pause'); }); navigator.mediaSession.setActionHandler('stop', () => { STATE.lastManualPauseAt = now(); setDesiredPlaying(false, 'mediasession-stop'); }); } catch (_) {} } function installGlobalListeners() { document.addEventListener('pointerdown', onPointerLike, true); document.addEventListener('pointerup', onPointerLike, true); document.addEventListener('click', onPointerLike, true); document.addEventListener('touchstart', onPointerLike, true); document.addEventListener('mousedown', onPointerLike, true); document.addEventListener('keydown', onKeydown, true); document.addEventListener('visibilitychange', onVisibilityChange, true); window.addEventListener('pagehide', onPageHide, true); window.addEventListener('pageshow', onPageShow, true); window.addEventListener('freeze', onFreeze, true); window.addEventListener('resume', onResume, true); window.addEventListener('focus', onVisibilityChange, true); window.addEventListener('blur', onVisibilityChange, true); } function boot() { if (STATE.booted) return; STATE.booted = true; installVisibilitySpoof(); patchPause(); patchHistory(); installGlobalListeners(); installMediaSessionHandlers(); const tryStart = () => { startObserver(); if (document.documentElement) startFrameLoop(); }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', tryStart, { once: true }); } else { tryStart(); } setInterval(() => { if (!STATE.desiredPlaying) return; syncVideo(); if (!STATE.currentVideo) return; if (isActuallyHidden()) { if (STATE.currentVideo.paused) scheduleRetry('watchdog-hidden'); maybeStartKeepaliveAudio(); return; } if (STATE.currentVideo.paused) { scheduleRetry('watchdog-visible'); } else { STATE.failures = 0; resetRetryDelay(); stopKeepaliveAudio(); } }, 250); } boot(); })(); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,467 +0,0 @@ -
affan2021shaikh created this gist
Apr 9, 2026 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,467 @@ // ==UserScript== // @name YouTube Background Play Plus // @namespace https://example.invalid/ // @version 2.0.0 // @description Best-effort background play for YouTube desktop and mobile web using only standard userscript features. // @match https://www.youtube.com/* // @match https://m.youtube.com/* // @run-at document-start // @grant none // @noframes // ==/UserScript== (() => { 'use strict'; const CFG = { manualGraceMs: 1000, hiddenRetryMs: 250, visibleRetryMs: 500, maxRetryMs: 5000, hiddenRetryWindowMs: 30000, debug: false, }; const log = (...args) => { if (CFG.debug) console.log('[YT BG Play Plus]', ...args); }; const SEL = [ '#movie_player video', 'ytd-player video', 'video.html5-main-video', 'video', ]; const CONTROL_SEL = [ 'button', '[role="button"]', 'tp-yt-paper-icon-button', 'yt-icon-button', '.ytp-button', '.ytp-play-button', '.ytp-volume-panel', '.ytp-fullscreen-button', ].join(','); let currentVideo = null; let desiredPlaying = false; let lastGestureAt = 0; let lastExplicitControlAt = 0; let lastManualPauseAt = 0; let hiddenSince = 0; let observerStarted = false; let retryTimer = 0; let retryDelay = CFG.visibleRetryMs; let playInFlight = false; let booted = false; const now = () => Date.now(); const max2 = Math.max; function touchGesture(event) { const t = now(); lastGestureAt = t; const target = event && event.target; if (target instanceof Element && target.closest(CONTROL_SEL)) { lastExplicitControlAt = t; } } function manualGraceActive() { const t = now(); const recent = max2(lastGestureAt, lastExplicitControlAt, lastManualPauseAt); return (t - recent) <= CFG.manualGraceMs; } function setMediaSessionState() { try { if (!('mediaSession' in navigator)) return; navigator.mediaSession.playbackState = desiredPlaying ? 'playing' : 'paused'; } catch (_) {} } function installMediaSessionHandlers() { try { if (!('mediaSession' in navigator)) return; if (typeof navigator.mediaSession.setActionHandler !== 'function') return; navigator.mediaSession.setActionHandler('play', () => { desiredPlaying = true; setMediaSessionState(); scheduleRetry('mediasession-play', true); }); navigator.mediaSession.setActionHandler('pause', () => { desiredPlaying = false; lastManualPauseAt = now(); setMediaSessionState(); try { if (currentVideo && !currentVideo.paused) currentVideo.pause(); } catch (_) {} }); } catch (_) {} } function scoreVideo(v) { if (!(v instanceof HTMLVideoElement) || !v.isConnected) return -1e18; const r = v.getBoundingClientRect(); const area = max2(0, r.width) * max2(0, r.height); let score = area; if (r.width > 0 && r.height > 0) score += 1_000_000; if (document.visibilityState === 'visible') score += 20_000; if (v.readyState >= 2) score += 10_000; if (!v.paused) score += 50_000; if (v.currentSrc) score += 2_000; if (v.matches('.html5-main-video')) score += 200_000; if (v.closest('#movie_player')) score += 150_000; if (v.closest('ytd-player')) score += 100_000; if (v.closest('.html5-video-container')) score += 50_000; return score; } function pickVideo() { const seen = new Set(); const candidates = []; for (const sel of SEL) { for (const v of document.querySelectorAll(sel)) { if (!seen.has(v)) { seen.add(v); candidates.push(v); } } } let best = null; let bestScore = -1e18; for (const v of candidates) { const s = scoreVideo(v); if (s > bestScore) { bestScore = s; best = v; } } return best; } function attachVideo(v) { if (!(v instanceof HTMLVideoElement)) return; if (v === currentVideo) return; if (currentVideo) { currentVideo.removeEventListener('play', onPlay, true); currentVideo.removeEventListener('playing', onPlaying, true); currentVideo.removeEventListener('pause', onPause, true); currentVideo.removeEventListener('ended', onEnded, true); currentVideo.removeEventListener('emptied', onChangedSource, true); currentVideo.removeEventListener('loadstart', onChangedSource, true); currentVideo.removeEventListener('loadedmetadata', onChangedSource, true); currentVideo.removeEventListener('stalled', onNeedRetry, true); currentVideo.removeEventListener('waiting', onNeedRetry, true); currentVideo.removeEventListener('suspend', onNeedRetry, true); currentVideo.removeEventListener('canplay', onNeedRetry, true); } currentVideo = v; try { currentVideo.setAttribute('playsinline', ''); currentVideo.setAttribute('webkit-playsinline', ''); } catch (_) {} currentVideo.addEventListener('play', onPlay, true); currentVideo.addEventListener('playing', onPlaying, true); currentVideo.addEventListener('pause', onPause, true); currentVideo.addEventListener('ended', onEnded, true); currentVideo.addEventListener('emptied', onChangedSource, true); currentVideo.addEventListener('loadstart', onChangedSource, true); currentVideo.addEventListener('loadedmetadata', onChangedSource, true); currentVideo.addEventListener('stalled', onNeedRetry, true); currentVideo.addEventListener('waiting', onNeedRetry, true); currentVideo.addEventListener('suspend', onNeedRetry, true); currentVideo.addEventListener('canplay', onNeedRetry, true); log('attached video'); } function syncVideo() { const best = pickVideo(); if (best) attachVideo(best); } function resetRetryDelay() { retryDelay = document.hidden ? CFG.hiddenRetryMs : CFG.visibleRetryMs; } function bumpRetryDelay() { retryDelay = Math.min(Math.max(retryDelay * 1.5, 250), CFG.maxRetryMs); } function safePlay(reason) { if (!currentVideo || !desiredPlaying) return Promise.resolve(false); if (playInFlight) return Promise.resolve(true); playInFlight = true; let p; try { p = currentVideo.play(); } catch (err) { playInFlight = false; log('play threw', reason, err && err.name ? err.name : err); return Promise.resolve(false); } if (p && typeof p.then === 'function') { return p.then(() => { playInFlight = false; resetRetryDelay(); setMediaSessionState(); log('play ok', reason); return true; }).catch((err) => { playInFlight = false; bumpRetryDelay(); log('play failed', reason, err && err.name ? err.name : err); return false; }); } playInFlight = false; resetRetryDelay(); setMediaSessionState(); return Promise.resolve(true); } function scheduleRetry(reason, immediate = false) { if (!desiredPlaying) return; clearTimeout(retryTimer); const delay = immediate ? 0 : (document.hidden ? CFG.hiddenRetryMs : retryDelay); retryTimer = setTimeout(async () => { if (!desiredPlaying) return; if (document.hidden && hiddenSince && (now() - hiddenSince) > CFG.hiddenRetryWindowMs) { return; } syncVideo(); if (!currentVideo) { bumpRetryDelay(); scheduleRetry(reason); return; } if (!currentVideo.paused && currentVideo.readyState >= 2) { resetRetryDelay(); return; } const ok = await safePlay(reason); if (!ok && desiredPlaying) scheduleRetry(reason); }, delay); } function onPlay() { desiredPlaying = true; resetRetryDelay(); setMediaSessionState(); } function onPlaying() { desiredPlaying = true; resetRetryDelay(); setMediaSessionState(); } function onPause() { if (manualGraceActive()) { desiredPlaying = false; lastManualPauseAt = now(); setMediaSessionState(); return; } if (desiredPlaying) { scheduleRetry('pause'); } } function onEnded() { desiredPlaying = false; resetRetryDelay(); setMediaSessionState(); } function onChangedSource() { setTimeout(() => { syncVideo(); if (desiredPlaying) scheduleRetry('source-change', true); }, 0); } function onNeedRetry() { if (desiredPlaying) scheduleRetry('buffering'); } function onVisibilityChange() { if (document.hidden) { hiddenSince = now(); if (desiredPlaying) scheduleRetry('hidden', true); } else { hiddenSince = 0; if (desiredPlaying) scheduleRetry('visible', true); } } function onPageHide() { if (desiredPlaying) scheduleRetry('pagehide', true); } function onPageShow() { if (desiredPlaying) scheduleRetry('pageshow', true); } function onFocusChange() { if (desiredPlaying) scheduleRetry('focus-change', true); } function onUserIntent(event) { touchGesture(event); } function patchPause() { const proto = HTMLMediaElement && HTMLMediaElement.prototype; if (!proto || typeof proto.pause !== 'function') return; const nativePause = proto.pause; proto.pause = function patchedPause() { try { if ( this === currentVideo && desiredPlaying && document.hidden && !manualGraceActive() ) { log('blocked hidden pause'); return; } } catch (_) {} return nativePause.apply(this, arguments); }; } function patchHistory() { const dispatch = () => window.dispatchEvent(new Event('ytbg-locationchange')); try { const pushState = history.pushState; if (typeof pushState === 'function') { history.pushState = function () { const ret = pushState.apply(this, arguments); dispatch(); return ret; }; } } catch (_) {} try { const replaceState = history.replaceState; if (typeof replaceState === 'function') { history.replaceState = function () { const ret = replaceState.apply(this, arguments); dispatch(); return ret; }; } } catch (_) {} window.addEventListener('popstate', dispatch, true); window.addEventListener('yt-navigate-finish', dispatch, true); window.addEventListener('yt-page-data-updated', dispatch, true); window.addEventListener('ytbg-locationchange', () => { setTimeout(() => { syncVideo(); if (desiredPlaying) scheduleRetry('navigation', true); }, 0); }, true); } function startObserver() { if (observerStarted) return; const root = document.documentElement || document; observerStarted = true; const mo = new MutationObserver(() => { syncVideo(); }); mo.observe(root, { childList: true, subtree: true }); syncVideo(); log('observer started'); } function installGlobalListeners() { document.addEventListener('pointerdown', onUserIntent, true); document.addEventListener('touchstart', onUserIntent, true); document.addEventListener('mousedown', onUserIntent, true); document.addEventListener('click', onUserIntent, true); document.addEventListener('keydown', onUserIntent, true); document.addEventListener('visibilitychange', onVisibilityChange, true); window.addEventListener('pagehide', onPageHide, true); window.addEventListener('pageshow', onPageShow, true); window.addEventListener('focus', onFocusChange, true); window.addEventListener('blur', onFocusChange, true); } function boot() { if (booted) return; booted = true; patchPause(); patchHistory(); installGlobalListeners(); installMediaSessionHandlers(); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { startObserver(); }, { once: true }); } else { startObserver(); } setInterval(() => { if (!desiredPlaying) return; syncVideo(); if (!currentVideo) return; if (document.hidden) { if (currentVideo.paused) scheduleRetry('watchdog-hidden'); return; } if (currentVideo.paused) { scheduleRetry('watchdog-visible'); } }, 300); } boot(); })();