fix(m3u): use non-blank archive-days helper in the player

`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 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 4.8 committed 2026-07-01 15:52:59 +02:00
1 parent 23f20a1a30
commit a4251d881c
2 files changed
+24 -7

No files matched your search

@@ -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 ?? ''
);
@@ -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))