From 28b4e64de378d89e5fcb92ffa5bc747a6dc7ac2c Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Sun, 9 Jul 2023 22:04:42 +0200 Subject: [PATCH] feat: improvements in xtream code module --- .../category-view/category-view.component.ts | 4 + src/app/xtream/detail-view.scss | 10 ++- .../player-dialog.component.html | 16 ++-- .../player-dialog/player-dialog.component.ts | 40 ++++++++- .../season-container.component.html | 4 +- .../season-container.component.scss | 3 + .../serial-details.component.html | 2 +- .../serial-details.component.ts | 31 +++---- .../vod-details/vod-details.component.html | 6 +- .../vod-details/vod-details.component.ts | 2 +- .../xtream-main-container.component.html | 46 +++++----- .../xtream-main-container.component.scss | 6 ++ .../xtream/xtream-main-container.component.ts | 88 ++++++++++++++----- 13 files changed, 175 insertions(+), 83 deletions(-) diff --git a/src/app/xtream/category-view/category-view.component.ts b/src/app/xtream/category-view/category-view.component.ts index 7ca2a4c6e..b5d2a698d 100644 --- a/src/app/xtream/category-view/category-view.component.ts +++ b/src/app/xtream/category-view/category-view.component.ts @@ -52,6 +52,10 @@ import { FilterPipe } from '../../shared/pipes/filter.pipe'; display: block; } + mat-card-content { + overflow-wrap: break-word; + } + .no-content { text-align: center; diff --git a/src/app/xtream/detail-view.scss b/src/app/xtream/detail-view.scss index e7ce35a6b..0e47a6251 100644 --- a/src/app/xtream/detail-view.scss +++ b/src/app/xtream/detail-view.scss @@ -1,7 +1,7 @@ .container { display: grid; grid-gap: 10px; - grid-template-columns: 340px auto; + grid-template-columns: 350px 450px; &::before { content: ''; @@ -18,7 +18,7 @@ .image { width: 300px; - padding: 20px; + padding: 10px; } .image img { @@ -26,7 +26,7 @@ height: auto; } -@media (max-width: 480px) { +@media (max-width: 800px) { .container { display: flex; flex-wrap: wrap; @@ -38,6 +38,10 @@ .details { flex: 1 1 100%; } + + .details { + padding: 0 10px; + } } .label { diff --git a/src/app/xtream/player-dialog/player-dialog.component.html b/src/app/xtream/player-dialog/player-dialog.component.html index aa0d3184a..bab0f67ae 100644 --- a/src/app/xtream/player-dialog/player-dialog.component.html +++ b/src/app/xtream/player-dialog/player-dialog.component.html @@ -1,13 +1,9 @@ -

Title

- +

{{ title }}

+ + + diff --git a/src/app/xtream/player-dialog/player-dialog.component.ts b/src/app/xtream/player-dialog/player-dialog.component.ts index 1d79ac6ce..ebcca004c 100644 --- a/src/app/xtream/player-dialog/player-dialog.component.ts +++ b/src/app/xtream/player-dialog/player-dialog.component.ts @@ -1,17 +1,49 @@ +import { NgIf } from '@angular/common'; import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { getExtensionFromUrl } from '../../../../shared/playlist.utils'; +import { HtmlVideoPlayerComponent } from '../../player/components/html-video-player/html-video-player.component'; import { VjsPlayerComponent } from '../../player/components/vjs-player/vjs-player.component'; +import { VideoPlayer } from '../../settings/settings.interface'; + +interface DialogData { + streamUrl: string; + player: VideoPlayer; + title: string; +} @Component({ selector: 'app-player-dialog', templateUrl: './player-dialog.component.html', standalone: true, - imports: [VjsPlayerComponent, MatDialogModule], + imports: [ + HtmlVideoPlayerComponent, + MatDialogModule, + NgIf, + VjsPlayerComponent, + ], }) export class PlayerDialogComponent { - streamUrl: string; + channel = {}; + vjsOptions = {}; + player: VideoPlayer; + title: string; - constructor(@Inject(MAT_DIALOG_DATA) streamUrl: any) { - this.streamUrl = streamUrl; + constructor(@Inject(MAT_DIALOG_DATA) data: DialogData) { + this.player = data.player; + this.title = data.title; + + const extension = getExtensionFromUrl(data.streamUrl); + const mimeType = + extension === 'm3u' || extension === 'm3u8' || extension === 'ts' + ? 'application/x-mpegURL' + : 'video/mp4'; + + this.vjsOptions = { + sources: [{ src: data.streamUrl, type: mimeType }], + }; + this.channel = { + url: data.streamUrl, + }; } } diff --git a/src/app/xtream/season-container/season-container.component.html b/src/app/xtream/season-container/season-container.component.html index 52bbcb39e..e7f087eb6 100644 --- a/src/app/xtream/season-container/season-container.component.html +++ b/src/app/xtream/season-container/season-container.component.html @@ -12,14 +12,14 @@ - < Back to seasons +
< Back to seasons
-
diff --git a/src/app/xtream/season-container/season-container.component.scss b/src/app/xtream/season-container/season-container.component.scss index d3667809a..659474277 100644 --- a/src/app/xtream/season-container/season-container.component.scss +++ b/src/app/xtream/season-container/season-container.component.scss @@ -13,8 +13,11 @@ .season-item, .episode-item { + min-height: 100px; + justify-content: center; width: 150px; cursor: pointer; + display: block; } .episode-item { diff --git a/src/app/xtream/serial-details/serial-details.component.html b/src/app/xtream/serial-details/serial-details.component.html index a92230b7c..308360c52 100644 --- a/src/app/xtream/serial-details/serial-details.component.html +++ b/src/app/xtream/serial-details/serial-details.component.html @@ -45,5 +45,5 @@ diff --git a/src/app/xtream/serial-details/serial-details.component.ts b/src/app/xtream/serial-details/serial-details.component.ts index 12f94cbd1..656e6ff06 100644 --- a/src/app/xtream/serial-details/serial-details.component.ts +++ b/src/app/xtream/serial-details/serial-details.component.ts @@ -1,12 +1,13 @@ import { JsonPipe, NgIf } from '@angular/common'; import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; -import { MatDialog } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; -import { OPEN_MPV_PLAYER } from '../../../../shared/ipc-commands'; -import { XtreamSerieDetails } from '../../../../shared/xtream-serie-details.interface'; -import { DataService } from '../../services/data.service'; +import { + XtreamSerieDetails, + XtreamSerieEpisode, +} from '../../../../shared/xtream-serie-details.interface'; import { selectCurrentPlaylist } from '../../state/selectors'; +import { PlayerDialogComponent } from '../player-dialog/player-dialog.component'; import { SeasonContainerComponent } from '../season-container/season-container.component'; @Component({ @@ -14,25 +15,19 @@ import { SeasonContainerComponent } from '../season-container/season-container.c templateUrl: './serial-details.component.html', styleUrls: ['../detail-view.scss'], standalone: true, - imports: [JsonPipe, MatButtonModule, NgIf, SeasonContainerComponent], + imports: [ + JsonPipe, + MatButtonModule, + NgIf, + SeasonContainerComponent, + PlayerDialogComponent, + ], }) export class SerialDetailsComponent { @Input({ required: true }) item: XtreamSerieDetails; - @Output() playClicked = new EventEmitter(); + @Output() playClicked = new EventEmitter(); - dialog = inject(MatDialog); store = inject(Store); - dataService = inject(DataService); currentPlaylist = this.store.selectSignal(selectCurrentPlaylist); - - playEpisode(episode: any) { - const { serverUrl, username, password } = this.currentPlaylist(); - - const streamUrl = `${serverUrl}/series/${username}/${password}/${episode.id}.${episode.container_extension}`; - /* this.dialog.open(PlayerDialogComponent, { - data: streamUrl, - }); */ - this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { url: streamUrl }); - } } diff --git a/src/app/xtream/vod-details/vod-details.component.html b/src/app/xtream/vod-details/vod-details.component.html index bd6e790b2..0c9202a67 100644 --- a/src/app/xtream/vod-details/vod-details.component.html +++ b/src/app/xtream/vod-details/vod-details.component.html @@ -41,7 +41,11 @@ {{ item.info.duration }}
-
diff --git a/src/app/xtream/vod-details/vod-details.component.ts b/src/app/xtream/vod-details/vod-details.component.ts index 21f210392..8feeaf5f4 100644 --- a/src/app/xtream/vod-details/vod-details.component.ts +++ b/src/app/xtream/vod-details/vod-details.component.ts @@ -13,5 +13,5 @@ import { XtreamVodDetails } from '../../../../shared/xtream-vod-details.interfac export class VodDetailsComponent { @Input({ required: true }) item: XtreamVodDetails; - @Output() playClicked = new EventEmitter(); + @Output() playClicked = new EventEmitter(); } diff --git a/src/app/xtream/xtream-main-container.component.html b/src/app/xtream/xtream-main-container.component.html index 3109fba86..764ad0b6d 100644 --- a/src/app/xtream/xtream-main-container.component.html +++ b/src/app/xtream/xtream-main-container.component.html @@ -6,28 +6,34 @@ />
- + + + + + - + - + - + - + +
diff --git a/src/app/xtream/xtream-main-container.component.scss b/src/app/xtream/xtream-main-container.component.scss index 37eac69c2..9c3468de1 100644 --- a/src/app/xtream/xtream-main-container.component.scss +++ b/src/app/xtream/xtream-main-container.component.scss @@ -1,4 +1,10 @@ .container { height: calc(100vh - 140px); overflow-y: auto; + display: flex; + justify-content: center; +} + +mat-spinner { + margin-top: 10px; } diff --git a/src/app/xtream/xtream-main-container.component.ts b/src/app/xtream/xtream-main-container.component.ts index 8d145b947..1e9b94174 100644 --- a/src/app/xtream/xtream-main-container.component.ts +++ b/src/app/xtream/xtream-main-container.component.ts @@ -1,5 +1,12 @@ import { JsonPipe, KeyValuePipe, NgFor, NgIf, NgSwitch } from '@angular/common'; -import { Component, NgZone, OnInit, effect, inject } from '@angular/core'; +import { + Component, + NgZone, + OnInit, + Signal, + effect, + inject, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; @@ -28,7 +35,17 @@ import { NavigationBarComponent } from './navigation-bar/navigation-bar.componen import { PlayerViewComponent } from './player-view/player-view.component'; import { VodDetailsComponent } from './vod-details/vod-details.component'; -import { XtreamSerieDetails } from '../../../shared/xtream-serie-details.interface'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { MatDialog } from '@angular/material/dialog'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { StorageMap } from '@ngx-pwa/local-storage'; +import { + XtreamSerieDetails, + XtreamSerieEpisode, +} from '../../../shared/xtream-serie-details.interface'; +import { Settings, VideoPlayer } from '../settings/settings.interface'; +import { STORE_KEY } from '../shared/enums/store-keys.enum'; +import { PlayerDialogComponent } from './player-dialog/player-dialog.component'; import { SerialDetailsComponent } from './serial-details/serial-details.component'; const ContentTypes = { @@ -85,12 +102,16 @@ type LayoutView = PlayerViewComponent, CategoryContentViewComponent, SerialDetailsComponent, + PlayerDialogComponent, + MatProgressSpinnerModule, ], }) export class XtreamMainContainerComponent implements OnInit { dataService = inject(DataService); + dialog = inject(MatDialog); ngZone = inject(NgZone); snackBar = inject(MatSnackBar); + storage = inject(StorageMap); store = inject(Store); currentPlaylist = this.store.selectSignal(selectCurrentPlaylist); @@ -100,6 +121,10 @@ export class XtreamMainContainerComponent implements OnInit { selectedContentType = ContentType.VODS; currentLayout: LayoutView = 'category'; vodDetails!: XtreamVodDetails | XtreamSerieDetails; + settings = toSignal( + this.storage.get(STORE_KEY.Settings) + ) as Signal; + isLoading = true; commandsList = [ new IpcCommand(XTREAM_RESPONSE, (response: XtreamResponse) => @@ -139,7 +164,6 @@ export class XtreamMainContainerComponent implements OnInit { } handleResponse(response: XtreamResponse) { - console.log(response.payload); switch (response.action) { case XtreamCodeActions.GetSeriesCategories: case XtreamCodeActions.GetVodCategories: @@ -160,6 +184,7 @@ export class XtreamMainContainerComponent implements OnInit { default: break; } + this.isLoading = false; } getCategories(contentType: ContentType = this.selectedContentType) { @@ -169,7 +194,6 @@ export class XtreamMainContainerComponent implements OnInit { this.sendRequest({ action }); } - // TODO: MAKE IT ABSTRACT SOMEHOW ngOnDestroy(): void { if (this.dataService.isElectron) { this.commandsList.forEach((command) => @@ -213,7 +237,7 @@ export class XtreamMainContainerComponent implements OnInit { title: item.name, action: XtreamCodeActions.GetLiveStreams, }); - this.playStream(item); + this.playLiveStream(item); } else if (item.series_id) { action = XtreamCodeActions.GetSeriesInfo; this.breadcrumbs.push({ title: item.name, action }); @@ -221,31 +245,47 @@ export class XtreamMainContainerComponent implements OnInit { } } - playStream(item: XtreamLiveStream) { - this.currentLayout = 'player'; + playLiveStream(item: XtreamLiveStream) { const { serverUrl, username, password } = this.currentPlaylist(); - const streamUrl = `${serverUrl}/${item.stream_type}/${username}/${password}/${item.stream_id}.ts`; - - this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { - url: streamUrl, - }); + this.openPlayer(streamUrl, item.name); } - playVod() { - this.currentLayout = 'player'; + openPlayer(streamUrl: string, title: string) { + const player = this.settings().player; + if (player === VideoPlayer.MPV) { + this.currentLayout = 'player'; + this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { + url: streamUrl, + }); + } else { + this.dialog.open(PlayerDialogComponent, { + data: { streamUrl, player, title }, + width: '80%', + }); + } + } + + playVod(vodItem: XtreamVodDetails) { const { serverUrl, username, password } = this.currentPlaylist(); - this.items = []; - const streamUrl = `${serverUrl}/movie/${username}/${password}/${ - (this.vodDetails as XtreamVodDetails).movie_data.stream_id - }.${ - (this.vodDetails as XtreamVodDetails).movie_data.container_extension - }`; + const streamUrl = `${serverUrl}/movie/${username}/${password}/${vodItem.movie_data.stream_id}.${vodItem.movie_data.container_extension}`; - this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { - url: streamUrl, - }); + this.openPlayer(streamUrl, vodItem.info.name); + } + + playEpisode(episode: XtreamSerieEpisode) { + const { serverUrl, username, password } = this.currentPlaylist(); + const player = this.settings().player; + const streamUrl = `${serverUrl}/series/${username}/${password}/${episode.id}.${episode.container_extension}`; + if (player === VideoPlayer.MPV) { + this.dataService.sendIpcEvent(OPEN_MPV_PLAYER, { url: streamUrl }); + } else { + this.dialog.open(PlayerDialogComponent, { + data: { streamUrl, player, title: episode.title }, + width: '80%', + }); + } } changeContentType(contentType: ContentType) { @@ -261,6 +301,7 @@ export class XtreamMainContainerComponent implements OnInit { null, { duration: 4000 } ); + this.isLoading = false; } /** @@ -313,6 +354,7 @@ export class XtreamMainContainerComponent implements OnInit { } sendRequest(params: Record) { + this.isLoading = true; const { serverUrl, username, password } = this.currentPlaylist(); this.dataService.sendIpcEvent(XTREAM_REQUEST, { url: serverUrl,