diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts index 6498e10b9..2ea9153b4 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts @@ -242,6 +242,24 @@ describe('VodDetailsPlaybackService — external session ownership', () => { closeSession.mockResolvedValue(undefined); }); + it('still starts the replacement when closing the old player fails', async () => { + activeSession.set(sessionFor(ROUTE_PLAYLIST, ROUTE_VOD_ID)); + closeSession.mockRejectedValue(new Error('close ipc failed')); + openResolvedPlayback.mockClear(); + + await service.startResolvedPlayback({ + streamUrl: 'https://example.com/alt.mkv', + title: 'Example Movie', + }); + + // The switch is already committed — the badge names the new source. + // Bailing out here left the page claiming a source with nothing + // started at all. + expect(openResolvedPlayback).toHaveBeenCalledTimes(1); + + closeSession.mockResolvedValue(undefined); + }); + it('launches only the newest source when two switches overlap', async () => { activeSession.set(sessionFor(ROUTE_PLAYLIST, ROUTE_VOD_ID)); // One shared promise: both calls see the same running session, so diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts index b1343e3d3..b496b7513 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts @@ -358,7 +358,19 @@ export class VodDetailsPlaybackService { this.matchedExternalPlayback() ); if (running) { - await this.externalPlayback.closeSession(running); + try { + await this.externalPlayback.closeSession(running); + } catch (error) { + // The switch is already committed — the badge names the new + // source. Giving up here would leave the page claiming a + // source with nothing started, which is worse than the + // pre-existing risk of a lingering process (and a close that + // rejects usually means the session was gone anyway). + this.logger.warn( + 'Closing the previous external player failed; starting the replacement anyway.', + error + ); + } } // Closing is a round-trip, and a second pick across it would otherwise