mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
fix(portals): a failed close must not leave the page claiming a dead source
When `closeSession()` rejected, `startResolvedPlayback` rejected with it and never launched — while `switchToSource` had already marked the destination active and reported the switch as successful. The page then named a source that nothing was playing. The close failure is logged and the replacement starts anyway. A close that rejects usually means the session was already gone, and a possibly-lingering process is the lesser of the two evils: the alternative is a UI that lies about what is on screen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7c1b433d47
commit
caa89e3a6e
2 files changed
+31
-1
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user