mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
The root shell imported WindowControlsComponent and DialogService through the @iptvnator/ui/components barrel, and esbuild keeps every Angular component module a barrel re-exports, so channel lists, EPG views, @angular/forms, date-fns and the whole Stalker data layer sat in main.js. The shell now uses file-level entries, the Stalker connection editor is a lazy proxy, and the release-notes and external-player info dialogs load on demand with a handled failure path. renderer.initialBytes 2,714,336 -> 1,626,019 bytes (-40%); the baseline is lowered to the ubuntu ratchet measurement and the production/PWA initial budgets drop to 1.8/2 MB. J1: did-finish-load about -16 ms, first card within noise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
import { Injectable, inject } from '@angular/core';
|
|
import { MatDialog } from '@angular/material/dialog';
|
|
import { DataService } from '@iptvnator/services';
|
|
import {
|
|
ExternalPlayerSession,
|
|
OPEN_MPV_PLAYER,
|
|
OPEN_VLC_PLAYER,
|
|
PlayerContentInfo,
|
|
ResolvedPortalPlayback,
|
|
VideoPlayer,
|
|
} from '@iptvnator/shared/interfaces';
|
|
import type { ExternalPlayerName } from '@iptvnator/shared/interfaces';
|
|
import { SettingsStore } from './settings-store.service';
|
|
|
|
@Injectable({
|
|
providedIn: 'root',
|
|
})
|
|
export class PlayerService {
|
|
private dialog = inject(MatDialog);
|
|
private dataService = inject(DataService);
|
|
private settingsStore = inject(SettingsStore);
|
|
|
|
isEmbeddedPlayer(
|
|
player = this.settingsStore.player() ?? VideoPlayer.VideoJs
|
|
): boolean {
|
|
return (
|
|
player === VideoPlayer.VideoJs ||
|
|
player === VideoPlayer.Html5Player ||
|
|
player === VideoPlayer.ArtPlayer ||
|
|
player === VideoPlayer.EmbeddedMpv
|
|
);
|
|
}
|
|
|
|
openPlayer(
|
|
streamUrl: string,
|
|
title: string,
|
|
thumbnail?: string,
|
|
hideExternalInfoDialog = true,
|
|
isLiveContent = false,
|
|
userAgent?: string,
|
|
referer?: string,
|
|
origin?: string,
|
|
contentInfo?: PlayerContentInfo,
|
|
startTime?: number,
|
|
headers?: Record<string, string>
|
|
): Promise<ExternalPlayerSession | void> {
|
|
return this.openResolvedPlayback(
|
|
{
|
|
streamUrl,
|
|
title,
|
|
thumbnail,
|
|
isLive: isLiveContent,
|
|
startTime,
|
|
contentInfo,
|
|
headers,
|
|
userAgent,
|
|
referer,
|
|
origin,
|
|
},
|
|
hideExternalInfoDialog
|
|
);
|
|
}
|
|
|
|
async openResolvedPlayback(
|
|
playback: ResolvedPortalPlayback,
|
|
hideExternalInfoDialog = true
|
|
): Promise<ExternalPlayerSession | void> {
|
|
const player = this.settingsStore.player() ?? VideoPlayer.VideoJs;
|
|
|
|
if (player === VideoPlayer.MPV) {
|
|
if (!hideExternalInfoDialog) {
|
|
this.showExternalPlayerInfoDialog();
|
|
}
|
|
return await this.openExternalPlayback(playback, 'mpv');
|
|
} else if (player === VideoPlayer.VLC) {
|
|
if (!hideExternalInfoDialog) {
|
|
this.showExternalPlayerInfoDialog();
|
|
}
|
|
return await this.openExternalPlayback(playback, 'vlc');
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Loaded on demand: the dialog's Material checkbox brings @angular/forms,
|
|
* and this service is created with the app, so a static import would put
|
|
* both on the initial path. Playback does not wait for the dialog.
|
|
*/
|
|
private showExternalPlayerInfoDialog(): void {
|
|
this.loadExternalPlayerInfoDialog()
|
|
.then(({ ExternalPlayerInfoDialogComponent }) =>
|
|
this.dialog.open(ExternalPlayerInfoDialogComponent)
|
|
)
|
|
.catch((error: unknown) => {
|
|
// Informational only: playback is already starting.
|
|
console.error(
|
|
'Could not load the external player info dialog:',
|
|
error
|
|
);
|
|
});
|
|
}
|
|
|
|
/** The dynamic import; a field so specs can substitute it. */
|
|
loadExternalPlayerInfoDialog = () =>
|
|
import('./external-player-info-dialog.lazy');
|
|
|
|
async openExternalPlayback(
|
|
playback: ResolvedPortalPlayback,
|
|
player: ExternalPlayerName
|
|
): Promise<ExternalPlayerSession | void> {
|
|
const ipcEvent = player === 'mpv' ? OPEN_MPV_PLAYER : OPEN_VLC_PLAYER;
|
|
|
|
return await this.dataService.sendIpcEvent<ExternalPlayerSession>(
|
|
ipcEvent,
|
|
{
|
|
url: playback.streamUrl,
|
|
title: playback.title,
|
|
thumbnail: playback.thumbnail,
|
|
'user-agent': playback.userAgent,
|
|
referer: playback.referer,
|
|
origin: playback.origin,
|
|
headers: playback.headers,
|
|
contentInfo: playback.contentInfo,
|
|
startTime: playback.startTime,
|
|
}
|
|
);
|
|
}
|
|
}
|