// 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 {} // NEW: keep hidden audio eager and ready sooner (helps first-seek stability) try { audio.setAttribute('preload', 'auto'); } 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: short quiesce windows to ignore chatty events after seek or coordinator actions const SEEK_QUIESCE_MS = 700; // time to ignore play/pause/arbiter after seek const ACTION_COOLDOWN_MS = 550; // time to ignore arbiter after coordinated play/pause let quiesceUntil = 0; // timestamp (ms) until which we ignore event noise let arbiterCooldownUntil = 0; // timestamp (ms) for arbiter cool-down const now = () => performance.now(); const inQuiesce = () => now() < quiesceUntil; const inCooldown = () => now() < arbiterCooldownUntil; // 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(() => { // NEW: do nothing during seek quiesce or cooldown; this is where ping-pong used to start if (inQuiesce() || inCooldown() || syncing || restarting || seekingInProgress) return; // treat "playing" strictly; ended counts as paused const vPlaying = !video.paused() && !video.ended(); const aPlaying = !audio.paused && !audio.ended; // NEW: if both aren't actually playable at the current time, don't force a pause yet const t = Number(video.currentTime()); if (!bothPlayableAt(t)) return; // 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