From d43bc835315650ced942fed61d1c7ccdaaad10f6 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 27 Jun 2026 12:02:00 +0200 Subject: [PATCH] feat(epg): improve catchup row playback handling --- .../live-stream-layout.component.html | 1 + .../live-stream-layout.component.spec.ts | 41 ++++++++++++++++++ .../live-stream-layout.component.ts | 3 ++ .../epg-list-item.component.html | 1 + .../epg-list-item.component.scss | 13 ++++++ .../epg-list-item.component.spec.ts | 13 ++++++ .../epg-list-item/epg-list-item.component.ts | 3 ++ .../src/lib/epg-list/epg-list.component.html | 13 +++++- .../src/lib/epg-list/epg-list.component.scss | 42 +++++++++++++++++++ .../lib/epg-list/epg-list.component.spec.ts | 37 ++++++++++++++++ .../src/lib/epg-list/epg-list.component.ts | 23 ++++++++++ 11 files changed, 189 insertions(+), 1 deletion(-) diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html index 204ded13b..cc104d7f2 100644 --- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html +++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.html @@ -221,6 +221,7 @@ [controlledPrograms]="controlledEpgPrograms()" [controlledArchiveDays]="controlledArchiveDays()" [archivePlaybackAvailable]="archivePlaybackAvailable()" + [activeProgram]="activeCatchupProgram()" [selectedDate]=" unifiedDateNavigator ? selectedLiveEpgDate() : null " diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.spec.ts b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.spec.ts index 957698e8b..2b0183fe2 100644 --- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.spec.ts +++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.spec.ts @@ -102,6 +102,7 @@ class StubEpgListComponent { readonly controlledPrograms = input(null); readonly controlledArchiveDays = input(null); readonly archivePlaybackAvailable = input(null); + readonly activeProgram = input(null); readonly selectedDate = input(null); readonly showDateNavigator = input(true); readonly programActivated = output(); @@ -763,6 +764,46 @@ describe('LiveStreamLayoutComponent', () => { ); }); + it('passes the active catchup program to the EPG list until live playback resumes', async () => { + const archivedProgram: EpgProgram = { + start: '2026-04-04T10:00:00.000Z', + stop: '2026-04-04T11:00:00.000Z', + channel: 'channel-101', + title: 'Archived Show', + desc: null, + category: null, + startTimestamp: 1775296800, + stopTimestamp: 1775300400, + }; + epgItems.set([ + buildEpgItem( + '1', + 'Archived Show', + archivedProgram.start, + archivedProgram.stop + ), + ]); + + await component.onProgramActivated({ + type: 'timeshift', + program: archivedProgram, + }); + fixture.detectChanges(); + + let epgList = fixture.debugElement.query( + By.directive(StubEpgListComponent) + ); + expect(epgList.componentInstance.activeProgram()).toEqual( + archivedProgram + ); + + component.playLive(sampleChannel); + fixture.detectChanges(); + + epgList = fixture.debugElement.query(By.directive(StubEpgListComponent)); + expect(epgList.componentInstance.activeProgram()).toBeNull(); + }); + it('starts external playback from remote channel navigation when double-click opening is enabled', () => { const nextChannel = { ...sampleChannel, diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.ts b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.ts index 0a9dcde66..a7657337b 100644 --- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.ts +++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.component.ts @@ -268,6 +268,7 @@ export class LiveStreamLayoutComponent implements OnInit, OnDestroy { readonly activeStreamUrl = computed( () => this.activePlayback()?.streamUrl ?? '' ); + readonly activeCatchupProgram = signal(null); favorites = new Map(); constructor() { @@ -426,6 +427,7 @@ export class LiveStreamLayoutComponent implements OnInit, OnDestroy { startPlayback = !this.settingsStore.openStreamOnDoubleClick() ) { const streamUrl = this.xtreamStore.constructStreamUrl(item); + this.activeCatchupProgram.set(null); // Keep both root/recently-added playback and same-category replays in // sync with the category rail. For already-selected channels this is a // store no-op. @@ -633,6 +635,7 @@ export class LiveStreamLayoutComponent implements OnInit, OnDestroy { playlist.serverTimezone ); + this.activeCatchupProgram.set(program); this.activePlayback.set({ streamUrl: catchupUrl, title: this.getCatchupPlaybackTitle(item, program), diff --git a/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html b/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html index b6bbc3955..147733ab4 100644 --- a/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html +++ b/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html @@ -10,6 +10,7 @@ @if (showArchiveBadge) {