// in the beginning.... god made mrrprpmnaynayaynaynayanyuwuuuwmauwnwanwaumawp :p var _yt_player = videojs; var versionclient = "youtube.player.web_20250917_22_RC00" document.addEventListener("DOMContentLoaded", () => { // video.js 8 init - source can be seen in https://poketube.fun/static/vjs.min.js or the vjs.min.js file const video = videojs('video', { controls: true, autoplay: false, preload: 'auto', errorDisplay: false, }); // todo : remove this code lol const qs = new URLSearchParams(window.location.search); const qua = qs.get("quality") || ""; const vidKey = qs.get('v'); try { localStorage.setItem(`progress-${vidKey}`, 0); } catch {} // raw media elements const videoEl = document.getElementById('video'); const audio = document.getElementById('aud'); const audioEl = document.getElementById('aud'); let volGuard = false; // FIX: ensure inline playback hint for iOS/Safari try { videoEl.setAttribute('playsinline', ''); videoEl.setAttribute('webkit-playsinline', ''); } catch {} // global anti-ping-pong guard let syncing = false; // prevents normal ping-pong let restarting = false; // prevents loop-end ping-pong // FIX: explicit loop-state variables let desiredLoop = !!videoEl.loop || qs.get("loop") === "1" || qs.get("loop") === "true" || window.forceLoop === true; // FIX: tracks the short window *during* a loop restart let suppressEndedUntil = 0; // FIX: co-play tracking flags (true only when each element fires 'playing') let vIsPlaying = false; let aIsPlaying = false; // remember mute states for temporary autoplay retries let prevVideoMuted = false; let prevAudioMuted = false; let pendingUnmute = false; // FIX: seeking coordination (prevents first-load seek ping-pong) let seekingInProgress = false; let resumeAfterSeek = false; // NEW FIX: grace window to silence arbiter + bridge immediately after seeks / restarts let suppressBridgeUntil = 0; const nowPerf = () => performance?.now?.() || Date.now(); const bridgingSilenced = () => nowPerf() < suppressBridgeUntil; function silenceBridging(ms = 900) { // tuned: long enough for decoders to settle after a seek suppressBridgeUntil = nowPerf() + ms; } // FIX: state arbiter watchdog (forces both to share same paused/playing state) let arbiterTimer = null; const ARBITER_MS = 150; function startArbiter() { if (arbiterTimer) clearInterval(arbiterTimer); arbiterTimer = setInterval(() => { if (syncing || restarting || seekingInProgress || bridgingSilenced()) return; // treat "playing" strictly; ended counts as paused const vPlaying = !video.paused() && !video.ended(); const aPlaying = !audio.paused && !audio.ended; // if exactly one is playing, pause the one that is playing (safe + deterministic) if (vPlaying && !aPlaying) { try { video.pause(); } catch {} } else if (aPlaying && !vPlaying) { try { audio.pause(); } catch {} } }, ARBITER_MS); } function stopArbiter() { if (arbiterTimer) { clearInterval(arbiterTimer); arbiterTimer = null; } } // turn OFF native loop so 'ended' fires and we control both tracks together try { videoEl.loop = false; videoEl.removeAttribute?.('loop'); } catch {} try { audio.loop = false; audio.removeAttribute?.('loop'); } catch {} // If someone toggles the