Update css/player-base.js
This commit is contained in:
parent
3a4737518f
commit
c879b83a77
@ -15,8 +15,7 @@ var versionclient = "youtube.player.web_20250917_22_RC00"
|
|||||||
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
|
* <https://github.com/mozilla/vtt.js/blob/main/LICENSE>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
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
|
// 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', {
|
const video = videojs('video', {
|
||||||
controls: true,
|
controls: true,
|
||||||
@ -109,6 +108,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
let userMutedVideo = false;
|
let userMutedVideo = false;
|
||||||
let userMutedAudio = false;
|
let userMutedAudio = false;
|
||||||
|
|
||||||
|
// remember if user tried to play while sources weren’t ready/playable
|
||||||
|
let pendingUserPlay = false;
|
||||||
|
|
||||||
|
// prevent quick “waiting” blips from pausing everything at startup
|
||||||
|
let waitingTimerVideo = null;
|
||||||
|
let waitingTimerAudio = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
videoEl.loop = false;
|
videoEl.loop = false;
|
||||||
videoEl.removeAttribute?.('loop');
|
videoEl.removeAttribute?.('loop');
|
||||||
@ -226,6 +232,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
// if video is playing but audio silently stopped, try to nudge it
|
||||||
|
try {
|
||||||
|
if (!video.paused() && audio.paused && bothPlayableAt(vt)) {
|
||||||
|
audio.play().catch(() => {});
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
}, SYNC_INTERVAL_MS);
|
}, SYNC_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +259,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isReadyForStart() {
|
||||||
|
const t = Number(video.currentTime());
|
||||||
|
return audioReady && videoReady && isFinite(t) && bothPlayableAt(t);
|
||||||
|
}
|
||||||
|
|
||||||
async function tryPlay(el) {
|
async function tryPlay(el) {
|
||||||
try {
|
try {
|
||||||
const p = el.play();
|
const p = el.play();
|
||||||
@ -259,9 +276,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
|
|
||||||
async function playTogether({ allowMutedRetry = true } = {}) {
|
async function playTogether({ allowMutedRetry = true } = {}) {
|
||||||
if (syncing || restarting) return;
|
if (syncing || restarting) return;
|
||||||
|
const t = Number(video.currentTime());
|
||||||
|
// do not start anything until both are genuinely playable at this timestamp
|
||||||
|
if (!isReadyForStart()) {
|
||||||
|
pendingUserPlay = true;
|
||||||
|
showError('Buffering… preparing audio and video');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
syncing = true;
|
syncing = true;
|
||||||
try {
|
try {
|
||||||
const t = Number(video.currentTime());
|
|
||||||
if (isFinite(t) && Math.abs(Number(audio.currentTime) - t) > 0.05) {
|
if (isFinite(t) && Math.abs(Number(audio.currentTime) - t) > 0.05) {
|
||||||
safeSetCT(audio, t);
|
safeSetCT(audio, t);
|
||||||
}
|
}
|
||||||
@ -286,7 +310,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
showError('Video failed to start.');
|
showError('Video failed to start.');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
video.play().catch(() => showError('Video retry failed.'));
|
video.play().catch(() => showError('Video retry failed.'));
|
||||||
}, 3000);
|
}, 1500);
|
||||||
});
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
@ -297,6 +321,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!syncInterval) startSyncLoop();
|
if (!syncInterval) startSyncLoop();
|
||||||
|
hideError();
|
||||||
|
pendingUserPlay = false;
|
||||||
} finally {
|
} finally {
|
||||||
syncing = false;
|
syncing = false;
|
||||||
}
|
}
|
||||||
@ -322,7 +348,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
errorBox.style.width = 'fit-content';
|
errorBox.style.width = 'fit-content';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideError() {
|
function hideError() {
|
||||||
if (errorBox) {
|
if (errorBox) {
|
||||||
errorBox.style.display = 'none';
|
errorBox.style.display = 'none';
|
||||||
@ -387,37 +412,54 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
function wireResilience(el, label) {
|
function wireResilience(el, label) {
|
||||||
try {
|
try {
|
||||||
el.addEventListener('waiting', () => {
|
el.addEventListener('waiting', () => {
|
||||||
showError(`${label} buffering…`);
|
// small debounce so instant, harmless "waiting" blips don't hard-pause UX
|
||||||
// pause both while buffering
|
const timer = label === 'Video' ? 'video' : 'audio';
|
||||||
try { video.pause(); } catch {}
|
const ref = timer === 'video' ? 'waitingTimerVideo' : 'waitingTimerAudio';
|
||||||
try { audio.pause(); } catch {}
|
if (ref === 'waitingTimerVideo') {
|
||||||
|
clearTimeout(waitingTimerVideo);
|
||||||
|
waitingTimerVideo = setTimeout(() => {
|
||||||
|
showError(`${label} buffering…`);
|
||||||
|
try { video.pause(); } catch {}
|
||||||
|
try { audio.pause(); } catch {}
|
||||||
|
pendingUserPlay = true;
|
||||||
|
}, 180);
|
||||||
|
} else {
|
||||||
|
clearTimeout(waitingTimerAudio);
|
||||||
|
waitingTimerAudio = setTimeout(() => {
|
||||||
|
showError(`${label} buffering…`);
|
||||||
|
try { video.pause(); } catch {}
|
||||||
|
try { audio.pause(); } catch {}
|
||||||
|
pendingUserPlay = true;
|
||||||
|
}, 180);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('stalled', () => {
|
el.addEventListener('stalled', () => {
|
||||||
showError(`${label} stalled`);
|
showError(`${label} stalled`);
|
||||||
try { video.pause(); } catch {}
|
try { video.pause(); } catch {}
|
||||||
try { audio.pause(); } catch {}
|
try { audio.pause(); } catch {}
|
||||||
|
pendingUserPlay = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('emptied', () => {
|
el.addEventListener('emptied', () => {
|
||||||
showError(`${label} source emptied`);
|
showError(`${label} source emptied`);
|
||||||
try { video.pause(); } catch {}
|
try { video.pause(); } catch {}
|
||||||
try { audio.pause(); } catch {}
|
try { audio.pause(); } catch {}
|
||||||
|
pendingUserPlay = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('error', () => {
|
el.addEventListener('error', () => {
|
||||||
showError(`${label} error`);
|
showError(`${label} error`);
|
||||||
try { video.pause(); } catch {}
|
try { video.pause(); } catch {}
|
||||||
try { audio.pause(); } catch {}
|
try { audio.pause(); } catch {}
|
||||||
|
pendingUserPlay = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('canplay', () => {
|
el.addEventListener('canplay', () => {
|
||||||
// when it becomes playable again, hide error and try resume
|
// playable again: hide messages and resume if user wanted to play
|
||||||
hideError();
|
hideError();
|
||||||
if (!video.paused() && !audio.paused()) {
|
if (pendingUserPlay && isReadyForStart()) {
|
||||||
// already playing
|
playTogether({ allowMutedRetry: true });
|
||||||
} else {
|
|
||||||
// auto resume
|
|
||||||
const t = Number(video.currentTime());
|
|
||||||
if (bothPlayableAt(t)) {
|
|
||||||
playTogether({ allowMutedRetry: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch {}
|
} catch {}
|
||||||
@ -447,7 +489,12 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
safeSetCT(audio, t);
|
safeSetCT(audio, t);
|
||||||
}
|
}
|
||||||
if (bothPlayableAt(t)) playTogether({ allowMutedRetry: true });
|
if (bothPlayableAt(t)) playTogether({ allowMutedRetry: true });
|
||||||
else pauseTogether();
|
else {
|
||||||
|
// prevent audio from starting first then pausing — block until both ok
|
||||||
|
pendingUserPlay = true;
|
||||||
|
showError('Buffering… preparing audio and video');
|
||||||
|
pauseTogether();
|
||||||
|
}
|
||||||
setupMediaSession();
|
setupMediaSession();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -455,15 +502,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
attachRetry(audio, pickAudioSrc, () => { audioReady = true; });
|
attachRetry(audio, pickAudioSrc, () => { audioReady = true; });
|
||||||
attachRetry(videoEl, () => videoSrc, () => { videoReady = true; });
|
attachRetry(videoEl, () => videoSrc, () => { videoReady = true; });
|
||||||
|
|
||||||
|
// manual mute handling kept
|
||||||
video.on('volumechange', () => {
|
video.on('volumechange', () => {
|
||||||
try {
|
try {
|
||||||
const isMuted = video.muted();
|
const isMuted = video.muted();
|
||||||
if (isMuted !== userMutedVideo) {
|
if (isMuted !== userMutedVideo) {
|
||||||
userMutedVideo = isMuted;
|
userMutedVideo = isMuted;
|
||||||
}
|
}
|
||||||
if (!isMuted && !userMutedVideo) {
|
|
||||||
audio.muted = false;
|
|
||||||
}
|
|
||||||
if (audio) {
|
if (audio) {
|
||||||
audio.muted = isMuted;
|
audio.muted = isMuted;
|
||||||
}
|
}
|
||||||
@ -473,6 +518,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
} catch {}
|
} catch {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if the audio UI is exposed and user toggles it
|
||||||
audio.addEventListener('volumechange', () => {
|
audio.addEventListener('volumechange', () => {
|
||||||
try {
|
try {
|
||||||
const isMuted = audio.muted;
|
const isMuted = audio.muted;
|
||||||
@ -489,12 +535,28 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
try { audio.playbackRate = video.playbackRate(); } catch {}
|
try { audio.playbackRate = video.playbackRate(); } catch {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// do not allow one to run without the other
|
||||||
video.on('play', () => {
|
video.on('play', () => {
|
||||||
vIsPlaying = true;
|
vIsPlaying = true;
|
||||||
|
const t = Number(video.currentTime());
|
||||||
|
if (!isReadyForStart()) {
|
||||||
|
pendingUserPlay = true;
|
||||||
|
showError('Buffering… preparing audio and video');
|
||||||
|
pauseTogether();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!aIsPlaying) playTogether();
|
if (!aIsPlaying) playTogether();
|
||||||
});
|
});
|
||||||
|
|
||||||
audio.addEventListener('play', () => {
|
audio.addEventListener('play', () => {
|
||||||
aIsPlaying = true;
|
aIsPlaying = true;
|
||||||
|
const t = Number(video.currentTime());
|
||||||
|
if (!isReadyForStart()) {
|
||||||
|
// stop audio from racing ahead then pausing
|
||||||
|
try { audio.pause(); } catch {}
|
||||||
|
pendingUserPlay = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!vIsPlaying) playTogether();
|
if (!vIsPlaying) playTogether();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -507,10 +569,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
audio.addEventListener('pause', () => {
|
audio.addEventListener('pause', () => {
|
||||||
aIsPlaying = false;
|
aIsPlaying = false;
|
||||||
if (!restarting) {
|
if (!restarting) {
|
||||||
try { video.pause(); } catch {}
|
// if video is still playing, try a gentle re-play for audio; otherwise keep both paused
|
||||||
|
if (!video.paused()) {
|
||||||
|
audio.play().catch(() => {});
|
||||||
|
} else {
|
||||||
|
try { video.pause(); } catch {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// small vs large seeks: small seeks auto-continue, large seeks pause+resync
|
||||||
let wasPlayingBeforeSeek = false;
|
let wasPlayingBeforeSeek = false;
|
||||||
let lastSeekTime = 0;
|
let lastSeekTime = 0;
|
||||||
video.on('seeking', () => {
|
video.on('seeking', () => {
|
||||||
@ -524,7 +592,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
const newTime = Number(video.currentTime());
|
const newTime = Number(video.currentTime());
|
||||||
const seekDiff = Math.abs(newTime - lastSeekTime);
|
const seekDiff = Math.abs(newTime - lastSeekTime);
|
||||||
const dur = Number(video.duration()) || 60;
|
const dur = Number(video.duration()) || 60;
|
||||||
const threshold = Math.max(5, Math.min(dur * 0.66, 60));
|
|
||||||
|
// small seek limit ≈ max(0.75s, 5% of duration up to 4s)
|
||||||
|
const smallSeekLimit = Math.min(4, Math.max(0.75, dur * 0.05));
|
||||||
|
const bigSeekLimit = Math.max(5, Math.min(dur * 0.66, 60));
|
||||||
|
|
||||||
if (!firstSeekDone) {
|
if (!firstSeekDone) {
|
||||||
safeSetCT(audio, newTime);
|
safeSetCT(audio, newTime);
|
||||||
@ -532,16 +603,23 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seekDiff > threshold) {
|
if (seekDiff > bigSeekLimit) {
|
||||||
pauseTogether();
|
pauseTogether();
|
||||||
safeSetCT(audio, newTime);
|
safeSetCT(audio, newTime);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (wasPlayingBeforeSeek && bothPlayableAt(newTime)) {
|
if (wasPlayingBeforeSeek && bothPlayableAt(newTime)) {
|
||||||
playTogether({ allowMutedRetry: true });
|
playTogether({ allowMutedRetry: true });
|
||||||
}
|
}
|
||||||
}, 180);
|
}, 150);
|
||||||
} else {
|
} else {
|
||||||
safeSetCT(audio, newTime);
|
safeSetCT(audio, newTime);
|
||||||
|
if (seekDiff <= smallSeekLimit && wasPlayingBeforeSeek) {
|
||||||
|
if (bothPlayableAt(newTime)) playTogether({ allowMutedRetry: true });
|
||||||
|
else {
|
||||||
|
pendingUserPlay = true;
|
||||||
|
showError('Buffering…');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -552,6 +630,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
wireResilience(videoEl, 'Video');
|
wireResilience(videoEl, 'Video');
|
||||||
wireResilience(audio, 'Audio');
|
wireResilience(audio, 'Audio');
|
||||||
|
|
||||||
|
// looping restarts properly
|
||||||
|
// doesnt work LOOOOOOOOL
|
||||||
|
// sooo... I guess, TODO: fix the looping??????
|
||||||
async function restartLoop() {
|
async function restartLoop() {
|
||||||
if (restarting) return;
|
if (restarting) return;
|
||||||
restarting = true;
|
restarting = true;
|
||||||
@ -568,6 +649,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// okay, this actually, legit, not working idk why guuuh
|
||||||
video.on('ended', () => {
|
video.on('ended', () => {
|
||||||
if (restarting) return;
|
if (restarting) return;
|
||||||
if (performance.now() < suppressEndedUntil) return;
|
if (performance.now() < suppressEndedUntil) return;
|
||||||
@ -582,14 +664,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
videoEl.addEventListener('canplay', () => {
|
videoEl.addEventListener('canplay', () => {
|
||||||
if (!video.paused() && !audio.paused()) return;
|
if (pendingUserPlay && isReadyForStart()) {
|
||||||
const t = Number(video.currentTime());
|
playTogether({ allowMutedRetry: true });
|
||||||
if (bothPlayableAt(t)) playTogether({ allowMutedRetry: true });
|
}
|
||||||
});
|
});
|
||||||
audio.addEventListener('canplay', () => {
|
audio.addEventListener('canplay', () => {
|
||||||
if (!video.paused() && !audio.paused()) return;
|
if (pendingUserPlay && isReadyForStart()) {
|
||||||
const t = Number(video.currentTime());
|
playTogether({ allowMutedRetry: true });
|
||||||
if (bothPlayableAt(t)) playTogether({ allowMutedRetry: true });
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -613,7 +695,6 @@ 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