diff --git a/src/app/portals/live-stream-layout/live-stream-layout.component.html b/src/app/portals/live-stream-layout/live-stream-layout.component.html
new file mode 100644
index 000000000..aebd99c8e
--- /dev/null
+++ b/src/app/portals/live-stream-layout/live-stream-layout.component.html
@@ -0,0 +1,40 @@
+
+
+ @if (player === 'videojs' || player === 'html5') {
+
+ @if (streamUrl) {
+
+ }
+
+ }
+
+
diff --git a/src/app/portals/live-stream-layout/live-stream-layout.component.scss b/src/app/portals/live-stream-layout/live-stream-layout.component.scss
new file mode 100644
index 000000000..7f6de82ab
--- /dev/null
+++ b/src/app/portals/live-stream-layout/live-stream-layout.component.scss
@@ -0,0 +1,47 @@
+:host {
+ width: 100%;
+}
+
+.itv-container {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ grid-template-rows: 1fr 1fr 1fr;
+ gap: 0 0;
+ grid-auto-flow: row;
+ grid-template-areas:
+ 'sidebar video-player video-player'
+ 'sidebar epg epg'
+ 'sidebar epg epg';
+ height: 100%;
+}
+
+.itv-container-epg {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ grid-template-rows: 1fr 1fr 1fr;
+ gap: 0 0;
+ grid-auto-flow: row;
+ grid-template-areas:
+ 'sidebar epg epg'
+ 'sidebar epg epg'
+ 'sidebar epg epg';
+ height: 100%;
+}
+
+.sidebar { grid-area: sidebar; }
+
+.video-player { grid-area: video-player; }
+
+.epg { grid-area: epg; }
+
+.epg-content {
+ height: 100%;
+ overflow-y: auto;
+ border-right: #111 1px solid;
+}
+
+.sidebar-content {
+ height: 100%;
+ overflow-y: auto;
+ border-right: #111 1px solid;
+}
diff --git a/src/app/portals/live-stream-layout/live-stream-layout.component.ts b/src/app/portals/live-stream-layout/live-stream-layout.component.ts
new file mode 100644
index 000000000..fc28cddc7
--- /dev/null
+++ b/src/app/portals/live-stream-layout/live-stream-layout.component.ts
@@ -0,0 +1,24 @@
+import { NgIf } from '@angular/common';
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { MatListModule } from '@angular/material/list';
+import { XtreamItem } from '../../../../shared/xtream-item.interface';
+import { VideoPlayer } from '../../settings/settings.interface';
+import { EpgItem } from '../../xtream/epg-item.interface';
+import { EpgViewComponent } from '../epg-view/epg-view.component';
+import { WebPlayerViewComponent } from '../web-player-view/web-player-view.component';
+
+@Component({
+ standalone: true,
+ selector: 'app-live-stream-layout',
+ templateUrl: './live-stream-layout.component.html',
+ styleUrls: ['./live-stream-layout.component.scss'],
+ imports: [EpgViewComponent, MatListModule, NgIf, WebPlayerViewComponent],
+})
+export class LiveStreamLayoutComponent {
+ @Input({ required: true }) channels: XtreamItem[];
+ @Input({ required: true }) player: VideoPlayer = VideoPlayer.VideoJs;
+ @Input() epgItems: EpgItem[];
+ @Input() streamUrl: string;
+
+ @Output() itemClicked = new EventEmitter();
+}
diff --git a/src/app/xtream/xtream-main-container.component.html b/src/app/xtream/xtream-main-container.component.html
index 577727dd3..b1cd40ce0 100644
--- a/src/app/xtream/xtream-main-container.component.html
+++ b/src/app/xtream/xtream-main-container.component.html
@@ -30,10 +30,20 @@
/>
}
@case ('category_content') {
-
+ @if (selectedContentType === 'itv') {
+
+ } @else {
+
+ }
}
@case ('vod-details') {
;
breadcrumbs: Breadcrumb[] = [];
items = [];
@@ -160,6 +173,8 @@ export class XtreamMainContainerComponent implements OnInit {
searchPhrase = this.portalStore.searchPhrase();
contentId: number;
errorViewInfo = { title: '', message: '' };
+ streamUrl: string;
+ epgItems = [];
commandsList = [
new IpcCommand(XTREAM_RESPONSE, (response: XtreamResponse) =>
@@ -233,6 +248,15 @@ export class XtreamMainContainerComponent implements OnInit {
this.currentLayout = 'serie-details';
this.vodDetails = response.payload as XtreamSerieDetails;
break;
+ case 'get_short_epg':
+ this.epgItems = (
+ (response.payload as any).epg_listings as EpgItem[]
+ ).map((i) => ({
+ ...i,
+ title: b64DecodeUnicode(i.title).trim(),
+ description: b64DecodeUnicode(i.description).trim(),
+ }));
+ break;
default:
break;
}
@@ -287,6 +311,11 @@ export class XtreamMainContainerComponent implements OnInit {
this.contentId = item.stream_id;
this.sendRequest({ action, vod_id: item.stream_id });
} else if (item.stream_type && item.stream_type === 'live') {
+ this.sendRequest({
+ action: 'get_short_epg',
+ stream_id: item.stream_id,
+ limit: 10,
+ });
this.playLiveStream(item);
} else if (item.series_id) {
this.items = [];
@@ -304,22 +333,25 @@ export class XtreamMainContainerComponent implements OnInit {
}
openPlayer(streamUrl: string, title: string) {
- const player = this.settings().player;
- if (player === VideoPlayer.MPV) {
+ this.player = this.settings().player ?? VideoPlayer.VideoJs;
+ if (this.player === VideoPlayer.MPV) {
this.dialog.open(ExternalPlayerInfoDialogComponent);
this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, {
url: streamUrl,
});
- } else if (player === VideoPlayer.VLC) {
+ } else if (this.player === VideoPlayer.VLC) {
this.dialog.open(ExternalPlayerInfoDialogComponent);
this.dataService.sendIpcEvent(OPEN_VLC_PLAYER, {
url: streamUrl,
});
} else {
- this.dialog.open(PlayerDialogComponent, {
- data: { streamUrl, player, title },
- width: '80%',
- });
+ this.streamUrl = streamUrl;
+ if (this.selectedContentType !== ContentType.ITV) {
+ this.dialog.open(PlayerDialogComponent, {
+ data: { streamUrl, player: this.player, title },
+ width: '80%',
+ });
+ }
}
}
@@ -427,7 +459,9 @@ export class XtreamMainContainerComponent implements OnInit {
}
sendRequest(params: Record) {
- this.isLoading = true;
+ if (params.action !== 'get_short_epg') {
+ this.isLoading = true;
+ }
const { serverUrl, username, password } = this.currentPlaylist();
this.dataService.sendIpcEvent(XTREAM_REQUEST, {
url: serverUrl,