mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(portals): a Stalker movie launch superseded inside the IPC closes its player, and a reset holds the starts
After the launch IPC began, moving on to another movie or a newer start could no longer stop it, and the session it returned was dropped, so its player opened beside the new one; the session is kept and closed when the launch is no longer current. A progress reset now counts as a pending start of its movie, so Play, Start over and launches are held until the write landed instead of resuming from the row being cleared. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
ccac905ba0
commit
bfa04001c9
2 files changed
+66
-1
No files matched your search
@@ -196,6 +196,32 @@ describe('createStalkerVodDetailActions openExternal', () => {
|
||||
expect(t.openExternalPlayback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('closes the player a launch opened after the page moved on', async () => {
|
||||
let selected = 42;
|
||||
const t = setup(() => selected);
|
||||
const opened = {
|
||||
id: 'mpv-7',
|
||||
status: 'launching',
|
||||
} as ExternalPlayerSession;
|
||||
t.openExternalPlayback.mockResolvedValue(opened);
|
||||
const launch = t.actions.openExternal({
|
||||
item: MOVIE,
|
||||
player: 'mpv',
|
||||
positionSeconds: null,
|
||||
});
|
||||
t.resolveLink();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
// The launch is inside the player IPC when the viewer opens another
|
||||
// movie: its player must not open beside that one.
|
||||
selected = 43;
|
||||
await launch;
|
||||
|
||||
expect(t.openExternalPlayback).toHaveBeenCalledTimes(1);
|
||||
expect(t.closeSession).toHaveBeenCalledWith(opened);
|
||||
expect(t.open).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('drops the launch once a newer start superseded it', async () => {
|
||||
const t = setup(() => 42);
|
||||
const launch = t.actions.openExternal({
|
||||
@@ -301,6 +327,12 @@ describe('createStalkerVodDetailActions resetProgress', () => {
|
||||
const discardPendingPositionLoad = jest.fn();
|
||||
const afterProgressReset = jest.fn();
|
||||
const open = jest.fn();
|
||||
const settlePendingStart = jest.fn();
|
||||
const beginPendingStart = jest.fn().mockReturnValue({
|
||||
settle: settlePendingStart,
|
||||
isCurrent: () => true,
|
||||
rebase: jest.fn(),
|
||||
});
|
||||
const actions = createStalkerVodDetailActions({
|
||||
resolvePlayback: jest.fn(),
|
||||
portalPlayer: { openExternalPlayback: jest.fn() },
|
||||
@@ -313,6 +345,7 @@ describe('createStalkerVodDetailActions resetProgress', () => {
|
||||
selectedVodId,
|
||||
selectedVodPosition,
|
||||
discardPendingPositionLoad,
|
||||
beginPendingStart,
|
||||
afterProgressReset,
|
||||
snackBar: { open },
|
||||
translate: { instant: (key: string) => key },
|
||||
@@ -325,9 +358,29 @@ describe('createStalkerVodDetailActions resetProgress', () => {
|
||||
discardPendingPositionLoad,
|
||||
afterProgressReset,
|
||||
open,
|
||||
beginPendingStart,
|
||||
settlePendingStart,
|
||||
};
|
||||
}
|
||||
|
||||
it("holds the movie's starts until the clear landed", async () => {
|
||||
const t = setup(() => 42);
|
||||
let finishClear: () => void = () => undefined;
|
||||
t.clearPlaybackPositionOrThrow.mockImplementationOnce(
|
||||
() => new Promise<void>((resolve) => (finishClear = resolve))
|
||||
);
|
||||
|
||||
const reset = t.actions.resetProgress(MOVIE);
|
||||
// A start made now would resume from the row being cleared.
|
||||
expect(t.beginPendingStart).toHaveBeenCalledTimes(1);
|
||||
expect(t.settlePendingStart).not.toHaveBeenCalled();
|
||||
|
||||
finishClear();
|
||||
await reset;
|
||||
expect(t.settlePendingStart).toHaveBeenCalledTimes(1);
|
||||
expect(t.selectedVodPosition()).toBeNull();
|
||||
});
|
||||
|
||||
it('clears the shown position of the movie that is still selected', async () => {
|
||||
const t = setup(() => 42);
|
||||
await t.actions.resetProgress(MOVIE);
|
||||
|
||||
@@ -171,10 +171,16 @@ export function createStalkerVodDetailActions(
|
||||
// The host's inline teardown retired the request id too;
|
||||
// only a start made after this point supersedes the launch.
|
||||
pending?.rebase();
|
||||
await deps.portalPlayer.openExternalPlayback(
|
||||
const session = await deps.portalPlayer.openExternalPlayback(
|
||||
playback,
|
||||
event.player
|
||||
);
|
||||
// The page moved on, or a newer start took over, while the
|
||||
// launch sat inside the player IPC: the player it opened
|
||||
// must not stay beside what the viewer chose since.
|
||||
if (session && (!stillSelected() || superseded())) {
|
||||
await deps.externalPlayback.closeSession(session);
|
||||
}
|
||||
} catch (error) {
|
||||
// A launch a newer start superseded fails on its own; the
|
||||
// newer one reports for the movie now.
|
||||
@@ -195,6 +201,10 @@ export function createStalkerVodDetailActions(
|
||||
if (!playlistId || !Number.isFinite(vodId) || vodId <= 0) {
|
||||
return;
|
||||
}
|
||||
// Counted as a pending start of this movie: its Play, Start over
|
||||
// and launches are held until the write landed, or one made
|
||||
// meanwhile would resume from the very row being cleared.
|
||||
const pending = deps.beginPendingStart?.();
|
||||
try {
|
||||
await deps.playbackPositions.clearPlaybackPositionOrThrow(
|
||||
playlistId,
|
||||
@@ -204,6 +214,8 @@ export function createStalkerVodDetailActions(
|
||||
} catch (error) {
|
||||
deps.logError('Resetting the VOD position failed', error);
|
||||
return;
|
||||
} finally {
|
||||
pending?.settle();
|
||||
}
|
||||
// The clear was async: only the movie still on screen loses its
|
||||
// shown progress, and no older read may put it back.
|
||||
|
||||
Reference in new issue
Block a user