From a4251d881c82ffd893d83d010cd6daa19d32f72e Mon Sep 17 00:00:00 2001 From: 4gray Date: Wed, 1 Jul 2026 15:52:59 +0200 Subject: [PATCH] fix(m3u): use non-blank archive-days helper in the player MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `epgArchiveDays` used `tvg.rec ?? timeshift ?? catchup.days`, but `??` does not skip an empty string — the common `tvg-rec=""` default stopped the chain and yielded 0, which the timeline treats as an unbounded catch-up window, so a channel with a real `timeshift`/`catchup-days` could offer "Watch" on programmes older than its actual archive. Use the shared `getM3uArchiveDays` (non-blank + correct precedence), matching the unified live tab. (Codex P2) Co-Authored-By: Claude Opus 4.8 --- .../lib/video-player/video-player.component.ts | 17 ++++++++++------- .../m3u-utils/src/lib/catchup.utils.spec.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.ts b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.ts index 4999d4820..be43235a5 100644 --- a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.ts +++ b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.ts @@ -21,7 +21,10 @@ import { Store } from '@ngrx/store'; import { StorageMap } from '@ngx-pwa/local-storage'; import { TranslatePipe } from '@ngx-translate/core'; import { ResizableDirective } from '@iptvnator/ui/components'; -import { isM3uCatchupPlaybackSupported } from '@iptvnator/shared/m3u-utils'; +import { + getM3uArchiveDays, + isM3uCatchupPlaybackSupported, +} from '@iptvnator/shared/m3u-utils'; import { PlaylistContextFacade } from '@iptvnator/playlist/shared/util'; import { COMPONENT_OVERLAY_REF, @@ -171,12 +174,12 @@ export class VideoPlayerComponent implements OnInit, OnDestroy { readonly epgPrograms = toSignal(this.epgService.currentEpgPrograms$, { initialValue: [] as EpgProgram[], }); - readonly epgArchiveDays = computed(() => { - const channel = this.activeChannel(); - const value = - channel?.tvg?.rec ?? channel?.timeshift ?? channel?.catchup?.days; - return Math.max(0, Number(value ?? 0) || 0); - }); + // Shared helper skips blank strings (`tvg-rec=""` is a common default that + // `??` would not fall through), so a channel with only `timeshift`/ + // `catchup-days` still gets its real window instead of 0 (unbounded). + readonly epgArchiveDays = computed(() => + getM3uArchiveDays(this.activeChannel()) + ); readonly timelineChannelName = computed( () => this.activeChannel()?.name ?? '' ); diff --git a/libs/shared/m3u-utils/src/lib/catchup.utils.spec.ts b/libs/shared/m3u-utils/src/lib/catchup.utils.spec.ts index 19c9961df..676c415d0 100644 --- a/libs/shared/m3u-utils/src/lib/catchup.utils.spec.ts +++ b/libs/shared/m3u-utils/src/lib/catchup.utils.spec.ts @@ -66,6 +66,20 @@ describe('catchup.utils', () => { ).toBe(7); }); + it('skips a blank tvg-rec and uses a real timeshift value', () => { + // `tvg-rec=""` is a common parser default; a nullish (`??`) chain would + // stop on it and yield 0 (unbounded window). The non-blank helper skips + // it and returns the real timeshift days. + expect( + getM3uArchiveDays({ + ...baseChannel, + catchup: undefined, + timeshift: '7', + tvg: { ...baseChannel.tvg, rec: '' }, + }) + ).toBe(7); + }); + it('supports legacy same-stream shift playback when catchup type is shift', () => { expect(isM3uCatchupPlaybackSupported(baseChannel)).toBe(true); expect(resolveM3uCatchupUrl(baseChannel, archivedProgram, 1_775_820_000))