mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
refactor(nx): update repository structure
Introduce new libraries to improve the boundaries and dependencies in mono repo
This commit is contained in:
1 parent
1780bb0e53
commit
256f0e0136
198 files changed
+1384
-815
No files matched your search
@@ -4,7 +4,7 @@
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/web/src",
|
||||
"tags": [],
|
||||
"tags": ["scope:app", "type:app"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular/build:application",
|
||||
|
||||
@@ -20,13 +20,15 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { PlaylistEffects, playlistReducer } from 'm3u-state';
|
||||
import { NgxIndexedDBModule, NgxIndexedDBService } from 'ngx-indexed-db';
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
||||
import { PORTAL_PLAYER } from '@iptvnator/portal/shared/util';
|
||||
import { provideXtreamDataSource } from '@iptvnator/portal/xtream/data-access';
|
||||
import { DataService } from 'services';
|
||||
import { dbConfig } from 'shared-interfaces';
|
||||
import { AppConfig } from '../environments/environment';
|
||||
import { routes } from './app.routes';
|
||||
import { ElectronService } from './services/electron.service';
|
||||
import { PlayerService } from './services/player.service';
|
||||
import { PwaService } from './services/pwa.service';
|
||||
import { provideXtreamDataSource } from './xtream-electron/data-sources';
|
||||
|
||||
// AoT requires an exported function for factories
|
||||
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
|
||||
@@ -85,6 +87,10 @@ export const appConfig: ApplicationConfig = {
|
||||
useFactory: DataFactory,
|
||||
deps: [NgxIndexedDBService, HttpClient],
|
||||
},
|
||||
{
|
||||
provide: PORTAL_PLAYER,
|
||||
useExisting: PlayerService,
|
||||
},
|
||||
...provideXtreamDataSource(),
|
||||
],
|
||||
};
|
||||
@@ -76,7 +76,7 @@ export const routes: Routes = [
|
||||
path: 'downloads',
|
||||
data: { layout: 'workspace' },
|
||||
loadComponent: () =>
|
||||
import('./xtream-electron/downloads/downloads.component').then(
|
||||
import('@iptvnator/portal/downloads/feature').then(
|
||||
(c) => c.DownloadsComponent
|
||||
),
|
||||
},
|
||||
|
||||
File renamed without changes.
File renamed without changes.
+9
-9
@@ -28,9 +28,9 @@ import { DownloadsService } from '../../services/downloads.service';
|
||||
import { ExternalPlaybackService } from '../../services/external-playback.service';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { GridListComponent } from '../../shared/components/grid-list/grid-list.component';
|
||||
import { PlaylistErrorViewComponent } from '../../shared/components/playlist-error-view/playlist-error-view.component';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { StalkerVodSource } from '../../stalker/models';
|
||||
import { PlaylistErrorViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import { StalkerVodSource } from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerSeriesViewComponent } from '../../stalker/stalker-series-view/stalker-series-view.component';
|
||||
import {
|
||||
buildStalkerSelectedVodItem,
|
||||
@@ -40,12 +40,12 @@ import {
|
||||
normalizeStalkerEntityId,
|
||||
normalizeStalkerEntityIdAsNumber,
|
||||
toggleStalkerVodFavorite,
|
||||
} from '../../stalker/stalker-vod.utils';
|
||||
import { StalkerStore } from '../../stalker/stalker.store';
|
||||
import { XTREAM_DATA_SOURCE } from '../data-sources';
|
||||
import { XtreamCategorySortMode } from '../stores/features/with-selection.feature';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { VodDetailsComponent } from '../vod-details/vod-details.component';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import { XTREAM_DATA_SOURCE } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamCategorySortMode } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { VodDetailsComponent } from '../../xtream-electron/vod-details/vod-details.component';
|
||||
|
||||
const SORT_STORAGE_KEY = 'xtream-category-sort-mode';
|
||||
|
||||
File renamed without changes.
File renamed without changes.
+4
-4
@@ -10,10 +10,10 @@ import { PlaylistInfoComponent } from 'components';
|
||||
import { selectPlaylistById } from 'm3u-state';
|
||||
import { Playlist } from 'shared-interfaces';
|
||||
import { SettingsComponent } from '../../settings/settings.component';
|
||||
import { AccountInfoComponent } from '../account-info/account-info.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { buildPortalRailLinks } from '../../shared/navigation/portal-rail-links';
|
||||
import { PortalRailLinksComponent } from '../../shared/navigation/portal-rail-links.component';
|
||||
import { AccountInfoComponent } from '../../xtream-electron/account-info/account-info.component';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { buildPortalRailLinks } from '@iptvnator/portal/shared/util';
|
||||
import { PortalRailLinksComponent } from '@iptvnator/portal/shared/ui';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navigation',
|
||||
@@ -1,424 +1,5 @@
|
||||
import { computed, inject, Injectable, OnDestroy, signal } from '@angular/core';
|
||||
import { SettingsStore } from './settings-store.service';
|
||||
|
||||
export type DownloadStatus =
|
||||
| 'queued'
|
||||
| 'downloading'
|
||||
| 'completed'
|
||||
| 'failed'
|
||||
| 'canceled';
|
||||
|
||||
export interface DownloadItem {
|
||||
id: number;
|
||||
playlistId: string;
|
||||
xtreamId: number;
|
||||
contentType: 'vod' | 'episode';
|
||||
seriesXtreamId?: number;
|
||||
seasonNumber?: number;
|
||||
episodeNumber?: number;
|
||||
title: string;
|
||||
url: string;
|
||||
fileName?: string;
|
||||
filePath?: string;
|
||||
posterUrl?: string;
|
||||
status: DownloadStatus;
|
||||
bytesDownloaded?: number;
|
||||
totalBytes?: number;
|
||||
errorMessage?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DownloadsService implements OnDestroy {
|
||||
private readonly settingsStore = inject(SettingsStore);
|
||||
private unsubscribe?: () => void;
|
||||
|
||||
/** Signal for the list of downloads */
|
||||
readonly downloads = signal<DownloadItem[]>([]);
|
||||
|
||||
/** Whether the download feature is available (Electron only) */
|
||||
readonly isAvailable = computed(() => !!window.electron?.downloadsGetList);
|
||||
|
||||
/** Whether there are any downloads */
|
||||
readonly hasDownloads = computed(() => this.downloads().length > 0);
|
||||
|
||||
/** Active downloads count */
|
||||
readonly activeCount = computed(
|
||||
() =>
|
||||
this.downloads().filter(
|
||||
(d) => d.status === 'queued' || d.status === 'downloading'
|
||||
).length
|
||||
);
|
||||
|
||||
/** Completed downloads count */
|
||||
readonly completedCount = computed(
|
||||
() => this.downloads().filter((d) => d.status === 'completed').length
|
||||
);
|
||||
|
||||
/** Failed downloads count */
|
||||
readonly failedCount = computed(
|
||||
() =>
|
||||
this.downloads().filter(
|
||||
(d) => d.status === 'failed' || d.status === 'canceled'
|
||||
).length
|
||||
);
|
||||
|
||||
/** Current download folder */
|
||||
readonly downloadFolder = signal<string>('');
|
||||
|
||||
constructor() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private async init() {
|
||||
if (!this.isAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load initial download list
|
||||
await this.loadDownloads();
|
||||
|
||||
// Load download folder
|
||||
await this.loadDownloadFolder();
|
||||
|
||||
// Subscribe to download updates
|
||||
this.unsubscribe = window.electron.onDownloadsUpdate(() => {
|
||||
this.loadDownloads();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unsubscribe?.();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load downloads from the backend
|
||||
*/
|
||||
async loadDownloads(playlistId?: string): Promise<void> {
|
||||
if (!this.isAvailable()) return;
|
||||
|
||||
try {
|
||||
const list = await window.electron.downloadsGetList(playlistId);
|
||||
this.downloads.set(list);
|
||||
} catch (error) {
|
||||
console.error('[DownloadsService] Error loading downloads:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load or resolve download folder
|
||||
*/
|
||||
async loadDownloadFolder(): Promise<string> {
|
||||
if (!this.isAvailable()) return '';
|
||||
|
||||
// First check settings
|
||||
const storedFolder = this.settingsStore.getDownloadFolder?.();
|
||||
if (storedFolder) {
|
||||
this.downloadFolder.set(storedFolder);
|
||||
return storedFolder;
|
||||
}
|
||||
|
||||
// Fall back to default
|
||||
try {
|
||||
const defaultFolder =
|
||||
await window.electron.downloadsGetDefaultFolder();
|
||||
this.downloadFolder.set(defaultFolder);
|
||||
return defaultFolder;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error getting default folder:',
|
||||
error
|
||||
);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new download
|
||||
*/
|
||||
async startDownload(data: {
|
||||
playlistId: string;
|
||||
xtreamId: number;
|
||||
contentType: 'vod' | 'episode';
|
||||
title: string;
|
||||
url: string;
|
||||
posterUrl?: string;
|
||||
headers?: { userAgent?: string; referer?: string; origin?: string };
|
||||
seriesXtreamId?: number;
|
||||
seasonNumber?: number;
|
||||
episodeNumber?: number;
|
||||
// Playlist info for auto-creation if needed (Stalker playlists)
|
||||
playlistName?: string;
|
||||
playlistType?: 'xtream' | 'stalker' | 'm3u-file' | 'm3u-text' | 'm3u-url';
|
||||
serverUrl?: string;
|
||||
portalUrl?: string;
|
||||
macAddress?: string;
|
||||
}): Promise<{ success: boolean; id?: number; error?: string }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'Downloads not available' };
|
||||
}
|
||||
|
||||
const folder = await this.loadDownloadFolder();
|
||||
if (!folder) {
|
||||
return { success: false, error: 'No download folder configured' };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await window.electron.downloadsStart({
|
||||
...data,
|
||||
downloadFolder: folder,
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[DownloadsService] Error starting download:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a download
|
||||
*/
|
||||
async cancelDownload(
|
||||
downloadId: number
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'Downloads not available' };
|
||||
}
|
||||
|
||||
try {
|
||||
return await window.electron.downloadsCancel(downloadId);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error canceling download:',
|
||||
error
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry a failed download
|
||||
*/
|
||||
async retryDownload(
|
||||
downloadId: number
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'Downloads not available' };
|
||||
}
|
||||
|
||||
const folder = await this.loadDownloadFolder();
|
||||
if (!folder) {
|
||||
return { success: false, error: 'No download folder configured' };
|
||||
}
|
||||
|
||||
try {
|
||||
return await window.electron.downloadsRetry(downloadId, folder);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error retrying download:',
|
||||
error
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a download from the list
|
||||
*/
|
||||
async removeDownload(
|
||||
downloadId: number
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'Downloads not available' };
|
||||
}
|
||||
|
||||
try {
|
||||
return await window.electron.downloadsRemove(downloadId);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error removing download:',
|
||||
error
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play a downloaded file
|
||||
*/
|
||||
async playDownload(
|
||||
filePath: string
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'Downloads not available' };
|
||||
}
|
||||
|
||||
try {
|
||||
return await window.electron.downloadsPlayFile(filePath);
|
||||
} catch (error) {
|
||||
console.error('[DownloadsService] Error playing file:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reveal file in system file manager
|
||||
*/
|
||||
async revealFile(
|
||||
filePath: string
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'Downloads not available' };
|
||||
}
|
||||
|
||||
try {
|
||||
return await window.electron.downloadsRevealFile(filePath);
|
||||
} catch (error) {
|
||||
console.error('[DownloadsService] Error revealing file:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a new download folder
|
||||
*/
|
||||
async selectFolder(): Promise<string | null> {
|
||||
if (!this.isAvailable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const folder = await window.electron.downloadsSelectFolder();
|
||||
if (folder) {
|
||||
this.downloadFolder.set(folder);
|
||||
// Save to settings
|
||||
await this.settingsStore.updateSettings({ downloadFolder: folder });
|
||||
}
|
||||
return folder;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error selecting folder:',
|
||||
error
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear completed/failed downloads
|
||||
*/
|
||||
async clearCompleted(
|
||||
playlistId?: string
|
||||
): Promise<{ success: boolean }> {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false };
|
||||
}
|
||||
|
||||
try {
|
||||
return await window.electron.downloadsClearCompleted(playlistId);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'[DownloadsService] Error clearing completed:',
|
||||
error
|
||||
);
|
||||
return { success: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get download progress as percentage
|
||||
*/
|
||||
getProgressPercent(item: DownloadItem): number {
|
||||
if (!item.totalBytes || item.totalBytes === 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.round(
|
||||
((item.bytesDownloaded || 0) / item.totalBytes) * 100
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get download item by xtreamId and playlistId
|
||||
*/
|
||||
getDownloadByContent(
|
||||
xtreamId: number,
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): DownloadItem | undefined {
|
||||
return this.downloads().find(
|
||||
(d) =>
|
||||
d.xtreamId === xtreamId &&
|
||||
d.playlistId === playlistId &&
|
||||
d.contentType === contentType
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if content is downloaded (completed status)
|
||||
*/
|
||||
isDownloaded(
|
||||
xtreamId: number,
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): boolean {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
return download?.status === 'completed' && !!download.filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if content is currently downloading or queued
|
||||
*/
|
||||
isDownloading(
|
||||
xtreamId: number,
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): boolean {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
return download?.status === 'downloading' || download?.status === 'queued';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file path for a downloaded content item
|
||||
*/
|
||||
getDownloadedFilePath(
|
||||
xtreamId: number,
|
||||
playlistId: string,
|
||||
contentType: 'vod' | 'episode'
|
||||
): string | undefined {
|
||||
const download = this.getDownloadByContent(xtreamId, playlistId, contentType);
|
||||
if (download?.status === 'completed') {
|
||||
return download.filePath;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to human readable string
|
||||
*/
|
||||
formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
||||
}
|
||||
}
|
||||
export {
|
||||
DownloadsService,
|
||||
type DownloadItem,
|
||||
type DownloadStatus,
|
||||
} from 'services';
|
||||
@@ -18,7 +18,7 @@ import { AppConfig } from '../../environments/environment';
|
||||
import {
|
||||
createPortalDebugRequestContext,
|
||||
logPortalDebugEvent,
|
||||
} from '../shared/utils/logger';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
createPortalDebugSuccessEvent,
|
||||
logPortalDebugEvent,
|
||||
logPortalDebugRequest,
|
||||
} from '../shared/utils/logger';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,109 +1 @@
|
||||
import { inject } from '@angular/core';
|
||||
import {
|
||||
patchState,
|
||||
signalStore,
|
||||
withHooks,
|
||||
withMethods,
|
||||
withState,
|
||||
} from '@ngrx/signals';
|
||||
import { StorageMap } from '@ngx-pwa/local-storage';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import {
|
||||
Language,
|
||||
Settings,
|
||||
STORE_KEY,
|
||||
StreamFormat,
|
||||
Theme,
|
||||
VideoPlayer,
|
||||
} from 'shared-interfaces';
|
||||
|
||||
const DEFAULT_SETTINGS: Settings = {
|
||||
player: VideoPlayer.VideoJs,
|
||||
streamFormat: StreamFormat.M3u8StreamFormat,
|
||||
language: Language.ENGLISH,
|
||||
showCaptions: false,
|
||||
showExternalPlaybackBar: true,
|
||||
theme: Theme.LightTheme,
|
||||
mpvPlayerPath: '',
|
||||
mpvReuseInstance: false,
|
||||
vlcPlayerPath: '',
|
||||
remoteControl: false,
|
||||
remoteControlPort: 8765,
|
||||
epgUrl: [],
|
||||
downloadFolder: '',
|
||||
};
|
||||
|
||||
export const SettingsStore = signalStore(
|
||||
{ providedIn: 'root' },
|
||||
withState<Settings>(DEFAULT_SETTINGS),
|
||||
withMethods((store, storage = inject(StorageMap)) => ({
|
||||
async loadSettings() {
|
||||
try {
|
||||
const stored = await firstValueFrom(
|
||||
storage.get(STORE_KEY.Settings)
|
||||
);
|
||||
if (stored) {
|
||||
patchState(store, {
|
||||
...DEFAULT_SETTINGS,
|
||||
...(stored as Settings),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load settings:', error);
|
||||
// Keep default settings if loading fails
|
||||
}
|
||||
},
|
||||
|
||||
async updateSettings(settings: Partial<Settings>) {
|
||||
patchState(store, settings);
|
||||
// Save the complete settings object, not just the partial update
|
||||
const completeSettings = this.getSettings();
|
||||
try {
|
||||
await firstValueFrom(
|
||||
storage.set(STORE_KEY.Settings, completeSettings)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
getSettings() {
|
||||
return {
|
||||
player: store.player(),
|
||||
streamFormat: store.streamFormat(),
|
||||
language: store.language(),
|
||||
showCaptions: store.showCaptions(),
|
||||
showExternalPlaybackBar: store.showExternalPlaybackBar(),
|
||||
theme: store.theme(),
|
||||
mpvPlayerPath: store.mpvPlayerPath(),
|
||||
mpvReuseInstance: store.mpvReuseInstance(),
|
||||
vlcPlayerPath: store.vlcPlayerPath(),
|
||||
remoteControl: store.remoteControl(),
|
||||
remoteControlPort: store.remoteControlPort(),
|
||||
epgUrl: store.epgUrl(),
|
||||
downloadFolder: store.downloadFolder(),
|
||||
};
|
||||
},
|
||||
|
||||
getDownloadFolder() {
|
||||
return store.downloadFolder();
|
||||
},
|
||||
|
||||
getPlayer() {
|
||||
return store.player();
|
||||
},
|
||||
|
||||
isEmbeddedPlayer() {
|
||||
return (
|
||||
store.player() === VideoPlayer.VideoJs ||
|
||||
store.player() === VideoPlayer.Html5Player
|
||||
);
|
||||
},
|
||||
})),
|
||||
withHooks({
|
||||
onInit(store) {
|
||||
store.loadSettings();
|
||||
},
|
||||
})
|
||||
);
|
||||
export { SettingsStore } from 'services';
|
||||
@@ -4,7 +4,7 @@ import { MatIcon } from '@angular/material/icon';
|
||||
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { PlaylistErrorViewComponent } from '../playlist-error-view/playlist-error-view.component';
|
||||
import { PlaylistErrorViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { ProgressCapsuleComponent } from '../../../xtream-electron/shared/progress-capsule/progress-capsule.component';
|
||||
import { WatchedBadgeComponent } from '../../../xtream-electron/shared/watched-badge/watched-badge.component';
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
export * from './category-view/category-view.component';
|
||||
export * from './content-card/content-card.component';
|
||||
export {
|
||||
CategoryViewComponent,
|
||||
ContentCardComponent,
|
||||
FavoritesLayoutComponent,
|
||||
PlaylistErrorViewComponent,
|
||||
PortalCollectionLiveShellComponent,
|
||||
PortalCollectionShellComponent,
|
||||
PortalEmptyStateComponent,
|
||||
SearchFormComponent,
|
||||
SearchLayoutComponent,
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
export * from './header/header.component';
|
||||
export * from './playlist-error-view/playlist-error-view.component';
|
||||
export * from './portal-collection-shell/portal-collection-shell.component';
|
||||
export * from './portal-empty-state/portal-empty-state.component';
|
||||
export * from './stalker-inline-detail/stalker-inline-detail.component';
|
||||
@@ -1,3 +1 @@
|
||||
// Canonical source: libs/ui/styles/_content-grid.scss
|
||||
// This stub forwards the mixins for components that @use this file directly.
|
||||
@forward '../../../../../../libs/ui/styles/content-grid';
|
||||
@forward '../../../../../../libs/portal/shared/ui/src/lib/styles/content-grid';
|
||||
@@ -1,3 +1 @@
|
||||
// Canonical source: libs/ui/styles/_portal-layout.scss
|
||||
// This stub forwards the mixin for components that @use this file directly.
|
||||
@forward '../../../../../../libs/ui/styles/portal-layout';
|
||||
@forward '../../../../../../libs/portal/shared/ui/src/lib/styles/portal-layout';
|
||||
@@ -1,3 +1 @@
|
||||
// Canonical source: libs/ui/styles/_portal-sidebar.scss
|
||||
// This stub forwards the rules for components that @use this file directly.
|
||||
@forward '../../../../../../libs/ui/styles/portal-sidebar';
|
||||
@forward '../../../../../../libs/portal/shared/ui/src/lib/styles/portal-sidebar';
|
||||
@@ -4,8 +4,8 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, map, switchMap } from 'rxjs';
|
||||
import { PlaylistsService } from 'services';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
import { matchesFavoriteById } from '../stalker-vod.utils';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import { matchesFavoriteById } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-favorites-button',
|
||||
|
||||
@@ -11,25 +11,25 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistsService } from 'services';
|
||||
import { EpgItem, VodDetailsItem } from 'shared-interfaces';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { PortalCollectionLiveShellComponent } from '../../shared/components/portal-collection-live-shell/portal-collection-live-shell.component';
|
||||
import { PortalCollectionLiveShellComponent } from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
PortalCollectionMode,
|
||||
PortalCollectionShellComponent,
|
||||
PortalCollectionShellLayout,
|
||||
} from '../../shared/components/portal-collection-shell/portal-collection-shell.component';
|
||||
import { StalkerInlineDetailComponent } from '../../shared/components/stalker-inline-detail/stalker-inline-detail.component';
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
import { StalkerInlineDetailComponent } from '../stalker-inline-detail/stalker-inline-detail.component';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
queryParamSignal,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
import { createPortalCollectionContext } from '../../shared/utils/portal-collection-context';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createPortalCollectionContext } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
buildStandardCollectionCategories,
|
||||
filterCollectionBucket,
|
||||
} from '../../shared/utils/portal-collection-items';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { FavoritesContextService } from '../../workspace/favorites-context.service';
|
||||
import { StalkerSelectedVodItem, StalkerVodSource } from '../models';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import { FavoritesContextService } from '@iptvnator/portal/shared/util';
|
||||
import { StalkerSelectedVodItem, StalkerVodSource } from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
clearStalkerDetailViewState,
|
||||
createStalkerInlineDetailState,
|
||||
@@ -40,9 +40,9 @@ import {
|
||||
isSelectedStalkerVodFavorite,
|
||||
normalizeStalkerVodDetailsItem,
|
||||
toggleStalkerVodFavorite,
|
||||
} from '../stalker-vod.utils';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerCollectionChannelsListComponent } from '../stalker-collection-channels-list/stalker-collection-channels-list.component';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
const STALKER_RECENT_LAYOUT: Omit<
|
||||
PortalCollectionShellLayout,
|
||||
|
||||
+3
-3
@@ -13,9 +13,9 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { ChannelListItemComponent } from 'components';
|
||||
import { EpgItem, EpgProgram } from 'shared-interfaces';
|
||||
import { StalkerVodSource } from '../models';
|
||||
import { normalizeStalkerEntityId } from '../stalker-vod.utils';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
import { StalkerVodSource } from '@iptvnator/portal/stalker/data-access';
|
||||
import { normalizeStalkerEntityId } from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-stalker-collection-channels-list',
|
||||
|
||||
@@ -11,22 +11,22 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistsService } from 'services';
|
||||
import { EpgItem, VodDetailsItem } from 'shared-interfaces';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { PortalCollectionLiveShellComponent } from '../../shared/components/portal-collection-live-shell/portal-collection-live-shell.component';
|
||||
import { PortalCollectionLiveShellComponent } from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
PortalCollectionMode,
|
||||
PortalCollectionShellComponent,
|
||||
PortalCollectionShellLayout,
|
||||
} from '../../shared/components/portal-collection-shell/portal-collection-shell.component';
|
||||
import { StalkerInlineDetailComponent } from '../../shared/components/stalker-inline-detail/stalker-inline-detail.component';
|
||||
import { queryParamSignal } from '../../shared/navigation/portal-route.utils';
|
||||
import { createPortalCollectionContext } from '../../shared/utils/portal-collection-context';
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
import { StalkerInlineDetailComponent } from '../stalker-inline-detail/stalker-inline-detail.component';
|
||||
import { queryParamSignal } from '@iptvnator/portal/shared/util';
|
||||
import { createPortalCollectionContext } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
buildStandardCollectionCategories,
|
||||
filterCollectionBucket,
|
||||
} from '../../shared/utils/portal-collection-items';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { FavoritesContextService } from '../../workspace/favorites-context.service';
|
||||
import { StalkerFavoriteItem } from '../models';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import { FavoritesContextService } from '@iptvnator/portal/shared/util';
|
||||
import { StalkerFavoriteItem } from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
clearStalkerDetailViewState,
|
||||
createStalkerInlineDetailState,
|
||||
@@ -38,9 +38,9 @@ import {
|
||||
normalizeStalkerEntityId,
|
||||
normalizeStalkerFavoriteItem,
|
||||
toggleStalkerVodFavorite,
|
||||
} from '../stalker-vod.utils';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerCollectionChannelsListComponent } from '../stalker-collection-channels-list/stalker-collection-channels-list.component';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
const STALKER_FAVORITES_LAYOUT: PortalCollectionShellLayout = {};
|
||||
|
||||
|
||||
File renamed without changes.
+4
-4
@@ -5,11 +5,11 @@ import {
|
||||
input,
|
||||
output,
|
||||
} from '@angular/core';
|
||||
import { ExternalPlaybackService } from '../../../services/external-playback.service';
|
||||
import { ExternalPlaybackService } from '../../services/external-playback.service';
|
||||
import { VodDetailsItem } from 'shared-interfaces';
|
||||
import { VodDetailsComponent } from '../../../xtream-electron/vod-details/vod-details.component';
|
||||
import { StalkerSelectedVodItem } from '../../../stalker/models/stalker-favorite-item.interface';
|
||||
import { StalkerSeriesViewComponent } from '../../../stalker/stalker-series-view/stalker-series-view.component';
|
||||
import { VodDetailsComponent } from '../../xtream-electron/vod-details/vod-details.component';
|
||||
import { StalkerSelectedVodItem } from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerSeriesViewComponent } from '../stalker-series-view/stalker-series-view.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-stalker-inline-detail',
|
||||
+8
-8
@@ -31,22 +31,22 @@ import { EpgItem, EpgProgram } from 'shared-interfaces';
|
||||
import { EpgViewComponent, WebPlayerViewComponent } from 'shared-portals';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { CategoryViewComponent } from '../../shared/components/category-view/category-view.component';
|
||||
import { PlaylistErrorViewComponent } from '../../shared/components/playlist-error-view/playlist-error-view.component';
|
||||
import { PortalEmptyStateComponent } from '../../shared/components/portal-empty-state/portal-empty-state.component';
|
||||
import { isWorkspaceLayoutRoute } from '../../shared/navigation/portal-route.utils';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { PlaylistErrorViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { PortalEmptyStateComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { isWorkspaceLayoutRoute } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
getAdjacentChannelItem,
|
||||
getChannelItemByNumber,
|
||||
} from '../../shared/services/remote-channel-navigation.util';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
StalkerCategoryItem,
|
||||
StalkerFavoriteItem,
|
||||
StalkerItvChannel,
|
||||
} from '../models';
|
||||
import { normalizeStalkerEntityId } from '../stalker-vod.utils';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { normalizeStalkerEntityId } from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-stalker-live-stream-layout',
|
||||
|
||||
@@ -5,10 +5,10 @@ import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistSwitcherComponent, ResizableDirective } from 'components';
|
||||
import { CategoryViewComponent } from '../shared/components/category-view/category-view.component';
|
||||
import { PlaylistErrorViewComponent } from '../shared/components/playlist-error-view/playlist-error-view.component';
|
||||
import { isWorkspaceLayoutRoute } from '../shared/navigation/portal-route.utils';
|
||||
import { StalkerStore } from './stalker.store';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { PlaylistErrorViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { isWorkspaceLayoutRoute } from '@iptvnator/portal/shared/util';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-stalker-main-container',
|
||||
|
||||
@@ -12,20 +12,20 @@ import {
|
||||
StalkerPortalActions,
|
||||
VodDetailsItem,
|
||||
} from 'shared-interfaces';
|
||||
import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
|
||||
import { SearchLayoutComponent } from '../../shared/components/search-layout/search-layout.component';
|
||||
import { StalkerInlineDetailComponent } from '../../shared/components/stalker-inline-detail/stalker-inline-detail.component';
|
||||
import { StalkerContentTypes } from '../stalker-content-types';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
import { ContentCardComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { SearchLayoutComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { StalkerInlineDetailComponent } from '../stalker-inline-detail/stalker-inline-detail.component';
|
||||
import { StalkerContentTypes } from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
queryParamSignal,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
StalkerSelectedVodItem,
|
||||
StalkerVodSource,
|
||||
} from '../models';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
buildStalkerSelectedVodItem,
|
||||
clearStalkerDetailViewState,
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
isSelectedStalkerVodFavorite,
|
||||
isStalkerSeriesFlag,
|
||||
toggleStalkerVodFavorite,
|
||||
} from '../stalker-vod.utils';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
interface StalkerFilter {
|
||||
key: string;
|
||||
|
||||
@@ -11,14 +11,14 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { FavoritesButtonComponent } from '../favorites-button/favorites-button.component';
|
||||
import { ContentHeroComponent } from 'components';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import { SeasonContainerComponent } from '../../xtream-electron/season-container/season-container.component';
|
||||
import {
|
||||
ResolvedPortalPlayback,
|
||||
XtreamSerieEpisode,
|
||||
} from 'shared-interfaces';
|
||||
import { XtreamStore } from '../../xtream-electron/stores/xtream.store';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
getVodSeriesSeasonKey,
|
||||
isVodSeriesItem,
|
||||
@@ -29,9 +29,9 @@ import {
|
||||
StalkerMappedEpisode,
|
||||
StalkerSeriesSeasonVm,
|
||||
VodSeriesSeasonVm,
|
||||
} from '../stalker-series.adapters';
|
||||
import { StalkerSelectedVodItem, StalkerVodSource } from '../models';
|
||||
import { normalizeStalkerVodDetailsItem } from '../stalker-vod.utils';
|
||||
} from '@iptvnator/portal/stalker/data-access';
|
||||
import { StalkerSelectedVodItem, StalkerVodSource } from '@iptvnator/portal/stalker/data-access';
|
||||
import { normalizeStalkerVodDetailsItem } from '@iptvnator/portal/stalker/data-access';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { ExternalPlaybackService } from '../../services/external-playback.service';
|
||||
import { PortalInlinePlayerComponent } from '../../shared/components/portal-inline-player/portal-inline-player.component';
|
||||
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
import { Store } from '@ngrx/store';
|
||||
import { PlaylistActions, selectPlaylistById } from 'm3u-state';
|
||||
import { map, switchMap } from 'rxjs';
|
||||
import { PortalRailSection } from '../../shared/navigation/portal-rail-links';
|
||||
import { PortalRailSection } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
resolveCurrentPortalSection,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { NavigationComponent } from '../../xtream-electron/navigation/navigation.component';
|
||||
import { XtreamStore } from '../../xtream-electron/stores/xtream.store';
|
||||
import { StalkerStore } from '../stalker.store';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import { NavigationComponent } from '../../portal-shared/navigation/navigation.component';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-stalker-shell',
|
||||
|
||||
@@ -28,7 +28,7 @@ export const stalkerRoutes: Route[] = [
|
||||
},
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'../xtream-electron/category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
},
|
||||
{
|
||||
@@ -39,7 +39,7 @@ export const stalkerRoutes: Route[] = [
|
||||
},
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'../xtream-electron/category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
},
|
||||
],
|
||||
@@ -68,7 +68,7 @@ export const stalkerRoutes: Route[] = [
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'../xtream-electron/category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
data: {
|
||||
api: 'stalker',
|
||||
@@ -79,7 +79,7 @@ export const stalkerRoutes: Route[] = [
|
||||
path: ':categoryId',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'../xtream-electron/category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
data: {
|
||||
api: 'stalker',
|
||||
@@ -113,7 +113,7 @@ export const stalkerRoutes: Route[] = [
|
||||
path: 'downloads',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'../xtream-electron/downloads/downloads.component'
|
||||
'@iptvnator/portal/downloads/feature'
|
||||
).then((c) => c.DownloadsComponent),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,35 +1 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
|
||||
/**
|
||||
* Lightweight service that bridges the favorites/recently-viewed routed
|
||||
* components (inside the router-outlet) with the WorkspaceShellComponent
|
||||
* (the outer shell that renders the context panel aside).
|
||||
*
|
||||
* The routed component pushes its category list here via an effect;
|
||||
* the workspace shell reads it to populate the context panel.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class FavoritesContextService {
|
||||
/** Category list managed by the active routed component (Favorites / RecentlyViewed). */
|
||||
readonly categories = signal<XtreamCategory[]>([]);
|
||||
|
||||
/** Currently selected category id — shared between panel and content. */
|
||||
readonly selectedCategoryId = signal<string>('all');
|
||||
|
||||
/** Called by the context panel when the user clicks a category. */
|
||||
setCategoryId(id: string): void {
|
||||
this.selectedCategoryId.set(id);
|
||||
}
|
||||
|
||||
/** Called by the routed component to push its category list into the panel. */
|
||||
setCategories(categories: XtreamCategory[]): void {
|
||||
this.categories.set(categories);
|
||||
}
|
||||
|
||||
/** Reset to defaults when navigating away from favorites/recent sections. */
|
||||
reset(): void {
|
||||
this.categories.set([]);
|
||||
this.selectedCategoryId.set('all');
|
||||
}
|
||||
}
|
||||
export { FavoritesContextService } from '@iptvnator/portal/shared/util';
|
||||
@@ -29,8 +29,8 @@ import {
|
||||
import { EpgViewComponent } from 'shared-portals';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { XtreamApiService } from '../../xtream-electron/services/xtream-api.service';
|
||||
import { XtreamUrlService } from '../../xtream-electron/services/xtream-url.service';
|
||||
import { XtreamApiService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamUrlService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { GlobalFavoritesListComponent } from './global-favorites-list.component';
|
||||
import { GlobalFavoritesService } from './global-favorites.service';
|
||||
import { UnifiedFavoriteChannel } from './unified-favorite-channel.interface';
|
||||
|
||||
@@ -5,14 +5,14 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { Router } from '@angular/router';
|
||||
import { CategoryViewComponent } from '../shared/components/category-view/category-view.component';
|
||||
import { PlaylistErrorViewComponent } from '../shared/components/playlist-error-view/playlist-error-view.component';
|
||||
import { StalkerStore } from '../stalker/stalker.store';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { PlaylistErrorViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import {
|
||||
CategoryManagementDialogComponent,
|
||||
CategoryManagementDialogData,
|
||||
} from '../xtream-electron/category-management-dialog/category-management-dialog.component';
|
||||
import { XtreamStore } from '../xtream-electron/stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
type WorkspaceProvider = 'xtreams' | 'stalker' | 'playlists';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { CategoryViewComponent } from '../shared/components/category-view/category-view.component';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { FavoritesContextService } from './favorites-context.service';
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PlaylistsService } from 'services';
|
||||
import { DownloadsService } from '../services/downloads.service';
|
||||
import { ExternalPlaybackService } from '../services/external-playback.service';
|
||||
import { SettingsStore } from '../services/settings-store.service';
|
||||
import { XtreamStore } from '../xtream-electron/stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { FavoritesContextService } from './favorites-context.service';
|
||||
import { SettingsContextService } from './settings-context.service';
|
||||
import { WorkspaceShellComponent } from './workspace-shell.component';
|
||||
|
||||
@@ -43,12 +43,12 @@ import { ExternalPlaybackDockComponent } from '../shared/components/external-pla
|
||||
import {
|
||||
buildPortalRailLinks,
|
||||
PortalRailLink,
|
||||
} from '../shared/navigation/portal-rail-links';
|
||||
import { PortalRailLinksComponent } from '../shared/navigation/portal-rail-links.component';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { PortalRailLinksComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { AccountInfoComponent } from '../xtream-electron/account-info/account-info.component';
|
||||
import { GlobalRecentlyViewedComponent } from '../xtream-electron/recently-viewed/global-recently-viewed.component';
|
||||
import { GlobalSearchResultsComponent } from '../xtream-electron/search-results/global-search-results.component';
|
||||
import { XtreamStore } from '../xtream-electron/stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { FavoritesContextService } from './favorites-context.service';
|
||||
import { SettingsContextService } from './settings-context.service';
|
||||
import {
|
||||
|
||||
@@ -5,9 +5,9 @@ import { MatListModule } from '@angular/material/list';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { selectActivePlaylist } from 'm3u-state';
|
||||
import { XtreamApiService } from '../services/xtream-api.service';
|
||||
import { XtreamAccountInfo } from './account-info.interface';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { XtreamApiService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamAccountInfo } from '@iptvnator/portal/xtream/data-access';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account-info',
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { DatabaseService, XCategoryFromDb } from 'services';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
|
||||
export interface CategoryManagementDialogData {
|
||||
playlistId: string;
|
||||
|
||||
@@ -16,23 +16,23 @@ import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { selectActivePlaylist } from 'm3u-state';
|
||||
import { BehaviorSubject, switchMap } from 'rxjs';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { PortalCollectionLiveShellComponent } from '../../shared/components/portal-collection-live-shell/portal-collection-live-shell.component';
|
||||
import { PortalCollectionLiveShellComponent } from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
PortalCollectionMode,
|
||||
PortalCollectionShellComponent,
|
||||
PortalCollectionShellLayout,
|
||||
} from '../../shared/components/portal-collection-shell/portal-collection-shell.component';
|
||||
import { queryParamSignal } from '../../shared/navigation/portal-route.utils';
|
||||
import { createPortalCollectionContext } from '../../shared/utils/portal-collection-context';
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
import { queryParamSignal } from '@iptvnator/portal/shared/util';
|
||||
import { createPortalCollectionContext } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
buildStandardCollectionCategories,
|
||||
filterCollectionBucket,
|
||||
} from '../../shared/utils/portal-collection-items';
|
||||
import { FavoritesContextService } from '../../workspace/favorites-context.service';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { FavoritesContextService } from '@iptvnator/portal/shared/util';
|
||||
import { PortalChannelsListComponent } from '../portal-channels-list/portal-channels-list.component';
|
||||
import { FavoriteItem } from '../services/favorite-item.interface';
|
||||
import { FavoritesService } from '../services/favorites.service';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { FavoriteItem } from '@iptvnator/portal/xtream/data-access';
|
||||
import { FavoritesService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
const XTREAM_FAVORITES_LAYOUT: PortalCollectionShellLayout = {};
|
||||
const XTREAM_COLLECTION_LABELS = {
|
||||
|
||||
@@ -27,9 +27,9 @@ import { PlaylistSwitcherComponent, ResizableDirective } from 'components';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { EpgViewComponent, WebPlayerViewComponent } from 'shared-portals';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { CategoryViewComponent } from '../../shared/components/category-view/category-view.component';
|
||||
import { PortalEmptyStateComponent } from '../../shared/components/portal-empty-state/portal-empty-state.component';
|
||||
import { isWorkspaceLayoutRoute } from '../../shared/navigation/portal-route.utils';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { PortalEmptyStateComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { isWorkspaceLayoutRoute } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
getAdjacentChannelItem,
|
||||
getChannelItemByNumber,
|
||||
@@ -39,8 +39,8 @@ import {
|
||||
CategoryManagementDialogData,
|
||||
} from '../category-management-dialog/category-management-dialog.component';
|
||||
import { PortalChannelsListComponent } from '../portal-channels-list/portal-channels-list.component';
|
||||
import { FavoritesService } from '../services/favorites.service';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { FavoritesService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
type LiveChannelSortMode = 'server' | 'name-asc' | 'name-desc';
|
||||
const LIVE_CHANNEL_SORT_STORAGE_KEY = 'xtream-live-channel-sort-mode';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { PlayerContentInfo } from 'shared-interfaces';
|
||||
import { WebPlayerViewComponent } from 'shared-portals';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
export interface PlayerDialogData {
|
||||
streamUrl: string;
|
||||
|
||||
+4
-4
@@ -29,10 +29,10 @@ import {
|
||||
XtreamItem,
|
||||
} from 'shared-interfaces';
|
||||
import { ChannelListItemComponent } from 'components';
|
||||
import { EpgQueueService } from '../services/epg-queue.service';
|
||||
import { XtreamCredentials } from '../services/xtream-api.service';
|
||||
import { FavoritesService } from '../services/favorites.service';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { EpgQueueService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamCredentials } from '@iptvnator/portal/xtream/data-access';
|
||||
import { FavoritesService } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
type LiveChannelSortMode = 'server' | 'name-asc' | 'name-desc';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { ContentType } from '../xtream-state';
|
||||
import { ContentCardComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { ContentType } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-recently-added',
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { RecentlyViewedComponent } from './recently-viewed.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-global-recently-viewed',
|
||||
|
||||
@@ -21,28 +21,28 @@ import groupBy from 'lodash/groupBy';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DatabaseService, PlaylistsService } from 'services';
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
|
||||
import { ContentCardComponent } from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
PortalCollectionLiveShellComponent,
|
||||
} from '../../shared/components/portal-collection-live-shell/portal-collection-live-shell.component';
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
PortalCollectionMode,
|
||||
PortalCollectionShellComponent,
|
||||
PortalCollectionShellLayout,
|
||||
} from '../../shared/components/portal-collection-shell/portal-collection-shell.component';
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
queryParamSignal,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
import { createPortalCollectionContext } from '../../shared/utils/portal-collection-context';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createPortalCollectionContext } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
buildStandardCollectionCategories,
|
||||
filterCollectionBucket,
|
||||
} from '../../shared/utils/portal-collection-items';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { FavoritesContextService } from '../../workspace/favorites-context.service';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import { FavoritesContextService } from '@iptvnator/portal/shared/util';
|
||||
import { PortalChannelsListComponent } from '../portal-channels-list/portal-channels-list.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
const XTREAM_RECENT_LAYOUT: Omit<
|
||||
PortalCollectionShellLayout,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SearchResultsComponent } from './search-results.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-global-search-results',
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ActivatedRoute, Router, convertToParamMap } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
import { DatabaseService } from 'services';
|
||||
import { SearchResultsComponent } from './search-results.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
class MockXtreamStore {
|
||||
readonly searchTerm = signal('');
|
||||
|
||||
@@ -23,17 +23,17 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import { DatabaseService } from 'services';
|
||||
import { ContentCardComponent } from '../../shared/components/content-card/content-card.component';
|
||||
import { SearchLayoutComponent } from '../../shared/components/search-layout/search-layout.component';
|
||||
import { ContentCardComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { SearchLayoutComponent } from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
queryParamSignal,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { XtreamContentItem } from '../data-sources/xtream-data-source.interface';
|
||||
import { SearchFilters } from '../stores/features/with-search.feature';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { ContentType } from '../xtream-state';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
import { XtreamContentItem } from '@iptvnator/portal/xtream/data-access';
|
||||
import { SearchFilters } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { ContentType } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
interface SearchResultsData {
|
||||
isGlobalSearch: boolean;
|
||||
|
||||
@@ -7,12 +7,12 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { XtreamSerieEpisode, XtreamSerieEpisodeInfo } from 'shared-interfaces';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { StalkerStore } from '../../stalker/stalker.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import { ProgressCapsuleComponent } from '../shared/progress-capsule/progress-capsule.component';
|
||||
import { WatchedBadgeComponent } from '../shared/watched-badge/watched-badge.component';
|
||||
import { DownloadsService } from '../../services/downloads.service';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
|
||||
type EpisodeViewMode = 'grid' | 'list';
|
||||
const EPISODE_VIEW_MODE_KEY = 'iptvnator_episode_view_mode';
|
||||
|
||||
@@ -14,7 +14,7 @@ import { PlayerService } from '../../services/player.service';
|
||||
import { ExternalPlaybackService } from '../../services/external-playback.service';
|
||||
import { PortalInlinePlayerComponent } from '../../shared/components/portal-inline-player/portal-inline-player.component';
|
||||
import { SeasonContainerComponent } from '../season-container/season-container.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
@Component({
|
||||
selector: 'app-serial-details',
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './xtream-api.service';
|
||||
export * from './xtream-url.service';
|
||||
@@ -23,9 +23,9 @@ import { ExternalPlaybackService } from '../../services/external-playback.servic
|
||||
import { PlayerService } from '../../services/player.service';
|
||||
import { SettingsStore } from '../../services/settings-store.service';
|
||||
import { PortalInlinePlayerComponent } from '../../shared/components/portal-inline-player/portal-inline-player.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { SafePipe } from '@iptvnator/pipes';
|
||||
import { createLogger } from '../../shared/utils/logger';
|
||||
import { createLogger } from '@iptvnator/portal/shared/util';
|
||||
|
||||
/**
|
||||
* Route-based VOD details container for Xtream.
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { DownloadsService } from '../../services/downloads.service';
|
||||
import { ExternalPlaybackService } from '../../services/external-playback.service';
|
||||
import { SafePipe } from '@iptvnator/pipes';
|
||||
import { StalkerStore } from '../../stalker/stalker.store';
|
||||
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
|
||||
import { PortalInlinePlayerComponent } from '../../shared/components/portal-inline-player/portal-inline-player.component';
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,14 +11,14 @@ import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { PlaylistSwitcherComponent, ResizableDirective } from 'components';
|
||||
import { XtreamCategory } from 'shared-interfaces';
|
||||
import { CategoryViewComponent } from '../shared/components/category-view/category-view.component';
|
||||
import { isWorkspaceLayoutRoute } from '../shared/navigation/portal-route.utils';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { isWorkspaceLayoutRoute } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
CategoryManagementDialogComponent,
|
||||
CategoryManagementDialogData,
|
||||
} from './category-management-dialog/category-management-dialog.component';
|
||||
import { XtreamStore } from './stores/xtream.store';
|
||||
import { XtreamCategorySortMode } from './stores/features/with-selection.feature';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamCategorySortMode } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
const XTREAM_CATEGORY_SORT_STORAGE_KEY = 'xtream-category-sort-mode';
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ import { Store } from '@ngrx/store';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { PlaylistActions } from 'm3u-state';
|
||||
import { filter } from 'rxjs';
|
||||
import { PortalRailSection } from '../../shared/navigation/portal-rail-links';
|
||||
import { PortalRailSection } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
resolveCurrentPortalSection,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { LoadingOverlayComponent } from '../loading-overlay/loading-overlay.component';
|
||||
import { NavigationComponent } from '../navigation/navigation.component';
|
||||
import { XtreamStore } from '../stores/xtream.store';
|
||||
import { NavigationComponent } from '../../portal-shared/navigation/navigation.component';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
|
||||
@Component({
|
||||
templateUrl: './xtream-shell.component.html',
|
||||
|
||||
@@ -40,14 +40,14 @@ export const xtreamRoutes: Route[] = [
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'./category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
},
|
||||
{
|
||||
path: ':categoryId',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'./category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
},
|
||||
{
|
||||
@@ -70,14 +70,14 @@ export const xtreamRoutes: Route[] = [
|
||||
path: '',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'./category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
},
|
||||
{
|
||||
path: ':categoryId',
|
||||
loadComponent: () =>
|
||||
import(
|
||||
'./category-content-view/category-content-view.component'
|
||||
'../portal-shared/category-content-view/category-content-view.component'
|
||||
).then((c) => c.CategoryContentViewComponent),
|
||||
},
|
||||
{
|
||||
@@ -120,7 +120,7 @@ export const xtreamRoutes: Route[] = [
|
||||
{
|
||||
path: 'downloads',
|
||||
loadComponent: () =>
|
||||
import('./downloads/downloads.component').then(
|
||||
import('@iptvnator/portal/downloads/feature').then(
|
||||
(c) => c.DownloadsComponent
|
||||
),
|
||||
},
|
||||
|
||||
+74
-2
@@ -17,8 +17,80 @@ export default [
|
||||
allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'],
|
||||
depConstraints: [
|
||||
{
|
||||
sourceTag: '*',
|
||||
onlyDependOnLibsWithTags: ['*'],
|
||||
sourceTag: 'type:app',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'type:feature',
|
||||
'type:ui',
|
||||
'type:data-access',
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:feature',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'type:feature',
|
||||
'type:ui',
|
||||
'type:data-access',
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:ui',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'type:ui',
|
||||
'type:data-access',
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:data-access',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'type:data-access',
|
||||
'type:util',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'type:util',
|
||||
onlyDependOnLibsWithTags: ['type:util'],
|
||||
},
|
||||
{
|
||||
sourceTag: 'domain:portal-shared',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'domain:portal-shared',
|
||||
'domain:m3u',
|
||||
'domain:playback',
|
||||
'scope:shared',
|
||||
'scope:portal',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'domain:xtream',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'domain:xtream',
|
||||
'domain:portal-shared',
|
||||
'domain:m3u',
|
||||
'scope:shared',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'domain:stalker',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'domain:stalker',
|
||||
'domain:portal-shared',
|
||||
'domain:m3u',
|
||||
'scope:shared',
|
||||
],
|
||||
},
|
||||
{
|
||||
sourceTag: 'domain:workspace',
|
||||
onlyDependOnLibsWithTags: [
|
||||
'domain:workspace',
|
||||
'domain:portal-shared',
|
||||
'domain:xtream',
|
||||
'domain:stalker',
|
||||
'domain:m3u',
|
||||
'scope:shared',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"sourceRoot": "libs/m3u-state/src",
|
||||
"prefix": "lib",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"tags": ["scope:playlist", "domain:m3u", "type:data-access"],
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "portal-downloads-feature",
|
||||
"$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/portal/downloads/feature/src",
|
||||
"prefix": "lib",
|
||||
"projectType": "library",
|
||||
"tags": ["scope:portal", "domain:portal-shared", "type:feature"],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './lib/downloads.component';
|
||||
File renamed without changes.
File renamed without changes.
+10
-9
@@ -14,22 +14,23 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { ResizableDirective } from 'components';
|
||||
import { firstValueFrom, map } from 'rxjs';
|
||||
import { DatabaseService, PlaylistsService } from 'services';
|
||||
import {
|
||||
DownloadItem,
|
||||
DatabaseService,
|
||||
type DownloadItem,
|
||||
DownloadsService,
|
||||
} from '../../services/downloads.service';
|
||||
import { CategoryViewComponent } from '../../shared/components/category-view/category-view.component';
|
||||
PlaylistsService,
|
||||
} from 'services';
|
||||
import { CategoryViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
isWorkspaceLayoutRoute,
|
||||
queryParamSignal,
|
||||
} from '../../shared/navigation/portal-route.utils';
|
||||
import { createPortalCollectionContext } from '../../shared/utils/portal-collection-context';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { createPortalCollectionContext } from '@iptvnator/portal/shared/util';
|
||||
import {
|
||||
buildStandardCollectionCategories,
|
||||
filterCollectionBucket,
|
||||
} from '../../shared/utils/portal-collection-items';
|
||||
import { FavoritesContextService } from '../../workspace/favorites-context.service';
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { FavoritesContextService } from '@iptvnator/portal/shared/util';
|
||||
|
||||
type PortalSource = 'xtream' | 'stalker';
|
||||
const DOWNLOAD_COLLECTION_LABELS = {
|
||||
@@ -44,7 +45,7 @@ const DOWNLOAD_COLLECTION_LABELS = {
|
||||
templateUrl: './downloads.component.html',
|
||||
styleUrls: [
|
||||
'./downloads.component.scss',
|
||||
'../../shared/styles/portal-sidebar.scss',
|
||||
'../../../../shared/ui/src/lib/styles/portal-sidebar.scss',
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"isolatedModules": true,
|
||||
"target": "es2022",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"emitDecoratorMetadata": false,
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "portal-shared-ui",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/portal/shared/ui/src",
|
||||
"prefix": "lib",
|
||||
"projectType": "library",
|
||||
"tags": ["scope:portal", "domain:portal-shared", "type:ui"],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export * from './lib/components/category-view/category-view.component';
|
||||
export * from './lib/components/content-card/content-card.component';
|
||||
export * from './lib/components/favorites-layout/favorites-layout.component';
|
||||
export * from './lib/components/playlist-error-view/playlist-error-view.component';
|
||||
export * from './lib/components/portal-collection-live-shell/portal-collection-live-shell.component';
|
||||
export * from './lib/components/portal-collection-shell/portal-collection-shell.component';
|
||||
export * from './lib/components/portal-empty-state/portal-empty-state.component';
|
||||
export * from './lib/components/search-form/search-form.component';
|
||||
export * from './lib/components/search-layout/search-layout.component';
|
||||
export * from './lib/navigation/portal-rail-links.component';
|
||||
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
||||
@use '../../../../nav-list.scss';
|
||||
@use '../../../../../../../../apps/web/src/nav-list.scss';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,5 +1,5 @@
|
||||
@use '@angular/material' as mat;
|
||||
@use '../../../shared/styles/content-grid' as grid;
|
||||
@use '../../styles/content-grid' as grid;
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { PlaylistSwitcherComponent, ResizableDirective } from 'components';
|
||||
import { isWorkspaceLayoutRoute } from '../../navigation/portal-route.utils';
|
||||
import { isWorkspaceLayoutRoute } from '@iptvnator/portal/shared/util';
|
||||
import { CategoryViewComponent } from '../category-view/category-view.component';
|
||||
import { ContentCardComponent } from '../content-card/content-card.component';
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -8,7 +8,7 @@ import {
|
||||
RouterLink,
|
||||
RouterLinkActive,
|
||||
} from '@angular/router';
|
||||
import { PortalRailLink, PortalRailSection } from './portal-rail-links';
|
||||
import { PortalRailLink, PortalRailSection } from '@iptvnator/portal/shared/util';
|
||||
|
||||
@Component({
|
||||
selector: 'app-portal-rail-links',
|
||||
@@ -0,0 +1,3 @@
|
||||
// Canonical source: libs/ui/styles/_content-grid.scss
|
||||
// This stub forwards the mixins for components that @use this file directly.
|
||||
@forward '../../../../../../../libs/ui/styles/content-grid';
|
||||
@@ -0,0 +1,3 @@
|
||||
// Canonical source: libs/ui/styles/_portal-layout.scss
|
||||
// This stub forwards the mixin for components that @use this file directly.
|
||||
@forward '../../../../../../../libs/ui/styles/portal-layout';
|
||||
@@ -0,0 +1,3 @@
|
||||
// Canonical source: libs/ui/styles/_portal-sidebar.scss
|
||||
// This stub forwards the rules for components that @use this file directly.
|
||||
@forward '../../../../../../../libs/ui/styles/portal-sidebar';
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"isolatedModules": true,
|
||||
"target": "es2022",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"emitDecoratorMetadata": false,
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|
||||
Loaded 100 of 198 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user