mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fix(portals): twenty-second review round on the cinematic details
- A Stalker launch that already started reports its failure even though the host's own inline teardown retired the request id. - An Xtream episode launch's bookkeeping applies only while the page still shows the series it started on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
6de998094a
commit
d71a92cec8
4 files changed
+48
-4
No files matched your search
@@ -227,6 +227,28 @@ describe('createStalkerVodDetailActions openExternal', () => {
|
||||
expect(t.logError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reports a failed launch although its own teardown retired the request', async () => {
|
||||
const t = setup(() => 42);
|
||||
// The host closes the inline player before the launch, bumping its
|
||||
// request id like a start would.
|
||||
t.beforeExternalLaunch.mockImplementation(() => t.supersede());
|
||||
t.openExternalPlayback.mockRejectedValue(new Error('no player'));
|
||||
const launch = t.actions.openExternal({
|
||||
item: MOVIE,
|
||||
player: 'mpv',
|
||||
positionSeconds: null,
|
||||
});
|
||||
t.resolveLink();
|
||||
await launch;
|
||||
|
||||
expect(t.logError).toHaveBeenCalledTimes(1);
|
||||
expect(t.open).toHaveBeenCalledWith(
|
||||
'PORTALS.PLAYBACK_ERROR',
|
||||
undefined,
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
it('drops the stream once another movie was selected meanwhile', async () => {
|
||||
let selected = 42;
|
||||
const t = setup(() => selected);
|
||||
|
||||
@@ -141,6 +141,10 @@ export function createStalkerVodDetailActions(
|
||||
deps.playlistId() === playlistId;
|
||||
const pending = deps.beginPendingStart?.();
|
||||
const superseded = () => pending?.isCurrent() === false;
|
||||
// Set once this launch is the one running: the host's inline
|
||||
// teardown before it retires the request id as well, which is
|
||||
// not a newer start.
|
||||
let launched = false;
|
||||
try {
|
||||
const playback = await deps.resolvePlayback(
|
||||
event.item.cmd,
|
||||
@@ -163,6 +167,7 @@ export function createStalkerVodDetailActions(
|
||||
return;
|
||||
}
|
||||
deps.beforeExternalLaunch?.();
|
||||
launched = true;
|
||||
await deps.portalPlayer.openExternalPlayback(
|
||||
playback,
|
||||
event.player
|
||||
@@ -170,7 +175,7 @@ export function createStalkerVodDetailActions(
|
||||
} catch (error) {
|
||||
// A launch a newer start superseded fails on its own; the
|
||||
// newer one reports for the movie now.
|
||||
if (!stillSelected() || superseded()) {
|
||||
if (!stillSelected() || (!launched && superseded())) {
|
||||
return;
|
||||
}
|
||||
deps.logError('External VOD playback failed', error);
|
||||
|
||||
+18
-3
@@ -21,6 +21,7 @@ import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { PlaybackPositionRuntimeBridgeService } from '@iptvnator/services';
|
||||
import {
|
||||
ExternalPlayerName,
|
||||
ExternalPlayerSession,
|
||||
PlaybackPositionData,
|
||||
PlayerContentInfo,
|
||||
ResolvedPortalPlayback,
|
||||
@@ -426,6 +427,18 @@ export class SerialDetailsPlaybackService {
|
||||
isEpisodeLaunchPending(this.launchOwner())
|
||||
);
|
||||
|
||||
/**
|
||||
* The launch's bookkeeping applies only while the page still shows the
|
||||
* series it started on; a session that lands after a navigation is not
|
||||
* written into the next series' position map.
|
||||
*/
|
||||
private ownedLaunch(launch: Promise<ExternalPlayerSession | void>) {
|
||||
const owner = this.launchOwner();
|
||||
return launch.then((session) =>
|
||||
this.launchOwner() === owner ? session : undefined
|
||||
);
|
||||
}
|
||||
|
||||
/** `playlist:series` of the page, null once it is gone or shows another series. */
|
||||
launchOwner(): string | null {
|
||||
const seriesId = this.selectedItem()?.series_id;
|
||||
@@ -447,9 +460,11 @@ export class SerialDetailsPlaybackService {
|
||||
this.closeInlinePlayer();
|
||||
void this.playbackPositionState.recordExternalLaunch(
|
||||
playback,
|
||||
player
|
||||
? openEpisodeExternally(this, playback, player)
|
||||
: this.portalPlayer.openResolvedPlayback(playback, true),
|
||||
this.ownedLaunch(
|
||||
player
|
||||
? openEpisodeExternally(this, playback, player)
|
||||
: this.portalPlayer.openResolvedPlayback(playback, true)
|
||||
),
|
||||
this.savePosition
|
||||
);
|
||||
}
|
||||
|
||||
@@ -674,6 +674,8 @@ describe('SerialDetailsComponent', () => {
|
||||
season: 2,
|
||||
} as never);
|
||||
await fixture.whenStable();
|
||||
// One more hop: the launch's owner check runs before the save.
|
||||
await Promise.resolve();
|
||||
|
||||
expect(savePlaybackPosition).toHaveBeenCalledWith(
|
||||
'xtream-1',
|
||||
|
||||
Reference in new issue
Block a user