mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 19:46:16 -08:00
fix(playback): release the radio override when a new selection mounts no player
Codex round-4 P2: after radio installed its credentials, selecting an item that never mounts a player surface (external video playback, failed resolution) left the old Cookie/Authorization installed — no WebPlayerViewComponent, close, or destroy cleanup runs on that path. Both radio hosts (unified collection tab and the Stalker live layout, which has the identical hole) now release the previously owned radio scope at the start of every new selection; the slot-ownership semantics keep this a no-op when another playback already owns the override. Regression test in the live-layout spec pins the failed-selection path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
02969dca26
commit
67e7161d65
3 files changed
+41
No files matched your search
+6
@@ -591,6 +591,12 @@ export class UnifiedLiveTabComponent {
|
||||
this.activeDetail.set(null);
|
||||
this.activeTimeshift.set(null);
|
||||
this.isSelecting.set(true);
|
||||
// A previously owned radio override must not survive into a
|
||||
// selection that never mounts a player surface of its own — external
|
||||
// video playback and failed resolutions would otherwise keep the old
|
||||
// radio credentials installed for that origin.
|
||||
this.streamHeaders.clear(this.radioHeaderScopeUrl);
|
||||
this.radioHeaderScopeUrl = null;
|
||||
|
||||
try {
|
||||
const detail =
|
||||
|
||||
+29
@@ -1214,6 +1214,35 @@ describe('StalkerLiveStreamLayoutComponent', () => {
|
||||
);
|
||||
expect(component.activePlayback()).toBeNull();
|
||||
});
|
||||
|
||||
it('releases the radio override when the next selection never mounts a player', async () => {
|
||||
// Switching from a playing radio to a channel whose resolution fails
|
||||
// (or plays externally) mounts no player surface — the selection
|
||||
// itself must release the previously installed credentials.
|
||||
stalkerStore.selectedContentType.set('radio');
|
||||
selectedCategoryId.set('radio-all');
|
||||
selectedItem.set(null);
|
||||
selectedItvId.set(undefined);
|
||||
fixture.detectChanges();
|
||||
resolveRadioPlayback.mockResolvedValue({
|
||||
streamUrl: 'http://portal.example/radio_2.mpg',
|
||||
title: 'Portal FM',
|
||||
headers: { Cookie: 'mac=00:1A:79:00:00:01' },
|
||||
});
|
||||
await component.playChannel(radioChannels()[0]);
|
||||
await fixture.whenStable();
|
||||
|
||||
stalkerStore.selectedContentType.set('itv');
|
||||
resolveItvPlayback.mockRejectedValueOnce(new Error('portal down'));
|
||||
await component.playChannel(itvChannels()[0]);
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(window.electron?.setUserAgent).toHaveBeenLastCalledWith(
|
||||
undefined,
|
||||
undefined,
|
||||
'http://portal.example/radio_2.mpg'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function buildProgram(channelId: string, title: string): EpgProgram {
|
||||
|
||||
+6
@@ -582,6 +582,12 @@ export class StalkerLiveStreamLayoutComponent implements OnDestroy {
|
||||
const channelId = normalizeStalkerEntityId(item.id);
|
||||
this.stalkerStore.setSelectedItem(item);
|
||||
this.ensureChannelWithinRenderWindow(channelId);
|
||||
// A previously owned radio override must not survive into a
|
||||
// selection that never mounts a player surface of its own — external
|
||||
// video playback and failed resolutions would otherwise keep the old
|
||||
// radio credentials installed for that origin.
|
||||
this.streamHeaders.clear(this.radioHeaderScopeUrl);
|
||||
this.radioHeaderScopeUrl = null;
|
||||
|
||||
try {
|
||||
const isRadioMode = this.isRadioMode();
|
||||
|
||||
Reference in new issue
Block a user