fix(player): a stale session answer can no longer stop the playback that won

- The engine tracks the request out per item: A, then B, then back to A sent a second request
  for A beside the one still out.
- A hardware transcode superseded during its fallback answered with the NEWER session; the losing
  request's player then stopped it as stale. It now answers with its own (dead) session.
- A replaced hls.js instance's late manifest no longer marks the player ready or plays the
  outgoing media.
- A pause given after the answer stands: the manifest and the first canplay read the live intent.
- The debounced A/V-offset restart is dropped on an item change instead of restarting the
  previous item under the next one's title.
This commit is contained in:
2026-10-07 23:16:25 +02:00
parent d25a132495
commit 0a35f2a3b1
6 changed files with 131 additions and 15 deletions
@@ -402,3 +402,40 @@ describe("usePlexTracks — a <track> whose own VTT fetch failed", () => {
expect(harness.current!.subNotice).toBeNull();
});
});
describe("usePlexTracks — the debounced A/V-offset restart", () => {
afterEach(() => vi.useRealTimers());
it("is dropped when the item changes before it fires — it would restart the previous item", () => {
vi.useFakeTimers();
const restart = vi.fn();
const qc = client();
const { result, rerender } = renderHook(
({ id }: { id: string }) =>
usePlexTracks({
id,
detail: detail(),
sessionK: 0,
subOffset: 0,
subPos: 88,
restartSession: (at) => restart(id, at),
setAudioTrack: () => false,
patchPrefs: () => {},
videoRef: createRef<HTMLVideoElement>(),
audioRef: { current: 0 } as RefObject<number | null>,
aoffRef: { current: 0 },
playhead: () => 600,
}),
{
wrapper: ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={qc}>{children}</QueryClientProvider>
),
initialProps: { id: RK },
},
);
act(() => result.current.changeAudioOffset(0.2));
rerender({ id: "next-episode" });
act(() => void vi.advanceTimersByTime(1000));
expect(restart).not.toHaveBeenCalled();
});
});