fix(plex): a playlist that never answers is asked for again; the slow line reads this stream
A playlist timeout was resumed in the same session, but hls.js reloads no manifest on startLoad: no request, no error, a spinner for good. It is now asked for again like a server away. An absence also ended only on a fragment, which native HLS never reports, and a viewer trying again after the error started with the spent window; both now start a fresh one. The slow-connection meter was reset only when hls.js was built, so a direct answer showed the previous stream's numbers, and a replaced session's late fragment still counted; its rates now use the stats overlay's units.
This commit is contained in:
@@ -718,6 +718,80 @@ describe("a link too slow for the stream", () => {
|
||||
expect(hlses[0]!.resumes).toBe(1);
|
||||
expect(asks).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("asks for the session again when its playlist never answered", async () => {
|
||||
// Loading cannot resume what never parsed: hls.js reloads no manifest on startLoad, so a resume
|
||||
// here sent nothing, raised nothing, and left the spinner up for good.
|
||||
const { engine, asks, hlses, fire } = setup();
|
||||
engine.start("A", 300);
|
||||
await asks[0]!.resolve(hls("A", 300));
|
||||
hlses[0]!.timedOut("manifestLoadTimeOut");
|
||||
expect(fire()).toBe(true);
|
||||
expect(asks).toHaveLength(2);
|
||||
expect(asks[1]!.startAt).toBe(300);
|
||||
await asks[1]!.resolve(hls("A", 300));
|
||||
hlses[1]!.handlers.onManifestParsed();
|
||||
expect(engine.getSnapshot()).toMatchObject({ ready: true, error: null });
|
||||
});
|
||||
|
||||
it("says it cannot play once the playlist has not answered for the whole window", async () => {
|
||||
const { engine, asks, hlses, fire, advance } = setup();
|
||||
engine.start("A", 0);
|
||||
await asks[0]!.resolve(hls("A"));
|
||||
hlses[0]!.timedOut("manifestLoadTimeOut");
|
||||
advance(AWAY_WINDOW_MS + 1);
|
||||
expect(fire()).toBe(true);
|
||||
await asks[1]!.resolve(hls("A"));
|
||||
hlses[1]!.timedOut("manifestLoadTimeOut");
|
||||
expect(engine.getSnapshot().error).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("what a fragment reports", () => {
|
||||
it("is passed on from the session asked for only, never from one it replaced", async () => {
|
||||
const seen: number[] = [];
|
||||
const { engine, asks, hlses } = setup({ onFragLoaded: (f) => void seen.push(f.bytes) });
|
||||
engine.start("A", 0);
|
||||
await asks[0]!.resolve(hls("A"));
|
||||
hlses[0]!.arrived();
|
||||
engine.seek("A", 2000);
|
||||
await asks[1]!.resolve(hls("A", 2000));
|
||||
hlses[0]!.arrived(); // the replaced session's last fragment lands late
|
||||
expect(seen).toEqual([1_000_000]);
|
||||
hlses[1]!.arrived();
|
||||
expect(seen).toEqual([1_000_000, 1_000_000]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("an absence when no hls.js plays the stream", () => {
|
||||
it("ends with an answer the browser plays itself (native HLS)", async () => {
|
||||
// Without MSE an HLS answer plays natively and no fragment is ever reported, so the answer is
|
||||
// the only sign the server is back: a later absence gets its own window.
|
||||
const { engine, asks, advance, fire } = setup({ createHls: () => null });
|
||||
engine.start("A", 0);
|
||||
await asks[0]!.reject({ status: 503 });
|
||||
expect(fire()).toBe(true);
|
||||
await asks[1]!.resolve(hls("A"));
|
||||
advance(AWAY_WINDOW_MS + 60_000);
|
||||
engine.seek("A", 900);
|
||||
await asks[2]!.reject({ status: 503 });
|
||||
expect(engine.getSnapshot().error).toBeNull();
|
||||
expect(fire()).toBe(true);
|
||||
});
|
||||
|
||||
it("starts afresh when the viewer tries again after the error was shown", async () => {
|
||||
const { engine, asks, advance, fire } = setup();
|
||||
engine.start("A", 0);
|
||||
await asks[0]!.reject({ status: 503 });
|
||||
advance(AWAY_WINDOW_MS + 1);
|
||||
expect(fire()).toBe(true);
|
||||
await asks[1]!.reject({ status: 503 });
|
||||
expect(engine.getSnapshot().error).toBe("generic");
|
||||
engine.start("A", 0); // the viewer presses play again
|
||||
await asks[2]!.reject({ status: 503 });
|
||||
expect(engine.getSnapshot().error).toBeNull();
|
||||
expect(fire()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("play intent while a session is asked for", () => {
|
||||
|
||||
Reference in New Issue
Block a user