fix(plex): a lost HLS session is reclaimed at the first answer
The route answers 410 for a session it no longer has and keeps 404 for a segment not there yet. hls.js retries even a 4xx on a stream with one video variant, so the player waited about half a minute before asking again, and minutes in a throttled page.
This commit is contained in:
@@ -65,6 +65,27 @@ class FakeHls implements HlsPort {
|
||||
lost404() {
|
||||
this.lost(404);
|
||||
}
|
||||
/** A request answered "this session is gone". hls.js only reports it (not fatal) and retries for
|
||||
* half a minute: with one video variant it has nowhere else to go. */
|
||||
gone() {
|
||||
this.handlers.onError({
|
||||
fatal: false,
|
||||
network: true,
|
||||
status: 410,
|
||||
type: "networkError",
|
||||
details: "fragLoadError",
|
||||
});
|
||||
}
|
||||
/** hls.js reports a failed request it will retry by itself. */
|
||||
retrying(status: number) {
|
||||
this.handlers.onError({
|
||||
fatal: false,
|
||||
network: true,
|
||||
status,
|
||||
type: "networkError",
|
||||
details: "fragLoadError",
|
||||
});
|
||||
}
|
||||
/** hls.js gave up on a playlist or fragment with this answer (null: no answer at all). */
|
||||
lost(status: number | null) {
|
||||
this.handlers.onError({
|
||||
@@ -418,6 +439,57 @@ describe("a session the server no longer has (404)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("a session the server says is gone (410)", () => {
|
||||
async function playing() {
|
||||
const s = setup();
|
||||
s.engine.start("A", 600);
|
||||
await s.asks[0]!.resolve(hls("A", 600));
|
||||
s.hlses[0]!.handlers.onManifestParsed();
|
||||
s.media.fire("canplay");
|
||||
s.state.position = 1299;
|
||||
return s;
|
||||
}
|
||||
|
||||
it("is reclaimed at the first answer, not after hls.js's half minute of retries", async () => {
|
||||
const { asks, hlses } = await playing();
|
||||
hlses[0]!.gone();
|
||||
expect(asks[1]!.startAt).toBe(1299);
|
||||
});
|
||||
|
||||
it("keeps the running picture until the new session answers", async () => {
|
||||
const { engine, hlses } = await playing();
|
||||
expect(engine.getSnapshot().ready).toBe(true);
|
||||
hlses[0]!.gone();
|
||||
expect(engine.getSnapshot().ready).toBe(true);
|
||||
});
|
||||
|
||||
it("asks once, whatever the same instance still reports", async () => {
|
||||
const { asks, hlses } = await playing();
|
||||
hlses[0]!.gone();
|
||||
hlses[0]!.gone();
|
||||
hlses[0]!.lost(410);
|
||||
expect(asks).toHaveLength(2);
|
||||
});
|
||||
|
||||
it(`shares the reclaim budget, and says so after ${MAX_SESSION_RECLAIMS}`, async () => {
|
||||
const { engine, asks, hlses } = setup();
|
||||
engine.start("A", 600);
|
||||
for (let i = 0; i <= MAX_SESSION_RECLAIMS; i++) {
|
||||
await asks[i]!.resolve(hls("A", 600));
|
||||
hlses[i]!.gone();
|
||||
}
|
||||
expect(asks).toHaveLength(MAX_SESSION_RECLAIMS + 1);
|
||||
expect(engine.getSnapshot().error).toBe("generic");
|
||||
});
|
||||
|
||||
it("leaves a segment that is merely not there yet to hls.js's retries", async () => {
|
||||
const { engine, asks, hlses } = await playing();
|
||||
hlses[0]!.retrying(404);
|
||||
expect(asks).toHaveLength(1);
|
||||
expect(engine.getSnapshot().error).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("a server that is restarting (a deploy)", () => {
|
||||
// The gateway answers 502 while the app restarts, and nothing at all may answer (status 0): the
|
||||
// stream is not broken, the server is away. The buffer keeps playing meanwhile.
|
||||
|
||||
Reference in New Issue
Block a user