fix(plex): a seek during a quiet reclaim is no longer undone

A quiet request leaves `live` alone, so a seek inside the old session's
range went native and the answer then jumped back to where it asked from.
`seek` now also goes by `pending`; the server-away retry had the same hole.

Comments that still named 404 for a lost session say 410, and the hls.js
retry comment now tells a finished playlist from a growing one.
This commit is contained in:
2026-11-03 21:10:51 +01:00
parent 469c8aa481
commit 3f6638bf96
6 changed files with 50 additions and 23 deletions
@@ -134,13 +134,13 @@ const lost404 = {
fatal: true,
type: Hls.ErrorTypes.NETWORK_ERROR,
details: "fragLoadError",
response: { code: 404, text: "No active playback session" },
response: { code: 404, text: "Not available yet" },
};
describe("an HLS session the server no longer has", () => {
describe("an HLS segment that never arrives (a fatal 404)", () => {
it("is restarted where the viewer is, instead of ending playback", async () => {
// The server reaps a session nobody fetches from (a long pause; a deploy). hls.js never retries
// a 4xx, so the next segment's 404 used to end playback with "couldn't play".
// Once hls.js has given up on it, the 404 used to end playback with "couldn't play". A session
// the server no longer has answers 410 instead: `hlsEngine.test.ts` holds that case.
const session = mount();
await waitFor(() => expect(hlsInstances).toHaveLength(1));
act(() => hlsInstances[0]!.emit(Hls.Events.MANIFEST_PARSED));
@@ -155,10 +155,9 @@ describe("an HLS session the server no longer has", () => {
it("gives up and says so when the reclaimed session keeps 404ing", async () => {
// `parsed` cannot bound this: it lives on ONE hls instance and every restart builds a fresh one,
// so a session that parses its manifest and then 404s re-arms the guard every cycle. The server
// also answers 404 for "that segment is not there YET" (a transcode falling behind realtime),
// which is indistinguishable on the wire — so the player respawned ffmpeg forever behind a
// spinner that never became an error.
// so a session that parses its manifest and then 404s re-arms the guard every cycle. A transcode
// falling behind realtime answers 404 for "that segment is not there YET" every time, so the
// player respawned ffmpeg forever behind a spinner that never became an error.
const session = mount();
const v = () => document.querySelector("video") as Media;
// The first session plus the two it is allowed to reclaim; the third 404 has nothing left.