diff --git a/css/player-base-new.js b/css/player-base-new.js index d9eca9b1..8c70a38c 100644 --- a/css/player-base-new.js +++ b/css/player-base-new.js @@ -3,500 +3,487 @@ var _yt_player = videojs; var versionclient = "youtube.player.web_20250917_22_RC00" - -// video.js 8 init - source can be seen in https://poketube.fun/static/vjs.min.js or the vjs.min.js file document.addEventListener("DOMContentLoaded", () => { - const player = videojs('video', { - controls: true, - autoplay: false, - preload: 'auto', - errorDisplay: false, - }); + const player = 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 { if (vidKey) localStorage.setItem(`progress-${vidKey}`, 0); } catch {} + const qs = new URLSearchParams(location.search); + const qua = qs.get("quality") || ""; + const vidKey = qs.get("v"); + try { if (vidKey) localStorage.setItem(`progress-${vidKey}`, 0); } catch {} - // raw media elements - const videoEl = document.getElementById('video'); - const audioEl = document.getElementById('aud'); + // raw media nodes + const videoEl = document.getElementById("video"); + const audioEl = document.getElementById("aud"); - // harden hidden audio so it never steals session/focus + // keep the hidden audio truly hidden/quiet + try { + audioEl.controls = false; + audioEl.setAttribute("aria-hidden", "true"); + audioEl.setAttribute("tabindex", "-1"); + audioEl.setAttribute("controlslist", "noplaybackrate nodownload noremoteplayback"); + audioEl.disableRemotePlayback = true; + videoEl.setAttribute("playsinline", ""); + audioEl.setAttribute("playsinline", ""); + audioEl.preload = "auto"; + } catch {} + + // initial source probes + const pickAudioSrc = () => { + const s = audioEl?.getAttribute?.("src"); + if (s) return s; + const child = audioEl?.querySelector?.("source"); + if (child?.getAttribute?.("src")) return child.getAttribute("src"); + if (audioEl?.currentSrc) return audioEl.currentSrc; + return null; + }; + let audioSrc = pickAudioSrc(); + + const srcObj = player.src(); + const initialVideoSrc = Array.isArray(srcObj) ? (srcObj[0] && srcObj[0].src) : srcObj; + const initialVideoType = Array.isArray(srcObj) ? (srcObj[0] && srcObj[0].type) : undefined; + + // small state bag + let audioReady = false, videoReady = false; + let mediaSessionReady = false; + + // sync knobs + const BIG_DRIFT = 0.45; // snap threshold (s) + const MICRO_DRIFT = 0.035; // subtle rate nudge (s) + const EPS = 0.12; // buffer epsilon (s) + + // guards + let volGuard = false; + let syncGuard = false; // block re-entrant sync + let couplingGuard = false; // block play/pause echo + let autosyncTimer = null; + const AUTOSYNC_MS = 400; + + // seeking flags (so pausing audio for seeks won't pause the video) + let audioPauseForSeek = false; + let playerSeekWasPlaying = false; + + // loop detection + let lastVideoTime = 0; + + // helpers + function timeInBuffered(media, t) { try { - audioEl.controls = false; - audioEl.setAttribute('aria-hidden', 'true'); - audioEl.setAttribute('tabindex', '-1'); - audioEl.setAttribute('controlslist', 'noplaybackrate nodownload noremoteplayback'); - audioEl.disableRemotePlayback = true; - videoEl.setAttribute('playsinline', ''); - audioEl.setAttribute('playsinline', ''); - audioEl.preload = 'auto'; + const br = media.buffered; + if (!br || br.length === 0 || !isFinite(t)) return false; + for (let i = 0; i < br.length; i++) { + const s = br.start(i) - EPS, e = br.end(i) + EPS; + if (t >= s && t <= e) return true; + } + } catch {} + return false; + } + function canPlayAt(media, t) { + try { + const rs = Number(media.readyState || 0); + if (!isFinite(t)) return false; + if (rs >= 3) return true; // HAVE_FUTURE_DATA + return timeInBuffered(media, t); + } catch { return false; } + } + function bothPlayableAt(t) { return canPlayAt(videoEl, t) && canPlayAt(audioEl, t); } + function safeSetCT(media, t) { try { if (isFinite(t) && t >= 0) media.currentTime = t; } catch {} } + const clamp01 = v => Math.max(0, Math.min(1, Number(v))); + function isLooping() { try { return !!player.loop?.() || !!videoEl.loop; } catch { return !!videoEl.loop; } } + + // keep mute/volume mirrored either way + function mirrorFromPlayerVolumeMute() { + if (volGuard) return; volGuard = true; + try { + const m = !!player.muted(); + const v = clamp01(player.volume()); + audioEl.muted = m; + if (!m) audioEl.volume = v; + try { videoEl.muted = m; } catch {} + } catch {} + volGuard = false; + } + function mirrorFromAudioVolumeMute() { + if (volGuard) return; volGuard = true; + try { + const m = !!audioEl.muted; + const v = clamp01(audioEl.volume); + player.muted(m); + if (!m) player.volume(v); + try { videoEl.muted = m; } catch {} + } catch {} + volGuard = false; + } + player.on("volumechange", mirrorFromPlayerVolumeMute); + audioEl.addEventListener("volumechange", mirrorFromAudioVolumeMute); + player.ready(() => mirrorFromPlayerVolumeMute()); + + // one-button control helpers + function playBoth() { + if (couplingGuard) return; + couplingGuard = true; + player.play()?.catch(()=>{}); + audioEl.play()?.catch(()=>{}); + startAutosync(); + couplingGuard = false; + } + function pauseBoth() { + if (couplingGuard) return; + couplingGuard = true; + player.pause(); + audioEl.pause(); + stopAutosync(); + couplingGuard = false; + } + + // video is the boss: when it plays/pauses, audio mirrors + player.on("play", () => { + if (audioEl.paused) audioEl.play()?.catch(()=>{}); + startAutosync(); + try { if ("mediaSession" in navigator) navigator.mediaSession.playbackState = "playing"; } catch {} + }); + player.on("pause", () => { + if (!audioEl.paused) audioEl.pause(); + stopAutosync(); + try { if ("mediaSession" in navigator) navigator.mediaSession.playbackState = "paused"; } catch {} + }); + + // if the platform tries to play/pause audio alone, route through video + audioEl.addEventListener("play", () => { if (player.paused()) playBoth(); }); + audioEl.addEventListener("pause", () => { + if (audioPauseForSeek) return; // our deliberate pause (do not pause video) + if (!player.paused()) pauseBoth(); // any other pause → keep them in lockstep + }); + + // media-session timeline (keeps OS seekbar calm) + let lastMSPos = 0, lastMSAt = 0; + const MS_THROTTLE_MS = 250; + function getDuration() { + let d = Number(videoEl.duration); + if (!isFinite(d) || d <= 0) d = Number(player.duration()); + if (!isFinite(d) || d <= 0) d = Number(audioEl.duration); + return isFinite(d) && d > 0 ? d : null; + } + function updateMSPositionState(throttle = true) { + if (!("mediaSession" in navigator)) return; + const now = performance.now(); + if (throttle && (now - lastMSAt) < MS_THROTTLE_MS) return; + const dur = getDuration(); + if (!dur) return; + + let pos = Number(player.currentTime()); + if (!isFinite(pos) || pos < 0) pos = 0; + + // avoid flicker; allow jump-back only on real loop wrap + if (pos + 0.2 < lastMSPos && isLooping()) lastMSPos = 0; + else { + if (pos < lastMSPos && (lastMSPos - pos) < 0.2) pos = lastMSPos; + lastMSPos = pos; + } + lastMSAt = now; + + try { + if ("setPositionState" in navigator.mediaSession) { + navigator.mediaSession.setPositionState({ + duration: dur, + playbackRate: Number(player.playbackRate()) || 1, + position: Math.max(0, Math.min(dur, pos)), + }); + } + } catch {} + } + function setupMediaSession() { + if (mediaSessionReady) return; + if (!("mediaSession" in navigator)) return; + + try { + navigator.mediaSession.metadata = new MediaMetadata({ + title: document.title || "Video", + artist: "", + album: "", + artwork: [] + }); } catch {} - // resolve initial sources robustly (works whether