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) ** // ** VIDEO.JS AUTO-RETRY (30s GRACE + ONLY WHEN REALLY BROKEN) **
// Tight, nearly invisible retries. We ignore CODE:4 unless we previously had playable media or // We completely ignore all tech errors for the first 30s after the stream actually starts (play or loadeddata).
// the browser reports it can play the current type. We also avoid retries if playback is healthy. // 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 = [300, 600, 900, 1200, 1600, 2000, 2600, 3200]; const VJS_RETRY_STEPS_MS = [250, 400, 650, 900, 1200, 1600, 2000, 2600]; // tight, subtle backoff
let vjsRetryCount = 0; 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 }; 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() { function currentVideoSrc() {
const s = video.src(); const s = video.src();
@ -248,27 +254,45 @@ document.addEventListener("DOMContentLoaded", () => {
function isPlaybackHealthy() { function isPlaybackHealthy() {
try { try {
if (!video.paused() && video.currentTime() > 0) return true; if (!video.paused() && video.currentTime() > 0) return true;
if (video.readyState && video.readyState() >= 2 && video.duration() > 0) return true; if (typeof video.readyState === 'function') {
if (!isNaN(video.duration()) && video.duration() > 0) return true; if (video.readyState() >= 2 && video.duration() > 0) return true;
}
if (!isNaN(video.duration()) && video.duration() > 0 && video.currentTime() > 0) return true;
} catch {} } catch {}
return false; 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) { 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; vjsRetryCount = 0;
return; return;
} }
const step = Math.min(vjsRetryCount, VJS_RETRY_STEPS_MS.length - 1); const step = Math.min(vjsRetryCount, VJS_RETRY_STEPS_MS.length - 1);
const delay = VJS_RETRY_STEPS_MS[step]; const delay = VJS_RETRY_STEPS_MS[step];
vjsRetryCount++; vjsRetryCount++;
const keepTime = video.currentTime(); 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(); video.pause();
audio.pause(); audio.pause();
clearSyncLoop(); clearSyncLoop();
@ -277,7 +301,6 @@ document.addEventListener("DOMContentLoaded", () => {
const srcUrl = currentVideoSrc() || videoSrc; const srcUrl = currentVideoSrc() || videoSrc;
const srcType = currentVideoType(); 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 }); if (srcType) video.src({ src: srcUrl, type: srcType });
else video.src(srcUrl); else video.src(srcUrl);
@ -295,8 +318,7 @@ document.addEventListener("DOMContentLoaded", () => {
}, delay); }, delay);
} }
// watchdog: tiny grace; if time does not advance while “playing”, do a fast retry // watchdog: only active after grace; if time does not advance while “playing”, do a fast retry
const WATCH_GRACE_MS = 2500;
function startWatchdog() { function startWatchdog() {
watch.active = true; watch.active = true;
watch.t = video.currentTime(); watch.t = video.currentTime();
@ -305,11 +327,12 @@ document.addEventListener("DOMContentLoaded", () => {
function stopWatchdog() { function stopWatchdog() {
watch.active = false; watch.active = false;
} }
video.on('playing', () => { startWatchdog(); vjsRetryCount = 0; }); video.on('playing', () => { startWatchdog(); if (allowRetries) vjsRetryCount = 0; });
video.on('pause', () => { stopWatchdog(); }); video.on('pause', () => { stopWatchdog(); });
video.on('waiting', () => { startWatchdog(); }); video.on('waiting', () => { startWatchdog(); });
video.on('timeupdate', () => { video.on('timeupdate', () => {
if (!watch.active) return; if (!allowRetries || !watch.active) return;
const ct = video.currentTime(); const ct = video.currentTime();
if (ct !== watch.t) { if (ct !== watch.t) {
watch.t = ct; 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() { function browserThinksPlayable() {
try { try {
const type = currentVideoType(); const type = currentVideoType();
@ -335,21 +358,19 @@ document.addEventListener("DOMContentLoaded", () => {
} }
function shouldRetryForError(err) { function shouldRetryForError(err) {
if (!allowRetries) return false; // never react during grace window
if (!err) return false; if (!err) return false;
// If playback is healthy, ignore any error surfaced by the tech.
if (isPlaybackHealthy()) return false; 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; 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 // For code 4, only retry if we already had data or browser says it can play this type
if (err.code === 4) { if (err.code === 4) {
if (videoReady || browserThinksPlayable()) return true; 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(); const msg = (err.message || '').toLowerCase();
if ( if (
msg.includes('network error') || msg.includes('network error') ||
@ -360,28 +381,31 @@ document.addEventListener("DOMContentLoaded", () => {
return false; return false;
} }
// main error hook (quiet + filtered) // main error hook (gated by 30s)
video.on('error', () => { video.on('error', () => {
const err = video.error && video.error(); const err = video.error && video.error();
if (shouldRetryForError(err)) { if (shouldRetryForError(err)) {
scheduleVideoRetry('error'); scheduleVideoRetry('error');
} else {
// console.debug('[vjs-retry] ignored error:', err);
} }
}); });
// treat transient stalls/aborts as retryable, but only if not healthy // treat transient stalls/aborts as retryable, but only after grace and only if not healthy
video.on('stalled', () => { if (!isPlaybackHealthy()) scheduleVideoRetry('stalled'); }); video.on('stalled', () => { if (allowRetries && !isPlaybackHealthy()) scheduleVideoRetry('stalled'); });
video.on('abort', () => { if (!isPlaybackHealthy()) scheduleVideoRetry('abort'); }); video.on('abort', () => { if (allowRetries && !isPlaybackHealthy()) scheduleVideoRetry('abort'); });
// whenever we really can play, reset retries so user never notices // if we truly can play, reset counters; also cancel grace if were definitely healthy early
video.on('canplay', () => { vjsRetryCount = 0; }); function markHealthy() {
video.on('playing', () => { vjsRetryCount = 0; }); vjsRetryCount = 0;
video.on('loadeddata', () => { 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!!!!!! // hai!! if ur asking why are they here - its for smth in the future!!!!!!
const FORMATS = { const FORMATS = {