fix: only surface tap-to-play on NotAllowedError (autoplay block)

The first version reacted to any play() rejection, which broke the
stream-failure path: with a dead stream play() rejects with AbortError
(no data), clobbering the hls.js 'Playback failed / Retry' overlay and
stealing the user's one click. Scope the swap to the autoplay policy
rejection; everything else stays with the fatal-error path.

test_stream_failure_retry caught it; test_autoplay_blocked_shows_tap_to_play
still passes (simulates NotAllowedError on the first gesture-less play).
This commit is contained in:
jozamudi committed 2026-08-24 02:54:40 +00:00
1 parent db715be54b
commit 3f8c387a48
1 file changed
+10 -5
+10 -5
View File
@@ -465,12 +465,17 @@ async function play(ep_id, ep_no) {
if (currentEp && !playerBroken && !v.ended) playerOverlay("Buffering\u2026"); if (currentEp && !playerBroken && !v.ended) playerOverlay("Buffering\u2026");
}); });
v.addEventListener("ended", onEnded); v.addEventListener("ended", onEnded);
// The overlay only clears on 'playing', so a blocked autoplay would leave // NotAllowedError = the browser blocked autoplay: 'playing' will never
// "Loading…" parked over the (unreachable) native controls forever — the // fire, so the Loading… overlay (which only clears on 'playing') would
// rejected play() is the signal to swap it for a tappable one. // sit forever over the unreachable native controls. Other rejection
v.play().catch(() => playerOverlay("Tap to play", () => { // reasons (AbortError when the stream has no data yet, …) belong to the
// hls.js fatal-error path — leave its overlay alone.
v.play().catch(err => {
if (!err || err.name !== "NotAllowedError") return;
playerOverlay("Tap to play", () => {
v.play().catch(() => playerFail("playback blocked by the browser")); v.play().catch(() => playerFail("playback blocked by the browser"));
}, "Play")); }, "Play");
});
watchPos(v); watchPos(v);
postHistory({...meta(v), title: detail.name, slug_id: detail.slug_id, season_id: seasonId, postHistory({...meta(v), title: detail.name, slug_id: detail.slug_id, season_id: seasonId,
ep_id, lang, ep: ep_no, pos: saved?.pos || 0}); ep_id, lang, ep: ep_no, pos: saved?.pos || 0});