fix: blocked autoplay left 'Loading…' parked over unreachable controls

The overlay only clears on 'playing', which never fires when the browser
blocks autoplay — and the overlay covers the native controls, so the
'swap to the native play button' assumption from the click-to-play
removal never held. A rejected v.play() is the signal: swap the dead
Loading… overlay for a tappable one (Play), and a second rejection
escalates to the Playback failed overlay.

Browser test fakes the policy (reject first gesture-less play()) since
headless Chromium allows autoplay. Logs showed the shape of it: six
loads of the same ep, 38-40 segments each, all 200, player never started.
This commit is contained in:
jozamudi committed 2026-08-24 02:51:05 +00:00
1 parent aae5857dba
commit db715be54b
2 files changed
+57 -5

No files matched your search

+10 -5
View File
@@ -369,14 +369,14 @@ function stopHls() { if (hls) { hls.destroy(); hls = null; } }
let currentEp = null, lastSavedPos = 0, playerBroken = false;
function playerOverlay(text, onRetry) {
function playerOverlay(text, onAction, actionLabel = "Retry") {
const p = document.getElementById("player");
let o = p.querySelector(".pl-overlay");
if (!o) { o = el("div", "pl-overlay"); p.append(o); }
o.replaceChildren(el("span", null, text));
if (onRetry) {
const b = el("button", "btn", "Retry");
b.onclick = onRetry;
if (onAction) {
const b = el("button", "btn", actionLabel);
b.onclick = onAction;
o.append(b);
}
}
@@ -465,7 +465,12 @@ async function play(ep_id, ep_no) {
if (currentEp && !playerBroken && !v.ended) playerOverlay("Buffering\u2026");
});
v.addEventListener("ended", onEnded);
v.play().catch(() => {}); // autoplay blocked -> user uses the native play button
// The overlay only clears on 'playing', so a blocked autoplay would leave
// "Loading…" parked over the (unreachable) native controls forever — the
// rejected play() is the signal to swap it for a tappable one.
v.play().catch(() => playerOverlay("Tap to play", () => {
v.play().catch(() => playerFail("playback blocked by the browser"));
}, "Play"));
watchPos(v);
postHistory({...meta(v), title: detail.name, slug_id: detail.slug_id, season_id: seasonId,
ep_id, lang, ep: ep_no, pos: saved?.pos || 0});