mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 20:16:15 -08:00
fix(portals): one explicit Stalker launch at a time
A second "Open in external player" while the first create_link is still resolving is ignored, and the menu row is disabled for that window, so two resolutions cannot each start a player. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
175967e06b
commit
b47aeda543
5 files changed
+42
-1
No files matched your search
@@ -112,6 +112,25 @@ describe('createStalkerVodDetailActions openExternal', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores a second launch while the first is still resolving', async () => {
|
||||
const t = setup(() => 42);
|
||||
const first = t.actions.openExternal({
|
||||
item: MOVIE,
|
||||
player: 'mpv',
|
||||
positionSeconds: null,
|
||||
});
|
||||
await t.actions.openExternal({
|
||||
item: MOVIE,
|
||||
player: 'mpv',
|
||||
positionSeconds: null,
|
||||
});
|
||||
t.resolveLink();
|
||||
await first;
|
||||
|
||||
expect(t.beginPendingStart).toHaveBeenCalledTimes(1);
|
||||
expect(t.openExternalPlayback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('drops the stream once another movie was selected meanwhile', async () => {
|
||||
let selected = 42;
|
||||
const t = setup(() => selected);
|
||||
|
||||
@@ -71,6 +71,9 @@ 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;
|
||||
|
||||
return {
|
||||
/** The inline player copied the stream URL. */
|
||||
@@ -93,9 +96,10 @@ export function createStalkerVodDetailActions(
|
||||
},
|
||||
|
||||
async openExternal(event: StalkerVodExternalPlayEvent): Promise<void> {
|
||||
if (event.item.type !== 'stalker') {
|
||||
if (event.item.type !== 'stalker' || externalLaunchInFlight) {
|
||||
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.
|
||||
@@ -137,6 +141,7 @@ export function createStalkerVodDetailActions(
|
||||
notify('PORTALS.PLAYBACK_ERROR');
|
||||
}
|
||||
} finally {
|
||||
externalLaunchInFlight = false;
|
||||
settlePendingStart?.();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -125,6 +125,7 @@ export function createVodDetailsHeroState(deps: VodDetailsHeroStateDeps) {
|
||||
playbackActive:
|
||||
deps.externalState() !== 'idle' ||
|
||||
deps.playbackStartPending(),
|
||||
startPending: deps.playbackStartPending(),
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ const BASE = {
|
||||
hasPlaybackPosition: true,
|
||||
hasStoredProgress: true,
|
||||
playbackActive: false,
|
||||
startPending: false,
|
||||
};
|
||||
|
||||
function rows(input: Partial<typeof BASE>) {
|
||||
@@ -38,6 +39,18 @@ describe('buildVodMenuSections', () => {
|
||||
expect(reset?.disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('disables the external launch while a start still resolves', () => {
|
||||
const external = rows({ startPending: true }).find(
|
||||
(row) => row.id === VOD_DETAILS_MENU_ACTION.ExternalPlayer
|
||||
);
|
||||
expect(external?.disabled).toBe(true);
|
||||
expect(
|
||||
rows({}).find(
|
||||
(row) => row.id === VOD_DETAILS_MENU_ACTION.ExternalPlayer
|
||||
)?.disabled
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('disables the progress reset while playback owns the position', () => {
|
||||
// A running player, or a start still resolving its stream, would
|
||||
// write the position right back.
|
||||
|
||||
@@ -180,6 +180,8 @@ export function buildVodMenuSections(input: {
|
||||
hasStoredProgress: boolean;
|
||||
/** An external player owns the row: its next tick would undo a reset. */
|
||||
playbackActive: boolean;
|
||||
/** A start still resolving its stream: a second launch would double it. */
|
||||
startPending: boolean;
|
||||
}): VodMoreMenuSection[] {
|
||||
const sourceRows: VodMoreMenuSection['items'][number][] = [];
|
||||
if (input.externalPlayerAvailable) {
|
||||
@@ -188,6 +190,7 @@ export function buildVodMenuSections(input: {
|
||||
labelKey: 'PORTALS.DETAIL.OPEN_IN_EXTERNAL_PLAYER',
|
||||
icon: 'open_in_new',
|
||||
hint: input.externalPlayerHint,
|
||||
disabled: input.startPending,
|
||||
testId: 'vod-menu-external',
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user