test something
This commit is contained in:
parent
d6e1b4592c
commit
d87ee1a55a
@ -2,7 +2,9 @@
|
|||||||
var _yt_player = videojs;
|
var _yt_player = videojs;
|
||||||
|
|
||||||
var versionclient = "youtube.player.web_20250917_22_RC00"
|
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
|
|
||||||
|
|
||||||
|
// 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", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const player = videojs('video', {
|
const player = videojs('video', {
|
||||||
controls: true,
|
controls: true,
|
||||||
@ -59,8 +61,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
|
|
||||||
// guards to prevent feedback loops / re-entrancy
|
// guards to prevent feedback loops / re-entrancy
|
||||||
let volGuard = false;
|
let volGuard = false;
|
||||||
let syncGuard = false; // prevents re-entrant sync work
|
let syncGuard = false; // prevents re-entrant sync work
|
||||||
let couplingGuard = false; // prevents play/pause echo loops
|
let couplingGuard = false; // prevents play/pause echo loops
|
||||||
|
|
||||||
|
// lightweight autosyncer (only when playing)
|
||||||
|
let autosyncTimer = null;
|
||||||
|
const AUTOSYNC_MS = 400;
|
||||||
|
|
||||||
// last time to detect wrap (loop)
|
// last time to detect wrap (loop)
|
||||||
let lastVideoTime = 0;
|
let lastVideoTime = 0;
|
||||||
@ -98,7 +104,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
try { return !!player.loop?.() || !!videoEl.loop; } catch { return !!videoEl.loop; }
|
try { return !!player.loop?.() || !!videoEl.loop; } catch { return !!videoEl.loop; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// === VOLUME / MUTE MIRROR (both ways) ===
|
|
||||||
function mirrorFromPlayerVolumeMute() {
|
function mirrorFromPlayerVolumeMute() {
|
||||||
if (volGuard) return; volGuard = true;
|
if (volGuard) return; volGuard = true;
|
||||||
try {
|
try {
|
||||||
@ -125,13 +130,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
audioEl.addEventListener('volumechange', mirrorFromAudioVolumeMute);
|
audioEl.addEventListener('volumechange', mirrorFromAudioVolumeMute);
|
||||||
player.ready(() => mirrorFromPlayerVolumeMute());
|
player.ready(() => mirrorFromPlayerVolumeMute());
|
||||||
|
|
||||||
// === MASTER/SLAVE COUPLING (Video.js is master; STRICT) ===
|
|
||||||
function playBoth() {
|
function playBoth() {
|
||||||
if (couplingGuard) return;
|
if (couplingGuard) return;
|
||||||
couplingGuard = true;
|
couplingGuard = true;
|
||||||
// start video first so it owns the session; audio follows immediately
|
// start video first so it owns the session; audio follows immediately
|
||||||
player.play()?.catch(()=>{});
|
player.play()?.catch(()=>{});
|
||||||
audioEl.play()?.catch(()=>{});
|
audioEl.play()?.catch(()=>{});
|
||||||
|
startAutosync();
|
||||||
couplingGuard = false;
|
couplingGuard = false;
|
||||||
}
|
}
|
||||||
function pauseBoth() {
|
function pauseBoth() {
|
||||||
@ -139,19 +144,19 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
couplingGuard = true;
|
couplingGuard = true;
|
||||||
player.pause();
|
player.pause();
|
||||||
audioEl.pause();
|
audioEl.pause();
|
||||||
|
stopAutosync();
|
||||||
couplingGuard = false;
|
couplingGuard = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// absolutely enforce: whenever video plays/pauses, audio mirrors (and vice-versa is blocked)
|
// absolutely enforce: whenever video plays/pauses, audio mirrors (and vice-versa is blocked)
|
||||||
player.on('play', () => {
|
player.on('play', () => {
|
||||||
// ensure audio follows a user/UI-initiated play
|
|
||||||
if (audioEl.paused) audioEl.play()?.catch(()=>{});
|
if (audioEl.paused) audioEl.play()?.catch(()=>{});
|
||||||
// mark mediaSession state
|
startAutosync();
|
||||||
try { if ('mediaSession' in navigator) navigator.mediaSession.playbackState = 'playing'; } catch {}
|
try { if ('mediaSession' in navigator) navigator.mediaSession.playbackState = 'playing'; } catch {}
|
||||||
});
|
});
|
||||||
player.on('pause', () => {
|
player.on('pause', () => {
|
||||||
// ensure audio pauses when video pauses
|
|
||||||
if (!audioEl.paused) audioEl.pause();
|
if (!audioEl.paused) audioEl.pause();
|
||||||
|
stopAutosync();
|
||||||
try { if ('mediaSession' in navigator) navigator.mediaSession.playbackState = 'paused'; } catch {}
|
try { if ('mediaSession' in navigator) navigator.mediaSession.playbackState = 'paused'; } catch {}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -163,7 +168,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
if (!player.paused()) pauseBoth();
|
if (!player.paused()) pauseBoth();
|
||||||
});
|
});
|
||||||
|
|
||||||
// === MEDIA SESSION (stable timeline + working seek) ===
|
|
||||||
let lastMSPos = 0;
|
let lastMSPos = 0;
|
||||||
let lastMSAt = 0;
|
let lastMSAt = 0;
|
||||||
const MS_THROTTLE_MS = 250;
|
const MS_THROTTLE_MS = 250;
|
||||||
@ -261,7 +265,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
if (!player.paused()) playBoth();
|
if (!player.paused()) playBoth();
|
||||||
});
|
});
|
||||||
|
|
||||||
// keep MS timeline sane but light
|
|
||||||
player.on('timeupdate', () => updateMSPositionState(true));
|
player.on('timeupdate', () => updateMSPositionState(true));
|
||||||
player.on('ratechange', () => updateMSPositionState(false));
|
player.on('ratechange', () => updateMSPositionState(false));
|
||||||
player.on('loadedmetadata', () => { lastMSPos = 0; updateMSPositionState(false); });
|
player.on('loadedmetadata', () => { lastMSPos = 0; updateMSPositionState(false); });
|
||||||
@ -274,7 +277,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
mediaSessionReady = true;
|
mediaSessionReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ** DESKTOP MEDIA-KEY FALLBACK **
|
|
||||||
document.addEventListener('keydown', e => {
|
document.addEventListener('keydown', e => {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case 'AudioPlay':
|
case 'AudioPlay':
|
||||||
@ -300,18 +302,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// === EVENT-DRIVEN SYNC (no timers / no rAF) ==============================
|
function doSyncOnce() {
|
||||||
// Video is master; we only adjust audio on video 'timeupdate'.
|
|
||||||
player.on('timeupdate', () => {
|
|
||||||
if (syncGuard) return;
|
if (syncGuard) return;
|
||||||
const vt = Number(player.currentTime());
|
const vt = Number(player.currentTime());
|
||||||
const at = Number(audioEl.currentTime);
|
const at = Number(audioEl.currentTime);
|
||||||
if (!isFinite(vt) || !isFinite(at)) return;
|
if (!isFinite(vt) || !isFinite(at)) return;
|
||||||
|
|
||||||
// detect wrap (loop) via time going backwards on the <video> element
|
// detect wrap (loop) via time going backwards
|
||||||
if (vt + 0.02 < lastVideoTime && isLooping()) {
|
if (vt + 0.02 < lastVideoTime && isLooping()) {
|
||||||
safeSetCT(audioEl, vt);
|
safeSetCT(audioEl, vt);
|
||||||
// keep both playing right across the wrap
|
|
||||||
if (player.paused()) playBoth();
|
if (player.paused()) playBoth();
|
||||||
lastMSPos = 0;
|
lastMSPos = 0;
|
||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
@ -324,23 +323,33 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
|
|
||||||
const delta = vt - at;
|
const delta = vt - at;
|
||||||
|
|
||||||
// large drift → snap audio
|
|
||||||
if (Math.abs(delta) > BIG_DRIFT) {
|
if (Math.abs(delta) > BIG_DRIFT) {
|
||||||
safeSetCT(audioEl, vt);
|
safeSetCT(audioEl, vt);
|
||||||
try { audioEl.playbackRate = 1; } catch {}
|
try { audioEl.playbackRate = 1; } catch {}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// micro drift → rate nudge on audio (low impact)
|
|
||||||
if (Math.abs(delta) > MICRO_DRIFT) {
|
if (Math.abs(delta) > MICRO_DRIFT) {
|
||||||
const target = 1 + (delta * 0.12);
|
const target = 1 + (delta * 0.12);
|
||||||
try { audioEl.playbackRate = Math.max(0.97, Math.min(1.03, target)); } catch {}
|
try { audioEl.playbackRate = Math.max(0.97, Math.min(1.03, target)); } catch {}
|
||||||
} else {
|
} else {
|
||||||
try { audioEl.playbackRate = 1; } catch {}
|
try { audioEl.playbackRate = 1; } catch {}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// primary, event-driven sync
|
||||||
|
player.on('timeupdate', doSyncOnce);
|
||||||
|
|
||||||
|
// low-cost autosyncer: covers cases when 'timeupdate' cadence drops (background, lockscreen)
|
||||||
|
function startAutosync() {
|
||||||
|
if (autosyncTimer) return;
|
||||||
|
autosyncTimer = setInterval(doSyncOnce, AUTOSYNC_MS);
|
||||||
|
}
|
||||||
|
function stopAutosync() {
|
||||||
|
if (!autosyncTimer) return;
|
||||||
|
clearInterval(autosyncTimer);
|
||||||
|
autosyncTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
// === SEEK COUPLING (video drives; audio follows) =========================
|
|
||||||
let wasPlayingBeforeSeek = false;
|
let wasPlayingBeforeSeek = false;
|
||||||
player.on('seeking', () => {
|
player.on('seeking', () => {
|
||||||
syncGuard = true;
|
syncGuard = true;
|
||||||
@ -355,19 +364,37 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// === BUFFERING HANDLING (gentle; skip right-before-loop) =================
|
// ALSO couple when the platform (or a buggy lockscreen) seeks the AUDIO directly
|
||||||
|
audioEl.addEventListener('seeking', () => {
|
||||||
|
// mirror audio → video (rare but fixes “seekbar only moves audio” cases)
|
||||||
|
const at = Number(audioEl.currentTime) || 0;
|
||||||
|
syncGuard = true;
|
||||||
|
player.currentTime(at);
|
||||||
|
syncGuard = false;
|
||||||
|
// keep play state consistent
|
||||||
|
if (!player.paused()) { try { audioEl.play(); } catch {} try { player.play(); } catch {} }
|
||||||
|
updateMSPositionState(false);
|
||||||
|
});
|
||||||
|
audioEl.addEventListener('seeked', () => {
|
||||||
|
const at = Number(audioEl.currentTime) || 0;
|
||||||
|
syncGuard = true;
|
||||||
|
player.currentTime(at);
|
||||||
|
syncGuard = false;
|
||||||
|
if (!player.paused()) { try { audioEl.play(); } catch {} try { player.play(); } catch {} }
|
||||||
|
updateMSPositionState(false);
|
||||||
|
});
|
||||||
|
|
||||||
player.on('waiting', () => {
|
player.on('waiting', () => {
|
||||||
const dur = Number(player.duration());
|
const dur = Number(player.duration());
|
||||||
const vt = Number(player.currentTime());
|
const vt = Number(player.currentTime());
|
||||||
if (isLooping() && isFinite(dur) && isFinite(vt) && dur - vt < 0.25) return; // ignore near-loop waits
|
if (isLooping() && isFinite(dur) && isFinite(vt) && dur - vt < 0.25) return; // ignore near-loop waits
|
||||||
// let Video.js show spinner; keep audio paused to avoid desync noise
|
|
||||||
audioEl.pause();
|
audioEl.pause();
|
||||||
});
|
});
|
||||||
player.on('playing', () => {
|
player.on('playing', () => {
|
||||||
if (!player.paused()) audioEl.play()?.catch(()=>{});
|
if (!player.paused()) audioEl.play()?.catch(()=>{});
|
||||||
|
startAutosync();
|
||||||
});
|
});
|
||||||
|
|
||||||
// === ERROR UI (ignore code 1) ============================================
|
|
||||||
const errorBox = document.getElementById('loopedIndicator');
|
const errorBox = document.getElementById('loopedIndicator');
|
||||||
player.on('error', () => {
|
player.on('error', () => {
|
||||||
const mediaError = player.error();
|
const mediaError = player.error();
|
||||||
@ -383,7 +410,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// === END / LOOP BEHAVIOR (force smooth loop) =============================
|
|
||||||
player.on('ended', () => {
|
player.on('ended', () => {
|
||||||
if (isLooping()) {
|
if (isLooping()) {
|
||||||
safeSetCT(audioEl, 0);
|
safeSetCT(audioEl, 0);
|
||||||
@ -393,6 +419,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
} else {
|
} else {
|
||||||
audioEl.pause();
|
audioEl.pause();
|
||||||
|
stopAutosync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
audioEl.addEventListener('ended', () => {
|
audioEl.addEventListener('ended', () => {
|
||||||
@ -404,6 +431,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
lastMSPos = 0;
|
lastMSPos = 0;
|
||||||
updateMSPositionState(false);
|
updateMSPositionState(false);
|
||||||
|
} else {
|
||||||
|
stopAutosync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -467,6 +496,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// https://codeberg.org/ashley/poke/src/branch/main/src/libpoketube/libpoketube-youtubei-objects.json
|
// https://codeberg.org/ashley/poke/src/branch/main/src/libpoketube/libpoketube-youtubei-objects.json
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user