Update css/player-base.js

This commit is contained in:
ashley 2025-08-26 17:47:32 +02:00
parent 1b94a8286e
commit bf1efba130

View File

@ -228,12 +228,18 @@ document.addEventListener("DOMContentLoaded", () => {
}
});
// ** VIDEO.JS AUTO-RETRY (ONLY WHEN REALLY STUCK) **
// Tight, nearly invisible retries. We ignore CODE:4 unless we previously had playable media or
// the browser reports it can play the current type. We also avoid retries if playback is healthy.
const VJS_RETRY_STEPS_MS = [300, 600, 900, 1200, 1600, 2000, 2600, 3200];
// ** VIDEO.JS AUTO-RETRY (30s GRACE + ONLY WHEN REALLY BROKEN) **
// We completely ignore all tech errors for the first 30s after the stream actually starts (play or loadeddata).
// After 30s, retries stay invisible and only trigger if playback is genuinely stuck (no advancement, bad readyState, or real net/decode error).
const VJS_RETRY_STEPS_MS = [250, 400, 650, 900, 1200, 1600, 2000, 2600]; // tight, subtle backoff
let vjsRetryCount = 0;
let allowRetries = false; // becomes true only after grace window
let graceTimerStarted = false;
let graceTimerId = null;
// tiny watchdog state
let watch = { t: 0, at: 0, active: false };
const WATCH_GRACE_MS = 2200; // if no time advance for ~2.2s while "playing" post-grace → retry
function currentVideoSrc() {
const s = video.src();
@ -248,27 +254,45 @@ document.addEventListener("DOMContentLoaded", () => {
function isPlaybackHealthy() {
try {
if (!video.paused() && video.currentTime() > 0) return true;
if (video.readyState && video.readyState() >= 2 && video.duration() > 0) return true;
if (!isNaN(video.duration()) && video.duration() > 0) return true;
if (typeof video.readyState === 'function') {
if (video.readyState() >= 2 && video.duration() > 0) return true;
}
if (!isNaN(video.duration()) && video.duration() > 0 && video.currentTime() > 0) return true;
} catch {}
return false;
}
// only retry when truly transient: stalled/waiting with no progress for a bit
// start 30s grace on first real start signal
function startGraceIfNeeded() {
if (graceTimerStarted) return;
graceTimerStarted = true;
graceTimerId = setTimeout(() => {
// after 30s, only enable retries if we are not healthy
allowRetries = true;
if (!isPlaybackHealthy()) {
scheduleVideoRetry('post-30s-initial');
}
}, 30000);
}
video.one('loadeddata', startGraceIfNeeded);
video.one('play', startGraceIfNeeded);
// only retry when truly broken, and only after grace
function scheduleVideoRetry(reason) {
if (isPlaybackHealthy()) {
if (!allowRetries) return; // do nothing inside 30s grace
if (isPlaybackHealthy()) { // if at any point we look fine, reset and stop
vjsRetryCount = 0;
return;
}
const step = Math.min(vjsRetryCount, VJS_RETRY_STEPS_MS.length - 1);
const delay = VJS_RETRY_STEPS_MS[step];
vjsRetryCount++;
const keepTime = video.currentTime();
// keep it quiet in console; comment out next line to silence even this:
// console.warn(`[vjs-retry] #${vjsRetryCount} in ${delay}ms (${reason})`);
// pause & clear sync while we refetch
// pause & clear sync while we refetch (quiet, no UI flicker)
video.pause();
audio.pause();
clearSyncLoop();
@ -277,7 +301,6 @@ document.addEventListener("DOMContentLoaded", () => {
const srcUrl = currentVideoSrc() || videoSrc;
const srcType = currentVideoType();
// Only re-request the same src; do not flip formats here to avoid flashes.
if (srcType) video.src({ src: srcUrl, type: srcType });
else video.src(srcUrl);
@ -295,8 +318,7 @@ document.addEventListener("DOMContentLoaded", () => {
}, delay);
}
// watchdog: tiny grace; if time does not advance while “playing”, do a fast retry
const WATCH_GRACE_MS = 2500;
// watchdog: only active after grace; if time does not advance while “playing”, do a fast retry
function startWatchdog() {
watch.active = true;
watch.t = video.currentTime();
@ -305,11 +327,12 @@ document.addEventListener("DOMContentLoaded", () => {
function stopWatchdog() {
watch.active = false;
}
video.on('playing', () => { startWatchdog(); vjsRetryCount = 0; });
video.on('playing', () => { startWatchdog(); if (allowRetries) vjsRetryCount = 0; });
video.on('pause', () => { stopWatchdog(); });
video.on('waiting', () => { startWatchdog(); });
video.on('timeupdate', () => {
if (!watch.active) return;
if (!allowRetries || !watch.active) return;
const ct = video.currentTime();
if (ct !== watch.t) {
watch.t = ct;
@ -322,7 +345,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
});
// error filter: ignore CODE:4 unless we have a reason to believe its transient/false-positive
// error gating: ignore everything until grace ends; after that, only retry if truly broken
function browserThinksPlayable() {
try {
const type = currentVideoType();
@ -335,21 +358,19 @@ document.addEventListener("DOMContentLoaded", () => {
}
function shouldRetryForError(err) {
if (!allowRetries) return false; // never react during grace window
if (!err) return false;
// If playback is healthy, ignore any error surfaced by the tech.
if (isPlaybackHealthy()) return false;
// HTML5 codes: 1=aborted, 2=network, 3=decode, 4=src not supported (often noisy)
// HTML5 codes: 1=aborted, 2=network, 3=decode, 4=src not supported (noisy)
if (err.code === 2 || err.code === 3) return true;
// For code 4, only retry if we already had data or browser says it can play this type
if (err.code === 4) {
if (videoReady || browserThinksPlayable()) return true;
return false; // real "not supported" → don't loop
return false; // real "not supported" → do not loop
}
// message-based fallback
const msg = (err.message || '').toLowerCase();
if (
msg.includes('network error') ||
@ -360,28 +381,31 @@ document.addEventListener("DOMContentLoaded", () => {
return false;
}
// main error hook (quiet + filtered)
// main error hook (gated by 30s)
video.on('error', () => {
const err = video.error && video.error();
if (shouldRetryForError(err)) {
scheduleVideoRetry('error');
} else {
// console.debug('[vjs-retry] ignored error:', err);
}
});
// treat transient stalls/aborts as retryable, but only if not healthy
video.on('stalled', () => { if (!isPlaybackHealthy()) scheduleVideoRetry('stalled'); });
video.on('abort', () => { if (!isPlaybackHealthy()) scheduleVideoRetry('abort'); });
// treat transient stalls/aborts as retryable, but only after grace and only if not healthy
video.on('stalled', () => { if (allowRetries && !isPlaybackHealthy()) scheduleVideoRetry('stalled'); });
video.on('abort', () => { if (allowRetries && !isPlaybackHealthy()) scheduleVideoRetry('abort'); });
// whenever we really can play, reset retries so user never notices
video.on('canplay', () => { vjsRetryCount = 0; });
video.on('playing', () => { vjsRetryCount = 0; });
video.on('loadeddata', () => { vjsRetryCount = 0; });
// if we truly can play, reset counters; also cancel grace if were definitely healthy early
function markHealthy() {
vjsRetryCount = 0;
if (!allowRetries && isPlaybackHealthy() && graceTimerId) {
// were clearly fine; still let grace finish naturally, but nothing to do
}
}
video.on('canplay', markHealthy);
video.on('playing', markHealthy);
video.on('loadeddata', markHealthy);
}
});
// hai!! if ur asking why are they here - its for smth in the future!!!!!!
const FORMATS = {