diff --git a/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.spec.ts index 3d40fae93..ee503898f 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.spec.ts @@ -131,6 +131,31 @@ describe('createStalkerVodDetailActions openExternal', () => { expect(t.openExternalPlayback).toHaveBeenCalledTimes(1); }); + it('still launches another movie while the first link resolves', async () => { + let selected = 42; + const t = setup(() => selected); + void t.actions.openExternal({ + item: MOVIE, + player: 'mpv', + positionSeconds: null, + }); + selected = 7; + const other = { + ...MOVIE, + data: { id: '7' }, + } as unknown as VodDetailsItem; + const second = t.actions.openExternal({ + item: other, + player: 'mpv', + positionSeconds: null, + }); + t.resolveLink(); + await second; + + expect(t.beginPendingStart).toHaveBeenCalledTimes(2); + expect(t.openExternalPlayback).toHaveBeenCalledTimes(1); + }); + it('drops the stream once another movie was selected meanwhile', async () => { let selected = 42; const t = setup(() => selected); diff --git a/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.ts b/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.ts index 2796f63c0..74bce669b 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-vod-detail-actions.ts @@ -71,9 +71,10 @@ export function createStalkerVodDetailActions( deps.snackBar.open(deps.translate.instant(key), undefined, { duration: 3000, }); - // A second "Open in external player" during a slow `create_link` would - // resolve beside the first and start a second player. - let externalLaunchInFlight = false; + // A second "Open in external player" for the SAME movie during a slow + // `create_link` would resolve beside the first and start a second + // player; another movie's launch is not held back by it. + let externalLaunchInFlight: string | null = null; return { /** The inline player copied the stream URL. */ @@ -96,15 +97,19 @@ export function createStalkerVodDetailActions( }, async openExternal(event: StalkerVodExternalPlayEvent): Promise { - if (event.item.type !== 'stalker' || externalLaunchInFlight) { + if (event.item.type !== 'stalker') { return; } - externalLaunchInFlight = true; // The `create_link` round trip may outlive the selection: a // stream resolved for a movie the user left is dropped, and its // failure is not reported over the new one. const playlistId = deps.playlistId(); const vodId = Number(event.item.data.id); + const launchKey = `${playlistId}:${vodId}`; + if (externalLaunchInFlight === launchKey) { + return; + } + externalLaunchInFlight = launchKey; const stillSelected = () => deps.selectedVodId() === vodId && deps.playlistId() === playlistId; @@ -141,7 +146,9 @@ export function createStalkerVodDetailActions( notify('PORTALS.PLAYBACK_ERROR'); } } finally { - externalLaunchInFlight = false; + if (externalLaunchInFlight === launchKey) { + externalLaunchInFlight = null; + } settlePendingStart?.(); } },