diff --git a/css/player-base.js b/css/player-base.js index c01d60a0..4fc7f9c2 100644 --- a/css/player-base.js +++ b/css/player-base.js @@ -2,530 +2,564 @@ 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, + + 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: inline playback hint for iOS/Safari + try { videoEl.setAttribute('playsinline', ''); videoEl.setAttribute('webkit-playsinline', ''); } catch {} + + // --- GLOBAL STATE GUARDS ---------------------------------------------------- + let syncing = false; // prevents normal ping-pong + let restarting = false; // prevents loop-end ping-pong + let seekingInProgress = false; // true between seeking/seeked + let resumeAfterSeek = false; // whether to auto-resume after seek + let didFirstSeek = false; // special handling for the first seek only + let vIsPlaying = false; // becomes true on 'playing' + let aIsPlaying = false; // becomes true on 'playing' + + // Mute memory for muted-retry on autoplay policy + let prevVideoMuted = false; + let prevAudioMuted = false; + let pendingUnmute = false; + + // 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: state arbiter watchdog (forces both to share same paused/playing state) + let arbiterTimer = null; + const ARBITER_MS = 150; + + // NEW: suppression window so arbiter ignores the fragile post-seek moment + let arbiterSuppressUntil = 0; + const now = () => performance.now(); + const arbiterSuppressed = () => now() < arbiterSuppressUntil; + + function startArbiter() { + if (arbiterTimer) clearInterval(arbiterTimer); + arbiterTimer = setInterval(() => { + if (syncing || restarting || seekingInProgress || arbiterSuppressed()) 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