test something
This commit is contained in:
parent
006d561f80
commit
418bd19804
@ -11,7 +11,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
errorDisplay: false,
|
errorDisplay: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const qs = new URLSearchParams(location.search);
|
// quick params + lightweight progress seed
|
||||||
|
const qs = new URLSearchParams(location.search);
|
||||||
const qua = qs.get("quality") || "";
|
const qua = qs.get("quality") || "";
|
||||||
const vidKey = qs.get("v");
|
const vidKey = qs.get("v");
|
||||||
try { if (vidKey) localStorage.setItem(`progress-${vidKey}`, 0); } catch {}
|
try { if (vidKey) localStorage.setItem(`progress-${vidKey}`, 0); } catch {}
|
||||||
@ -63,10 +64,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
let autosyncTimer = null;
|
let autosyncTimer = null;
|
||||||
const AUTOSYNC_MS = 400;
|
const AUTOSYNC_MS = 400;
|
||||||
|
|
||||||
// seeking flags (so pausing audio for seeks won't pause the video)
|
// seeking flags
|
||||||
let audioPauseForSeek = false;
|
let audioPauseForSeek = false;
|
||||||
let playerSeekWasPlaying = false;
|
let playerSeekWasPlaying = false;
|
||||||
|
|
||||||
|
// buffer-aware resume flag
|
||||||
|
let resumeAfterBuffer = false;
|
||||||
|
|
||||||
// loop detection
|
// loop detection
|
||||||
let lastVideoTime = 0;
|
let lastVideoTime = 0;
|
||||||
|
|
||||||
@ -95,6 +99,24 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const clamp01 = v => Math.max(0, Math.min(1, Number(v)));
|
const clamp01 = v => Math.max(0, Math.min(1, Number(v)));
|
||||||
function isLooping() { try { return !!player.loop?.() || !!videoEl.loop; } catch { return !!videoEl.loop; } }
|
function isLooping() { try { return !!player.loop?.() || !!videoEl.loop; } catch { return !!videoEl.loop; } }
|
||||||
|
|
||||||
|
function isSpinnerVisible() {
|
||||||
|
try {
|
||||||
|
if (player.hasClass("vjs-waiting")) return true;
|
||||||
|
const sp = player.el().querySelector(".vjs-loading-spinner");
|
||||||
|
if (!sp) return false;
|
||||||
|
const st = window.getComputedStyle(sp);
|
||||||
|
const hidden = st.display === "none" || st.visibility === "hidden" || Number(st.opacity) === 0;
|
||||||
|
return !hidden;
|
||||||
|
} catch { return false; }
|
||||||
|
}
|
||||||
|
function isBufferingNow() {
|
||||||
|
try {
|
||||||
|
const t = Number(player.currentTime());
|
||||||
|
const lowReady = (videoEl.readyState || 0) < 3 || (audioEl.readyState || 0) < 3;
|
||||||
|
return isSpinnerVisible() || lowReady || !bothPlayableAt(t);
|
||||||
|
} catch { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
// keep mute/volume mirrored either way
|
// keep mute/volume mirrored either way
|
||||||
function mirrorFromPlayerVolumeMute() {
|
function mirrorFromPlayerVolumeMute() {
|
||||||
if (volGuard) return; volGuard = true;
|
if (volGuard) return; volGuard = true;
|
||||||
@ -122,9 +144,20 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
audioEl.addEventListener("volumechange", mirrorFromAudioVolumeMute);
|
audioEl.addEventListener("volumechange", mirrorFromAudioVolumeMute);
|
||||||
player.ready(() => mirrorFromPlayerVolumeMute());
|
player.ready(() => mirrorFromPlayerVolumeMute());
|
||||||
|
|
||||||
// one-button control helpers
|
// one-button control helpers (buffer-aware)
|
||||||
|
function tryResumeAfterBuffer() {
|
||||||
|
if (!resumeAfterBuffer) return;
|
||||||
|
if (!isBufferingNow()) {
|
||||||
|
resumeAfterBuffer = false;
|
||||||
|
playBoth();
|
||||||
|
}
|
||||||
|
}
|
||||||
function playBoth() {
|
function playBoth() {
|
||||||
if (couplingGuard) return;
|
if (couplingGuard) return;
|
||||||
|
if (isBufferingNow()) { // if loading bar/spinner is up, do NOT play anything
|
||||||
|
resumeAfterBuffer = true; // remember to resume later
|
||||||
|
return;
|
||||||
|
}
|
||||||
couplingGuard = true;
|
couplingGuard = true;
|
||||||
player.play()?.catch(()=>{});
|
player.play()?.catch(()=>{});
|
||||||
audioEl.play()?.catch(()=>{});
|
audioEl.play()?.catch(()=>{});
|
||||||
@ -138,10 +171,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
audioEl.pause();
|
audioEl.pause();
|
||||||
stopAutosync();
|
stopAutosync();
|
||||||
couplingGuard = false;
|
couplingGuard = false;
|
||||||
|
resumeAfterBuffer = false; // cancel pending resume when user paused
|
||||||
}
|
}
|
||||||
|
|
||||||
// video is the boss: when it plays/pauses, audio mirrors
|
// video is the boss: when it plays/pauses, audio mirrors (buffer-aware)
|
||||||
player.on("play", () => {
|
player.on("play", () => {
|
||||||
|
if (isBufferingNow()) { resumeAfterBuffer = true; pauseBoth(); return; }
|
||||||
if (audioEl.paused) audioEl.play()?.catch(()=>{});
|
if (audioEl.paused) audioEl.play()?.catch(()=>{});
|
||||||
startAutosync();
|
startAutosync();
|
||||||
try { if ("mediaSession" in navigator) navigator.mediaSession.playbackState = "playing"; } catch {}
|
try { if ("mediaSession" in navigator) navigator.mediaSession.playbackState = "playing"; } catch {}
|
||||||
@ -155,8 +190,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
// if the platform tries to play/pause audio alone, route through video
|
// if the platform tries to play/pause audio alone, route through video
|
||||||
audioEl.addEventListener("play", () => { if (player.paused()) playBoth(); });
|
audioEl.addEventListener("play", () => { if (player.paused()) playBoth(); });
|
||||||
audioEl.addEventListener("pause", () => {
|
audioEl.addEventListener("pause", () => {
|
||||||
if (audioPauseForSeek) return; // our deliberate pause (do not pause video)
|
if (audioPauseForSeek) return;
|
||||||
if (!player.paused()) pauseBoth(); // any other pause → keep them in lockstep
|
if (!player.paused()) pauseBoth();
|
||||||
});
|
});
|
||||||
|
|
||||||
// media-session timeline (keeps OS seekbar calm)
|
// media-session timeline (keeps OS seekbar calm)
|
||||||
@ -178,7 +213,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
let pos = Number(player.currentTime());
|
let pos = Number(player.currentTime());
|
||||||
if (!isFinite(pos) || pos < 0) pos = 0;
|
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;
|
if (pos + 0.2 < lastMSPos && isLooping()) lastMSPos = 0;
|
||||||
else {
|
else {
|
||||||
if (pos < lastMSPos && (lastMSPos - pos) < 0.2) pos = lastMSPos;
|
if (pos < lastMSPos && (lastMSPos - pos) < 0.2) pos = lastMSPos;
|
||||||
@ -214,7 +248,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// IMPORTANT: when the OS seekbar is used, we pause the hidden audio first to kill the play/pause ping-pong, then restore state.
|
|
||||||
function msSeekTo(targetTime) {
|
function msSeekTo(targetTime) {
|
||||||
const wasPlaying = !player.paused();
|
const wasPlaying = !player.paused();
|
||||||
audioPauseForSeek = true;
|
audioPauseForSeek = true;
|
||||||
@ -242,13 +275,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
navigator.mediaSession.setActionHandler("seekbackward", ({ seekOffset }) => {
|
navigator.mediaSession.setActionHandler("seekbackward", ({ seekOffset }) => {
|
||||||
const skip = seekOffset || 10;
|
const skip = seekOffset || 10;
|
||||||
const to = Math.max(0, Number(player.currentTime()) - skip);
|
msSeekTo(Math.max(0, Number(player.currentTime()) - skip));
|
||||||
msSeekTo(to);
|
|
||||||
});
|
});
|
||||||
navigator.mediaSession.setActionHandler("seekforward", ({ seekOffset }) => {
|
navigator.mediaSession.setActionHandler("seekforward", ({ seekOffset }) => {
|
||||||
const skip = seekOffset || 10;
|
const skip = seekOffset || 10;
|
||||||
const to = Number(player.currentTime()) + skip;
|
msSeekTo(Number(player.currentTime()) + skip);
|
||||||
msSeekTo(to);
|
|
||||||
});
|
});
|
||||||
navigator.mediaSession.setActionHandler("seekto", ({ seekTime }) => {
|
navigator.mediaSession.setActionHandler("seekto", ({ seekTime }) => {
|
||||||
if (!isFinite(seekTime)) return;
|
if (!isFinite(seekTime)) return;
|
||||||
@ -299,6 +330,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const at = Number(audioEl.currentTime);
|
const at = Number(audioEl.currentTime);
|
||||||
if (!isFinite(vt) || !isFinite(at)) return;
|
if (!isFinite(vt) || !isFinite(at)) return;
|
||||||
|
|
||||||
|
// if spinner/loading bar is up or readiness is low, make sure nothing plays
|
||||||
|
if (isBufferingNow()) {
|
||||||
|
try { audioEl.pause(); } catch {}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// handle loop wrap cleanly
|
// handle loop wrap cleanly
|
||||||
if (vt + 0.02 < lastVideoTime && isLooping()) {
|
if (vt + 0.02 < lastVideoTime && isLooping()) {
|
||||||
safeSetCT(audioEl, vt);
|
safeSetCT(audioEl, vt);
|
||||||
@ -330,17 +367,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
player.on("timeupdate", doSyncOnce);
|
player.on("timeupdate", doSyncOnce);
|
||||||
|
|
||||||
// cheap autosync in case timeupdate slows (background/lockscreen)
|
// cheap autosync in case timeupdate slows (background/lockscreen)
|
||||||
function startAutosync() {
|
function startAutosync() { if (!autosyncTimer) autosyncTimer = setInterval(doSyncOnce, AUTOSYNC_MS); }
|
||||||
if (autosyncTimer) return;
|
function stopAutosync() { if (autosyncTimer) { clearInterval(autosyncTimer); autosyncTimer = null; } }
|
||||||
autosyncTimer = setInterval(doSyncOnce, AUTOSYNC_MS);
|
|
||||||
}
|
|
||||||
function stopAutosync() {
|
|
||||||
if (!autosyncTimer) return;
|
|
||||||
clearInterval(autosyncTimer);
|
|
||||||
autosyncTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// when user drags the player's own seekbar: pause audio immediately, then restore if needed
|
// when user drags the player's own seekbar: pause audio instantly, then restore
|
||||||
player.on("seeking", () => {
|
player.on("seeking", () => {
|
||||||
syncGuard = true;
|
syncGuard = true;
|
||||||
playerSeekWasPlaying = !player.paused();
|
playerSeekWasPlaying = !player.paused();
|
||||||
@ -353,7 +383,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
player.on("seeked", () => {
|
player.on("seeked", () => {
|
||||||
safeSetCT(audioEl, Number(player.currentTime()));
|
safeSetCT(audioEl, Number(player.currentTime()));
|
||||||
syncGuard = false;
|
syncGuard = false;
|
||||||
if (playerSeekWasPlaying && bothPlayableAt(Number(player.currentTime()))) playBoth();
|
if (playerSeekWasPlaying && !isBufferingNow() && bothPlayableAt(Number(player.currentTime()))) playBoth();
|
||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -373,17 +403,23 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// light buffering handling; ignore spinner right at loop boundary
|
// buffering handling; if loading bar/spinner is up, keep both fully paused
|
||||||
player.on("waiting", () => {
|
function onBufferStart() {
|
||||||
const dur = Number(player.duration());
|
const wasPlaying = !player.paused();
|
||||||
const vt = Number(player.currentTime());
|
resumeAfterBuffer = wasPlaying || resumeAfterBuffer;
|
||||||
if (isLooping() && isFinite(dur) && isFinite(vt) && dur - vt < 0.25) return;
|
pauseBoth();
|
||||||
try { audioEl.pause(); } catch {}
|
}
|
||||||
});
|
function onBufferEnd() {
|
||||||
player.on("playing", () => {
|
tryResumeAfterBuffer();
|
||||||
if (!player.paused()) audioEl.play()?.catch(()=>{});
|
}
|
||||||
startAutosync();
|
player.on("waiting", onBufferStart);
|
||||||
});
|
player.on("stalled", onBufferStart);
|
||||||
|
player.on("loadstart", onBufferStart);
|
||||||
|
player.on("canplay", onBufferEnd);
|
||||||
|
player.on("canplaythrough", onBufferEnd);
|
||||||
|
player.on("playing", onBufferEnd);
|
||||||
|
audioEl.addEventListener("canplay", onBufferEnd);
|
||||||
|
audioEl.addEventListener("canplaythrough", onBufferEnd);
|
||||||
|
|
||||||
// simple error surfacing
|
// simple error surfacing
|
||||||
const errorBox = document.getElementById("loopedIndicator");
|
const errorBox = document.getElementById("loopedIndicator");
|
||||||
@ -456,7 +492,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
if (audioReady && videoReady) {
|
if (audioReady && videoReady) {
|
||||||
const t = Number(player.currentTime());
|
const t = Number(player.currentTime());
|
||||||
if (isFinite(t) && Math.abs(Number(audioEl.currentTime) - t) > 0.1) safeSetCT(audioEl, t);
|
if (isFinite(t) && Math.abs(Number(audioEl.currentTime) - t) > 0.1) safeSetCT(audioEl, t);
|
||||||
if (bothPlayableAt(t)) playBoth();
|
if (!isBufferingNow() && bothPlayableAt(t)) playBoth();
|
||||||
else { audioEl.pause(); player.pause(); }
|
else { audioEl.pause(); player.pause(); }
|
||||||
setupMediaSession();
|
setupMediaSession();
|
||||||
}
|
}
|
||||||
@ -476,7 +512,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const tryKick = () => {
|
const tryKick = () => {
|
||||||
if (audioReady && videoReady && player.paused()) {
|
if (audioReady && videoReady && player.paused()) {
|
||||||
const t = Number(player.currentTime());
|
const t = Number(player.currentTime());
|
||||||
if (bothPlayableAt(t)) playBoth();
|
if (!isBufferingNow() && bothPlayableAt(t)) playBoth();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
player.el().addEventListener("click", tryKick, { once: true });
|
player.el().addEventListener("click", tryKick, { once: true });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user