mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
Merge pull request #625 from 4gray/app-restructuring
refactor(nx): add restructuring
This commit is contained in:
42 files changed
+215
-193
No files matched your search
@@ -3,8 +3,8 @@ import { contextBridge, ipcRenderer } from 'electron';
|
||||
contextBridge.exposeInMainWorld('electron', {
|
||||
getAppVersion: () => ipcRenderer.invoke('get-app-version'),
|
||||
platform: process.platform,
|
||||
fetchPlaylistByUrl: (url: string) =>
|
||||
ipcRenderer.invoke('fetch-playlist-by-url', url),
|
||||
fetchPlaylistByUrl: (url: string, title?: string) =>
|
||||
ipcRenderer.invoke('fetch-playlist-by-url', url, title),
|
||||
updatePlaylistFromFilePath: (filePath: string, title: string) =>
|
||||
ipcRenderer.invoke('update-playlist-from-file-path', filePath, title),
|
||||
openPlaylistFromFile: () => ipcRenderer.invoke('open-playlist-from-file'),
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class PlaylistEvents {
|
||||
|
||||
const https = require('https');
|
||||
|
||||
ipcMain.handle('fetch-playlist-by-url', async (event, url) => {
|
||||
ipcMain.handle('fetch-playlist-by-url', async (event, url, title?: string) => {
|
||||
try {
|
||||
const agent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
@@ -33,7 +33,7 @@ ipcMain.handle('fetch-playlist-by-url', async (event, url) => {
|
||||
: extractedName;
|
||||
|
||||
const playlistObject = createPlaylistObject(
|
||||
playlistName,
|
||||
title ?? playlistName,
|
||||
parsedPlaylist,
|
||||
url,
|
||||
'URL'
|
||||
@@ -62,7 +62,6 @@ ipcMain.handle(
|
||||
);
|
||||
|
||||
ipcMain.handle('open-playlist-from-file', async () => {
|
||||
// 1. Await the result from the dialog
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
@@ -89,13 +88,9 @@ ipcMain.handle('open-playlist-from-file', async () => {
|
||||
'FILE'
|
||||
);
|
||||
|
||||
console.log('Sending playlist object to renderer:', playlistObject);
|
||||
|
||||
// 5. Return the final object. This value is sent back to Angular.
|
||||
return playlistObject;
|
||||
} catch (error) {
|
||||
console.error('Error reading or parsing the file:', error);
|
||||
// It's good practice to throw the error so the renderer can catch it
|
||||
throw new Error('Failed to process the selected file.');
|
||||
}
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Params } from '@angular/router';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { readTextFile } from '@tauri-apps/plugin-fs';
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
PLAYLIST_UPDATE_RESPONSE,
|
||||
XTREAM_RESPONSE,
|
||||
} from 'shared-interfaces';
|
||||
import { DataService } from '../../../../../libs/services/src/lib/data.service';
|
||||
import { AppConfig } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
@@ -58,11 +58,11 @@ export class TauriService extends DataService {
|
||||
if (type === PLAYLIST_PARSE_BY_URL) {
|
||||
this.fetchM3uPlaylistFromUrl(payload);
|
||||
} else if (type === PLAYLIST_UPDATE) {
|
||||
// TODO: update playlist from URL?
|
||||
this.updateM3uPlaylistFromFile(
|
||||
payload as {
|
||||
id: string;
|
||||
filePath: string;
|
||||
filePath?: string;
|
||||
url?: string;
|
||||
title: string;
|
||||
}
|
||||
);
|
||||
@@ -201,24 +201,42 @@ export class TauriService extends DataService {
|
||||
|
||||
private async updateM3uPlaylistFromFile(data: {
|
||||
id: string;
|
||||
filePath: string;
|
||||
url?: string;
|
||||
filePath?: string;
|
||||
title: string;
|
||||
}) {
|
||||
window.electron
|
||||
.updatePlaylistFromFilePath(data.filePath, data.title)
|
||||
.then((playlistObject) => {
|
||||
window.postMessage({
|
||||
type: PLAYLIST_UPDATE_RESPONSE,
|
||||
payload: {
|
||||
message:
|
||||
'Success! The playlist was successfully updated',
|
||||
playlist: {
|
||||
...playlistObject,
|
||||
_id: data.id,
|
||||
},
|
||||
let methodToCall = null;
|
||||
if (data.url && !data.filePath) {
|
||||
// fetch from url
|
||||
methodToCall = window.electron.fetchPlaylistByUrl(
|
||||
data.url,
|
||||
data.title
|
||||
);
|
||||
} else if (data.filePath && !data.url) {
|
||||
// update from file path
|
||||
methodToCall = window.electron.updatePlaylistFromFilePath(
|
||||
data.filePath,
|
||||
data.title
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
'Either url or filePath must be provided, but not both.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
methodToCall.then((playlistObject) => {
|
||||
window.postMessage({
|
||||
type: PLAYLIST_UPDATE_RESPONSE,
|
||||
payload: {
|
||||
message: 'Success! The playlist was successfully updated',
|
||||
playlist: {
|
||||
...playlistObject,
|
||||
_id: data.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* private getErrorMessageByStatusCode(status: number) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@@ -7,7 +7,11 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
imports: [MatIconModule, TranslateModule],
|
||||
template: `
|
||||
<div mat-dialog-content>
|
||||
<img src="./assets/icons/icon-tv-256.png" width="128" /><br />
|
||||
<img
|
||||
src="./assets/icons/icon-tv-256.png"
|
||||
width="128"
|
||||
alt="logo"
|
||||
/><br />
|
||||
<h2 mat-dialog-title>{{ 'ABOUT.TITLE' | translate }}</h2>
|
||||
<p>{{ 'ABOUT.DESCRIPTION' | translate }}</p>
|
||||
<p>{{ 'ABOUT.VERSION' | translate }}: {{ appVersion }}</p>
|
||||
@@ -19,7 +23,8 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
[attr.aria-label]="'ABOUT.GITHUB_TOOLTIP' | translate"
|
||||
><img
|
||||
src="./assets/icons/github-light.png"
|
||||
[title]="'ABOUT.GITHUB_TOOLTIP' | translate" /></a
|
||||
[title]="'ABOUT.GITHUB_TOOLTIP' | translate"
|
||||
alt="GitHub" /></a
|
||||
>
|
||||
<a
|
||||
href="http://twitter.com/share?text=IPTVnator — free cross-platform IPTV player. Available as PWA and as native application.&url=https://github.com/4gray/iptvnator&hashtags=iptv,m3u,video-player"
|
||||
@@ -29,6 +34,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
height="32"
|
||||
src="./assets/icons/twitter-light.png"
|
||||
[title]="'ABOUT.TWITTER_TOOLTIP' | translate"
|
||||
alt="Twitter"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
@@ -57,8 +63,8 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
font-size: 32px;
|
||||
}
|
||||
`,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class AboutDialogComponent {
|
||||
constructor(@Inject(MAT_DIALOG_DATA) public appVersion: string) {}
|
||||
public appVersion = inject<string>(MAT_DIALOG_DATA);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, inject, Inject } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import {
|
||||
MAT_DIALOG_DATA,
|
||||
@@ -22,14 +22,14 @@ export type PlaylistType = 'xtream' | 'url' | 'text' | 'file' | 'stalker';
|
||||
|
||||
@Component({
|
||||
imports: [
|
||||
FileUploadComponent,
|
||||
MatButtonModule,
|
||||
MatDialogModule,
|
||||
TranslateModule,
|
||||
FileUploadComponent,
|
||||
XtreamCodeImportComponent,
|
||||
StalkerPortalImportComponent,
|
||||
TextImportComponent,
|
||||
TranslateModule,
|
||||
UrlUploadComponent,
|
||||
XtreamCodeImportComponent,
|
||||
],
|
||||
selector: 'app-add-playlist',
|
||||
templateUrl: './add-playlist-dialog.component.html',
|
||||
@@ -40,11 +40,12 @@ export class AddPlaylistDialogComponent {
|
||||
private store = inject(Store);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
private translateService = inject(TranslateService);
|
||||
readonly data = inject<{ type: PlaylistType }>(MAT_DIALOG_DATA);
|
||||
|
||||
readonly playlistType!: PlaylistType;
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) data: { type: PlaylistType }) {
|
||||
this.playlistType = data.type;
|
||||
constructor() {
|
||||
this.playlistType = this.data.type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export interface ConfirmDialogData {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
+3
-2
@@ -65,10 +65,11 @@ import { PortalStore } from '../../../xtream/portal.store';
|
||||
MatIconModule,
|
||||
RouterLink,
|
||||
TranslateModule,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class ExternalPlayerInfoDialogComponent {
|
||||
portalStore = inject(PortalStore);
|
||||
readonly portalStore = inject(PortalStore);
|
||||
|
||||
openUrl(url: string) {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
@@ -9,16 +9,16 @@ import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {
|
||||
DataService,
|
||||
SortBy,
|
||||
SortOrder,
|
||||
SortService,
|
||||
} from '@iptvnator/services';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import { selectActiveTypeFilters, setSelectedFilters } from 'm3u-state';
|
||||
import { DataService } from '../../../../../../../libs/services/src/lib/data.service';
|
||||
import {
|
||||
SortBy,
|
||||
SortOrder,
|
||||
SortService,
|
||||
} from '../../../../../../../libs/services/src/lib/sort.service';
|
||||
import { HomeComponent } from '../../../home/home.component';
|
||||
import { AboutDialogComponent } from '../about-dialog/about-dialog.component';
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { StalkerPortalActions } from '../../shared/stalker-portal-actions.enum';
|
||||
import { StalkerPortalActions } from 'shared-interfaces';
|
||||
|
||||
export const StalkerContentTypes = {
|
||||
stb: {
|
||||
|
||||
@@ -4,12 +4,11 @@ import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { selectPlaylistById } from 'm3u-state';
|
||||
import { STALKER_REQUEST } from 'shared-interfaces';
|
||||
import { DataService } from '../../../../../../libs/services/src/lib/data.service';
|
||||
import { StalkerPortalActions } from '../../../shared/stalker-portal-actions.enum';
|
||||
import { STALKER_REQUEST, StalkerPortalActions } from 'shared-interfaces';
|
||||
import { SearchFormComponent } from '../../shared/components/search-form/search-form.component';
|
||||
import { SearchResultItemComponent } from '../../shared/components/search-result-item/search-result-item.component';
|
||||
import { PlaylistErrorViewComponent } from '../../xtream-tauri/playlist-error-view/playlist-error-view.component';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, inject, resource, signal } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { DataService, PlaylistsService } from '@iptvnator/services';
|
||||
import {
|
||||
patchState,
|
||||
signalStore,
|
||||
@@ -9,10 +10,11 @@ import {
|
||||
withState,
|
||||
} from '@ngrx/signals';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistMeta, STALKER_REQUEST } from 'shared-interfaces';
|
||||
import { DataService } from '../../../../../libs/services/src/lib/data.service';
|
||||
import { PlaylistsService } from '../../../../../libs/services/src/lib/playlists.service';
|
||||
import { StalkerPortalActions } from '../../shared/stalker-portal-actions.enum';
|
||||
import {
|
||||
PlaylistMeta,
|
||||
STALKER_REQUEST,
|
||||
StalkerPortalActions,
|
||||
} from 'shared-interfaces';
|
||||
import { PlayerService } from '../services/player.service';
|
||||
import { ContentType } from '../xtream/content-type.enum';
|
||||
import { StalkerSeason } from './models/stalker-season.interface';
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { XtreamCategory } from '../../../shared/xtream-category.interface';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { PlaylistErrorViewComponent } from '../playlist-error-view/playlist-error-view.component';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { selectActivePlaylist } from 'm3u-state';
|
||||
import { BehaviorSubject, switchMap } from 'rxjs';
|
||||
import { XtreamCategory } from '../../../shared/xtream-category.interface';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { FavoritesLayoutComponent } from '../../shared/components/favorites-layout/favorites-layout.component';
|
||||
import { FavoriteItem } from '../services/favorite-item.interface';
|
||||
import { FavoritesService } from '../services/favorites.service';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { XtreamCategory } from '../../../shared/xtream-category.interface';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { EpgViewComponent, WebPlayerViewComponent } from 'shared-portals';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { MpvPlayerBarComponent } from '../../shared/components/mpv-player-bar/mpv-player-bar.component';
|
||||
|
||||
+1
-2
@@ -24,8 +24,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FilterPipe } from '@iptvnator/pipes';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { XtreamItem } from '../../../../../../libs/shared/interfaces/src/lib/xtream-item.interface';
|
||||
import { XtreamCategory } from '../../../shared/xtream-category.interface';
|
||||
import { XtreamCategory, XtreamItem } from 'shared-interfaces';
|
||||
import { FavoritesService } from '../services/favorites.service';
|
||||
import { XtreamStore } from '../xtream.store';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component, EventEmitter, Output, input } from '@angular/core';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { XtreamSerieEpisode } from '../../../shared/xtream-serie-details.interface';
|
||||
import { XtreamSerieEpisode } from 'shared-interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'app-season-container',
|
||||
|
||||
@@ -3,7 +3,7 @@ import { MatButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { XtreamSerieEpisode } from '../../../shared/xtream-serie-details.interface';
|
||||
import { XtreamSerieEpisode } from 'shared-interfaces';
|
||||
import { SeasonContainerComponent } from '../season-container/season-container.component';
|
||||
import { XtreamStore } from '../xtream.store';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { MatIcon } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { XtreamVodDetails } from '../../../shared/xtream-vod-details.interface';
|
||||
import { XtreamVodDetails } from 'shared-interfaces';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { XtreamStore } from '../xtream.store';
|
||||
import { SafePipe } from './safe.pipe';
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { CategoryViewComponent } from './category-view/category-view.component';
|
||||
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { XtreamCategory } from '../../shared/xtream-category.interface';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { MpvPlayerBarComponent } from '../shared/components/mpv-player-bar/mpv-player-bar.component';
|
||||
import { CategoryViewComponent } from './category-view/category-view.component';
|
||||
import { XtreamStore } from './xtream.store';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { GlobalSearchResult } from '../../../../../libs/services/src/lib/database.service';
|
||||
import { EpgItem } from '../../../../../libs/shared/interfaces/src/lib/epg-item.interface';
|
||||
import { XtreamSerieItem } from '../../../../../libs/shared/interfaces/src/lib/xtream-serie-item.interface';
|
||||
import { XtreamCategory } from '../../shared/xtream-category.interface';
|
||||
import { XtreamLiveStream } from '../../shared/xtream-live-stream.interface';
|
||||
import { XtreamVodStream } from '../../shared/xtream-vod-stream.interface';
|
||||
import { GlobalSearchResult } from '@iptvnator/services';
|
||||
import {
|
||||
EpgItem,
|
||||
XtreamCategory,
|
||||
XtreamLiveStream,
|
||||
XtreamSerieItem,
|
||||
XtreamVodStream,
|
||||
} from 'shared-interfaces';
|
||||
|
||||
export type ContentType = 'live' | 'vod' | 'series';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { computed, inject } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DatabaseService, DataService } from '@iptvnator/services';
|
||||
import {
|
||||
patchState,
|
||||
signalStore,
|
||||
@@ -20,15 +21,13 @@ import {
|
||||
switchMap,
|
||||
tap,
|
||||
} from 'rxjs';
|
||||
import { Playlist } from 'shared-interfaces';
|
||||
import { DataService } from '../../../../../libs/services/src/lib/data.service';
|
||||
import { DatabaseService } from '../../../../../libs/services/src/lib/database.service';
|
||||
import { XtreamCodeActions } from '../../shared/xtream-code-actions';
|
||||
import {
|
||||
Playlist,
|
||||
XtreamCodeActions,
|
||||
XtreamSerieDetails,
|
||||
XtreamSerieEpisode,
|
||||
} from '../../shared/xtream-serie-details.interface';
|
||||
import { XtreamVodDetails } from '../../shared/xtream-vod-details.interface';
|
||||
XtreamVodDetails,
|
||||
} from 'shared-interfaces';
|
||||
import { PlayerService } from '../services/player.service';
|
||||
import { SettingsStore } from '../services/settings-store.service';
|
||||
import { XtreamAccountInfo } from './account-info/account-info.interface';
|
||||
@@ -85,8 +84,8 @@ export const XtreamStore = signalStore(
|
||||
return type === 'live'
|
||||
? store.liveCategories()
|
||||
: type === 'vod'
|
||||
? store.vodCategories()
|
||||
: store.serialCategories();
|
||||
? store.vodCategories()
|
||||
: store.serialCategories();
|
||||
}),
|
||||
getSelectedCategory: computed(() => {
|
||||
const categoryId = store.selectedCategoryId();
|
||||
@@ -103,8 +102,8 @@ export const XtreamStore = signalStore(
|
||||
categoryType === 'live'
|
||||
? store.liveStreams()
|
||||
: categoryType === 'vod'
|
||||
? store.vodStreams()
|
||||
: store.serialStreams();
|
||||
? store.vodStreams()
|
||||
: store.serialStreams();
|
||||
|
||||
if (!store.selectedItem()) return null;
|
||||
|
||||
@@ -129,8 +128,8 @@ export const XtreamStore = signalStore(
|
||||
categoryType === 'live'
|
||||
? store.liveStreams()
|
||||
: categoryType === 'vod'
|
||||
? store.vodStreams()
|
||||
: store.serialStreams();
|
||||
? store.vodStreams()
|
||||
: store.serialStreams();
|
||||
|
||||
return content
|
||||
.filter((item) => item.category_id === categoryId)
|
||||
@@ -149,13 +148,13 @@ export const XtreamStore = signalStore(
|
||||
.filter((i) => Number((i as any).id) === categoryId)
|
||||
.length
|
||||
: categoryType === 'vod'
|
||||
? store
|
||||
.vodStreams()
|
||||
.filter((i) => Number(i.category_id) === categoryId)
|
||||
.length
|
||||
: store
|
||||
.serialStreams()
|
||||
.filter((i) => i.category_id === categoryId).length;
|
||||
? store
|
||||
.vodStreams()
|
||||
.filter((i) => Number(i.category_id) === categoryId)
|
||||
.length
|
||||
: store
|
||||
.serialStreams()
|
||||
.filter((i) => i.category_id === categoryId).length;
|
||||
|
||||
return Math.ceil(totalItems / store.limit());
|
||||
}),
|
||||
@@ -166,8 +165,8 @@ export const XtreamStore = signalStore(
|
||||
categoryType === 'live'
|
||||
? store.liveStreams()
|
||||
: categoryType === 'vod'
|
||||
? store.vodStreams()
|
||||
: store.serialStreams();
|
||||
? store.vodStreams()
|
||||
: store.serialStreams();
|
||||
|
||||
return content.filter((item) => item.category_id === categoryId);
|
||||
}),
|
||||
@@ -223,7 +222,9 @@ export const XtreamStore = signalStore(
|
||||
}
|
||||
|
||||
const remoteData = await dataService.fetchData(
|
||||
`${store.currentPlaylist().serverUrl}/player_api.php`,
|
||||
`${
|
||||
store.currentPlaylist().serverUrl
|
||||
}/player_api.php`,
|
||||
queryParams
|
||||
);
|
||||
|
||||
@@ -293,7 +294,9 @@ export const XtreamStore = signalStore(
|
||||
}
|
||||
|
||||
const remoteData = await dataService.fetchData(
|
||||
`${store.currentPlaylist().serverUrl}/player_api.php`,
|
||||
`${
|
||||
store.currentPlaylist().serverUrl
|
||||
}/player_api.php`,
|
||||
queryParams
|
||||
);
|
||||
|
||||
@@ -758,7 +761,9 @@ export const XtreamStore = signalStore(
|
||||
async loadChannelEpg(streamId: number) {
|
||||
try {
|
||||
const response = await dataService.fetchData(
|
||||
`${store.currentPlaylist().serverUrl}/player_api.php`,
|
||||
`${
|
||||
store.currentPlaylist().serverUrl
|
||||
}/player_api.php`,
|
||||
{
|
||||
action: 'get_short_epg',
|
||||
username: store.currentPlaylist().username,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { StalkerPortalActions } from '../../shared/stalker-portal-actions.enum';
|
||||
import { XtreamCodeActions } from '../../shared/xtream-code-actions';
|
||||
import { StalkerPortalActions, XtreamCodeActions } from 'shared-interfaces';
|
||||
|
||||
export type PortalActions = StalkerPortalActions | XtreamCodeActions;
|
||||
|
||||
|
||||
@@ -3,53 +3,43 @@ import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { FilterPipe } from '@iptvnator/pipes';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { XtreamCategory } from '../../../shared/xtream-category.interface';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { PlaylistErrorViewComponent } from '../playlist-error-view/playlist-error-view.component';
|
||||
import { PortalStore } from '../portal.store';
|
||||
|
||||
@Component({
|
||||
selector: 'app-category-view',
|
||||
template: `
|
||||
@if (items?.length > 0) {
|
||||
@for (
|
||||
item of items() | filterBy: searchPhrase() : 'category_name';
|
||||
track $index
|
||||
) {
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
class="category-item"
|
||||
(click)="categoryClicked.emit(item)"
|
||||
>
|
||||
<mat-card-content>
|
||||
{{
|
||||
item.category_name ||
|
||||
item.name ||
|
||||
'No category name'
|
||||
}}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
@if (
|
||||
!(items() | filterBy: searchPhrase() : 'category_name')?.length
|
||||
) {
|
||||
<app-playlist-error-view
|
||||
title="No results"
|
||||
[description]="
|
||||
'PORTALS.EMPTY_LIST_VIEW.NO_SEARCH_RESULTS' | translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'NO_SEARCH_RESULTS'"
|
||||
/>
|
||||
}
|
||||
} @else {
|
||||
<app-playlist-error-view
|
||||
[title]="'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.TITLE' | translate"
|
||||
[description]="
|
||||
'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.DESCRIPTION' | translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'EMPTY_CATEGORY'"
|
||||
/>
|
||||
@if (items?.length > 0) { @for ( item of items() | filterBy:
|
||||
searchPhrase() : 'category_name'; track $index ) {
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
class="category-item"
|
||||
(click)="categoryClicked.emit(item)"
|
||||
>
|
||||
<mat-card-content>
|
||||
{{ item.category_name || item.name || 'No category name' }}
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
} @if ( !(items() | filterBy: searchPhrase() : 'category_name')?.length
|
||||
) {
|
||||
<app-playlist-error-view
|
||||
title="No results"
|
||||
[description]="
|
||||
'PORTALS.EMPTY_LIST_VIEW.NO_SEARCH_RESULTS' | translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'NO_SEARCH_RESULTS'"
|
||||
/>
|
||||
} } @else {
|
||||
<app-playlist-error-view
|
||||
[title]="'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.TITLE' | translate"
|
||||
[description]="
|
||||
'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.DESCRIPTION' | translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'EMPTY_CATEGORY'"
|
||||
/>
|
||||
}
|
||||
`,
|
||||
styleUrl: './category-view.component.scss',
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
distinctUntilChanged,
|
||||
map,
|
||||
} from 'rxjs';
|
||||
import { XtreamLiveStream } from '../../../shared/xtream-live-stream.interface';
|
||||
import { XtreamLiveStream } from 'shared-interfaces';
|
||||
import { SettingsComponent } from '../../settings/settings.component';
|
||||
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { KeyValuePipe } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { XtreamSerieEpisode } from '../../../shared/xtream-serie-details.interface';
|
||||
import { XtreamSerieEpisode } from 'shared-interfaces';
|
||||
|
||||
@Component({
|
||||
selector: 'app-season-container',
|
||||
|
||||
@@ -2,14 +2,11 @@ import { Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PlaylistsService } from '@iptvnator/services';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { selectCurrentPlaylist } from 'm3u-state';
|
||||
import { PlaylistsService } from '../../../../../../libs/services/src/lib/playlists.service';
|
||||
import {
|
||||
XtreamSerieDetails,
|
||||
XtreamSerieEpisode,
|
||||
} from '../../../shared/xtream-serie-details.interface';
|
||||
import { XtreamSerieDetails, XtreamSerieEpisode } from 'shared-interfaces';
|
||||
import { SeasonContainerComponent } from '../season-container/season-container.component';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -10,9 +10,9 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PlaylistsService } from '@iptvnator/services';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { PlaylistsService } from '../../../../../../libs/services/src/lib/playlists.service';
|
||||
import { XtreamVodDetails } from '../../../shared/xtream-vod-details.interface';
|
||||
import { XtreamVodDetails } from 'shared-interfaces';
|
||||
import { SafePipe } from './safe.pipe';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -14,7 +14,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import { DataService, PlaylistsService } from '@iptvnator/services';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { selectCurrentPlaylist } from 'm3u-state';
|
||||
@@ -30,18 +30,15 @@ import {
|
||||
VideoPlayer,
|
||||
XTREAM_REQUEST,
|
||||
XTREAM_RESPONSE,
|
||||
} from 'shared-interfaces';
|
||||
import { LiveStreamLayoutComponent } from 'shared-portals';
|
||||
import { PlaylistsService } from '../../../../../libs/services/src/lib/playlists.service';
|
||||
import { XtreamCategory } from '../../shared/xtream-category.interface';
|
||||
import { XtreamCodeActions } from '../../shared/xtream-code-actions';
|
||||
import { XtreamLiveStream } from '../../shared/xtream-live-stream.interface';
|
||||
import { XtreamResponse } from '../../shared/xtream-response.interface';
|
||||
import {
|
||||
XtreamCategory,
|
||||
XtreamCodeActions,
|
||||
XtreamLiveStream,
|
||||
XtreamResponse,
|
||||
XtreamSerieDetails,
|
||||
XtreamSerieEpisode,
|
||||
} from '../../shared/xtream-serie-details.interface';
|
||||
import { XtreamVodDetails } from '../../shared/xtream-vod-details.interface';
|
||||
XtreamVodDetails,
|
||||
} from 'shared-interfaces';
|
||||
import { LiveStreamLayoutComponent } from 'shared-portals';
|
||||
import { SettingsStore } from '../services/settings-store.service';
|
||||
import { ExternalPlayerInfoDialogComponent } from '../shared/components/external-player-info-dialog/external-player-info-dialog.component';
|
||||
import { PlaylistErrorViewComponent } from '../xtream/playlist-error-view/playlist-error-view.component';
|
||||
|
||||
Vendored
+4
-1
@@ -12,7 +12,10 @@ declare global {
|
||||
electron: {
|
||||
getAppVersion: () => Promise<string>;
|
||||
platform: string;
|
||||
fetchPlaylistByUrl: (url: string) => Promise<Playlist>;
|
||||
fetchPlaylistByUrl: (
|
||||
url: string,
|
||||
title?: string
|
||||
) => Promise<Playlist>;
|
||||
updatePlaylistFromFilePath: (
|
||||
filePath: string,
|
||||
title: string
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ConfirmDialogData } from '../../../../apps/web/src/app/shared/components/confirm-dialog/confirm-dialog-data.interface';
|
||||
import { ConfirmDialogComponent } from '../../../../apps/web/src/app/shared/components/confirm-dialog/confirm-dialog.component';
|
||||
import { ConfirmDialogComponent, ConfirmDialogData } from 'components';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -12,8 +12,16 @@ export * from './lib/parsed-playlist.interface';
|
||||
export * from './lib/playlist-meta.type';
|
||||
export * from './lib/playlist.interface';
|
||||
export * from './lib/settings.interface';
|
||||
export * from './lib/stalker-portal-actions.enum';
|
||||
export * from './lib/store-keys.enum';
|
||||
export * from './lib/stream-format.enum';
|
||||
export * from './lib/theme.enum';
|
||||
export * from './lib/xtream-category.interface';
|
||||
export * from './lib/xtream-code-actions';
|
||||
export * from './lib/xtream-item.interface';
|
||||
export * from './lib/xtream-live-stream.interface';
|
||||
export * from './lib/xtream-response.interface';
|
||||
export * from './lib/xtream-serie-details.interface';
|
||||
export * from './lib/xtream-serie-item.interface';
|
||||
export * from './lib/xtream-vod-details.interface';
|
||||
export * from './lib/xtream-vod-stream.interface';
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { XtreamItem } from '../../../../libs/shared/interfaces/src/lib/xtream-item.interface';
|
||||
import { XtreamItem } from 'shared-interfaces';
|
||||
|
||||
export interface XtreamLiveStream extends XtreamItem {
|
||||
stream_type: 'live';
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { XtreamItem } from '../../../../libs/shared/interfaces/src/lib/xtream-item.interface';
|
||||
import { XtreamItem } from 'shared-interfaces';
|
||||
|
||||
export interface XtreamVodStream extends XtreamItem {
|
||||
stream_type: 'movie';
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './lib/art-player/art-player.component';
|
||||
export * from './lib/audio-player/audio-player.component';
|
||||
export * from './lib/channel-list-container/channel-list-container.component';
|
||||
export * from './lib/confirm-dialog/confirm-dialog.component';
|
||||
export * from './lib/epg-list/epg-item-description/epg-item-description.component';
|
||||
export * from './lib/epg-list/epg-list-item/epg-list-item.component';
|
||||
export * from './lib/epg-list/epg-list.component';
|
||||
|
||||
+17
-9
@@ -1,8 +1,16 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ConfirmDialogData } from './confirm-dialog-data.interface';
|
||||
|
||||
export interface ConfirmDialogData {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
@Component({
|
||||
imports: [MatButtonModule, MatDialogModule, TranslateModule],
|
||||
template: `
|
||||
@@ -14,19 +22,19 @@ import { ConfirmDialogData } from './confirm-dialog-data.interface';
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close cdkFocusInitial color="accent">
|
||||
{{ dialogData?.cancelLabel || 'NO' | translate }}
|
||||
{{ dialogData.cancelLabel || 'NO' | translate }}
|
||||
</button>
|
||||
<button mat-flat-button [mat-dialog-close]="true" color="accent">
|
||||
{{ dialogData?.confirmLabel || 'YES' | translate }}
|
||||
{{ dialogData.confirmLabel || 'YES' | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
`
|
||||
`,
|
||||
})
|
||||
export class ConfirmDialogComponent {
|
||||
/** Contains meta information to show in the dialog */
|
||||
dialogData!: ConfirmDialogData;
|
||||
readonly dialogData!: ConfirmDialogData;
|
||||
readonly data = inject<ConfirmDialogData>(MAT_DIALOG_DATA);
|
||||
|
||||
constructor(@Inject(MAT_DIALOG_DATA) data: ConfirmDialogData) {
|
||||
this.dialogData = data;
|
||||
constructor() {
|
||||
this.dialogData = this.data;
|
||||
}
|
||||
}
|
||||
+15
-15
@@ -26,11 +26,13 @@
|
||||
<mat-list-option
|
||||
*cdkVirtualFor="
|
||||
let item of channels
|
||||
| filterBy: searchString() : 'name';
|
||||
| filterBy : searchString() : 'name';
|
||||
trackBy: trackBy;
|
||||
templateCacheSize: 0
|
||||
"
|
||||
[selected]="item.stream_id === activeLiveStream?.stream_id"
|
||||
[selected]="
|
||||
item.stream_id === activeLiveStream?.stream_id
|
||||
"
|
||||
[value]="item"
|
||||
(click)="itemClicked.emit(item)"
|
||||
>
|
||||
@@ -39,6 +41,7 @@
|
||||
class="stream-icon"
|
||||
*ngIf="item.stream_icon"
|
||||
[src]="item.stream_icon"
|
||||
alt="channel icon"
|
||||
/>
|
||||
<div class="title" matListItemTitle>
|
||||
{{ item.name }}
|
||||
@@ -48,20 +51,17 @@
|
||||
</mat-selection-list>
|
||||
</div>
|
||||
</div>
|
||||
@if (player === 'videojs' || player === 'html5') {
|
||||
@if (streamUrl) {
|
||||
<div class="video-player">
|
||||
<app-web-player-view [streamUrl]="streamUrl" />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (streamUrl) {
|
||||
<div class="epg">
|
||||
<div class="epg-content">
|
||||
<app-epg-view [epgItems]="epgItems" />
|
||||
</div>
|
||||
@if (player === 'videojs' || player === 'html5') { @if (streamUrl) {
|
||||
<div class="video-player">
|
||||
<app-web-player-view [streamUrl]="streamUrl" />
|
||||
</div>
|
||||
} } @if (streamUrl) {
|
||||
<div class="epg">
|
||||
<div class="epg-content">
|
||||
<app-epg-view [epgItems]="epgItems" />
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="no-channel-selected">Please select a channel</div>
|
||||
<div class="no-channel-selected">Please select a channel</div>
|
||||
}
|
||||
</div>
|
||||
Reference in new issue
Block a user