diff --git a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html
index 54aeee093..1b8a9b2cc 100644
--- a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html
+++ b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html
@@ -27,6 +27,7 @@
@if (activeChannel(); as activeChannel) {
+ @let playbackTarget = playbackChannel();
@if (activeChannel.radio === 'true' || shouldShowInlinePlayer(activeChannel)) {
@if (activeChannel.radio === 'true') {
@@ -42,8 +43,8 @@
sources: [
{
src:
- activeChannel.url +
- activeChannel.epgParams,
+ playbackTarget?.url +
+ playbackTarget?.epgParams,
type: 'application/x-mpegURL',
},
],
@@ -54,14 +55,14 @@
}
@if (playerSettings.player === 'html5') {
}
@if (playerSettings.player === 'artplayer') {
@@ -73,7 +74,9 @@
@if (activeChannel.radio !== 'true') {
}
diff --git a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.spec.ts b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.spec.ts
index 1f76c6f2d..13d74fe37 100644
--- a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.spec.ts
+++ b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.spec.ts
@@ -10,6 +10,7 @@ import { BehaviorSubject, of } from 'rxjs';
import {
ChannelActions,
selectActive,
+ selectActivePlaybackUrl,
selectChannels,
selectCurrentEpgProgram,
} from 'm3u-state';
@@ -34,6 +35,7 @@ describe('VideoPlayerComponent', () => {
const playlistId = signal('playlist-1');
const activeChannel = signal
(null);
+ const activePlaybackUrl = signal(null);
const channels = signal([]);
const currentEpgProgram = signal(null);
@@ -66,6 +68,8 @@ describe('VideoPlayerComponent', () => {
switch (selector) {
case selectActive:
return activeChannel;
+ case selectActivePlaybackUrl:
+ return activePlaybackUrl;
case selectChannels:
return channels;
case selectCurrentEpgProgram:
@@ -143,6 +147,7 @@ describe('VideoPlayerComponent', () => {
localStorage.removeItem('m3u-sidebar-width');
player.set(VideoPlayer.VideoJs);
showCaptions.set(false);
+ activePlaybackUrl.set(null);
currentEpgProgram.set(null);
currentEpgProgram$.next(null);
overlayMock.create.mockClear();
@@ -286,6 +291,25 @@ describe('VideoPlayerComponent', () => {
).not.toBeNull();
});
+ it('uses the active playback override url when archive playback is active', () => {
+ syncStoreState(sampleChannel);
+ player.set(VideoPlayer.VideoJs);
+ activePlaybackUrl.set(
+ 'http://localhost/archive.m3u8?utc=123&lutc=456'
+ );
+
+ fixture.detectChanges();
+
+ expect(component.playbackChannel()?.url).toBe(
+ 'http://localhost/archive.m3u8?utc=123&lutc=456'
+ );
+
+ activePlaybackUrl.set(null);
+ fixture.detectChanges();
+
+ expect(component.playbackChannel()?.url).toBe(sampleChannel.url);
+ });
+
it('updates the outer sidebar width while grouped view requests a larger total width', () => {
fixture.detectChanges();
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 9bf871527..7e573627f 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
@@ -18,6 +18,7 @@ import { Store } from '@ngrx/store';
import { StorageMap } from '@ngx-pwa/local-storage';
import { TranslatePipe } from '@ngx-translate/core';
import { ResizableDirective } from 'components';
+import { isM3uCatchupPlaybackSupported } from 'm3u-utils';
import { PlaylistContextFacade } from '@iptvnator/playlist/shared/util';
import {
COMPONENT_OVERLAY_REF,
@@ -28,6 +29,7 @@ import {
ChannelActions,
PlaylistActions,
selectActive,
+ selectActivePlaybackUrl,
selectChannels,
selectCurrentEpgProgram,
} from 'm3u-state';
@@ -114,8 +116,31 @@ export class VideoPlayerComponent implements OnInit, OnDestroy {
/** Active selected channel */
readonly activeChannel = this.store.selectSignal(selectActive);
+ readonly activePlaybackUrl = this.store.selectSignal(
+ selectActivePlaybackUrl
+ );
readonly activePlaylistId = this.playlistContext.resolvedPlaylistId;
readonly channels = this.store.selectSignal(selectChannels);
+ readonly archivePlaybackAvailable = computed(() =>
+ isM3uCatchupPlaybackSupported(this.activeChannel())
+ );
+ readonly playbackChannel = computed(() => {
+ const activeChannel = this.activeChannel();
+ if (!activeChannel) {
+ return null;
+ }
+
+ const playbackUrl = this.activePlaybackUrl();
+ if (!playbackUrl) {
+ return activeChannel;
+ }
+
+ return {
+ ...activeChannel,
+ url: playbackUrl,
+ epgParams: '',
+ } as Channel;
+ });
readonly sidebarStorageKey = computed(() =>
this.activeView() === 'groups'
? M3U_GROUPS_SIDEBAR_STORAGE_KEY
diff --git a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.html b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.html
index 59369bd04..84568718c 100644
--- a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.html
+++ b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.html
@@ -112,6 +112,9 @@
} @else {
diff --git a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.ts b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.ts
index 3c5214ed6..369f8cc88 100644
--- a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.ts
+++ b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-tab.component.ts
@@ -12,6 +12,7 @@ import {
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { isM3uCatchupPlaybackSupported } from 'm3u-utils';
import {
matchesOpenLiveCollectionItem,
OpenLiveCollectionItemState,
@@ -106,6 +107,9 @@ export class UnifiedLiveTabComponent {
return detail.channel ?? null;
});
+ readonly currentM3uArchivePlaybackAvailable = computed(() =>
+ isM3uCatchupPlaybackSupported(this.currentM3uChannel())
+ );
readonly activeRadioChannel = computed(() => {
const channel = this.currentM3uChannel();
return channel?.radio === 'true' ? channel : null;
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 60b6106f0..e8bd8c2e9 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
@@ -107,6 +107,9 @@
} @else if (!isElectron && epgItems().length > 0) {