mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
218 lines
6.1 KiB
TypeScript
218 lines
6.1 KiB
TypeScript
import { inject, Injectable } from '@angular/core';
|
|
import { PlaybackPositionData } from '@iptvnator/shared/interfaces';
|
|
import { RuntimeCapabilitiesService } from './runtime-capabilities.service';
|
|
|
|
type PlaybackPositionContentType = 'vod' | 'episode';
|
|
|
|
type PlaybackPositionElectronBridge = Partial<{
|
|
dbSavePlaybackPosition: (
|
|
playlistId: string,
|
|
data: PlaybackPositionData
|
|
) => Promise<{ success: boolean }>;
|
|
dbGetPlaybackPosition: (
|
|
playlistId: string,
|
|
contentXtreamId: number,
|
|
contentType: PlaybackPositionContentType
|
|
) => Promise<PlaybackPositionData | null>;
|
|
dbGetSeriesPlaybackPositions: (
|
|
playlistId: string,
|
|
seriesXtreamId: number
|
|
) => Promise<PlaybackPositionData[]>;
|
|
dbGetRecentPlaybackPositions: (
|
|
playlistId: string,
|
|
limit?: number
|
|
) => Promise<PlaybackPositionData[]>;
|
|
dbGetAllPlaybackPositions: (
|
|
playlistId: string
|
|
) => Promise<PlaybackPositionData[]>;
|
|
dbClearAllPlaybackPositions: (
|
|
playlistId: string
|
|
) => Promise<{ success: boolean }>;
|
|
dbClearPlaybackPosition: (
|
|
playlistId: string,
|
|
contentXtreamId: number,
|
|
contentType: PlaybackPositionContentType
|
|
) => Promise<{ success: boolean }>;
|
|
onPlaybackPositionUpdate: (
|
|
callback: (data: PlaybackPositionData) => void
|
|
) => () => void;
|
|
}>;
|
|
|
|
type PlaybackPositionRuntimeWindow = Window & {
|
|
electron?: PlaybackPositionElectronBridge;
|
|
};
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class PlaybackPositionRuntimeBridgeService {
|
|
private readonly runtime = inject(RuntimeCapabilitiesService);
|
|
|
|
get supportsStorage(): boolean {
|
|
return this.runtime.supportsPlaybackPositionStorage;
|
|
}
|
|
|
|
get supportsUpdates(): boolean {
|
|
return this.runtime.supportsPlaybackPositionUpdates;
|
|
}
|
|
|
|
async savePlaybackPosition(
|
|
playlistId: string,
|
|
data: PlaybackPositionData
|
|
): Promise<void> {
|
|
if (!this.supportsStorage) {
|
|
return;
|
|
}
|
|
|
|
await this.bridge?.dbSavePlaybackPosition?.(playlistId, data);
|
|
}
|
|
|
|
async savePlaybackPositionOrThrow(
|
|
playlistId: string,
|
|
data: PlaybackPositionData
|
|
): Promise<void> {
|
|
if (!this.supportsStorage) {
|
|
throw new Error('Playback position storage is unavailable');
|
|
}
|
|
|
|
const bridge = this.bridge;
|
|
if (typeof bridge?.dbSavePlaybackPosition !== 'function') {
|
|
throw new Error(
|
|
'Playback position save method is unavailable'
|
|
);
|
|
}
|
|
|
|
const result = await bridge.dbSavePlaybackPosition(playlistId, data);
|
|
if (result?.success !== true) {
|
|
throw new Error('Playback position save did not succeed');
|
|
}
|
|
}
|
|
|
|
getPlaybackPosition(
|
|
playlistId: string,
|
|
contentXtreamId: number,
|
|
contentType: PlaybackPositionContentType
|
|
): Promise<PlaybackPositionData | null> {
|
|
if (!this.supportsStorage) {
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
return (
|
|
this.bridge?.dbGetPlaybackPosition?.(
|
|
playlistId,
|
|
contentXtreamId,
|
|
contentType
|
|
) ?? Promise.resolve(null)
|
|
);
|
|
}
|
|
|
|
getSeriesPlaybackPositions(
|
|
playlistId: string,
|
|
seriesXtreamId: number
|
|
): Promise<PlaybackPositionData[]> {
|
|
if (!this.supportsStorage) {
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
return (
|
|
this.bridge?.dbGetSeriesPlaybackPositions?.(
|
|
playlistId,
|
|
seriesXtreamId
|
|
) ?? Promise.resolve([])
|
|
);
|
|
}
|
|
|
|
getRecentPlaybackPositions(
|
|
playlistId: string,
|
|
limit?: number
|
|
): Promise<PlaybackPositionData[]> {
|
|
if (!this.supportsStorage) {
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
return (
|
|
this.bridge?.dbGetRecentPlaybackPositions?.(playlistId, limit) ??
|
|
Promise.resolve([])
|
|
);
|
|
}
|
|
|
|
getAllPlaybackPositions(
|
|
playlistId: string
|
|
): Promise<PlaybackPositionData[]> {
|
|
if (!this.supportsStorage) {
|
|
return Promise.resolve([]);
|
|
}
|
|
|
|
return (
|
|
this.bridge?.dbGetAllPlaybackPositions?.(playlistId) ??
|
|
Promise.resolve([])
|
|
);
|
|
}
|
|
|
|
async clearAllPlaybackPositions(playlistId: string): Promise<void> {
|
|
if (!this.supportsStorage) {
|
|
return;
|
|
}
|
|
|
|
await this.bridge?.dbClearAllPlaybackPositions?.(playlistId);
|
|
}
|
|
|
|
async clearPlaybackPosition(
|
|
playlistId: string,
|
|
contentXtreamId: number,
|
|
contentType: PlaybackPositionContentType
|
|
): Promise<void> {
|
|
if (!this.supportsStorage) {
|
|
return;
|
|
}
|
|
|
|
await this.bridge?.dbClearPlaybackPosition?.(
|
|
playlistId,
|
|
contentXtreamId,
|
|
contentType
|
|
);
|
|
}
|
|
|
|
async clearPlaybackPositionOrThrow(
|
|
playlistId: string,
|
|
contentXtreamId: number,
|
|
contentType: PlaybackPositionContentType
|
|
): Promise<void> {
|
|
if (!this.supportsStorage) {
|
|
throw new Error('Playback position storage is unavailable');
|
|
}
|
|
|
|
const bridge = this.bridge;
|
|
if (typeof bridge?.dbClearPlaybackPosition !== 'function') {
|
|
throw new Error(
|
|
'Playback position clear method is unavailable'
|
|
);
|
|
}
|
|
|
|
const result = await bridge.dbClearPlaybackPosition(
|
|
playlistId,
|
|
contentXtreamId,
|
|
contentType
|
|
);
|
|
if (result?.success !== true) {
|
|
throw new Error('Playback position clear did not succeed');
|
|
}
|
|
}
|
|
|
|
onPlaybackPositionUpdate(
|
|
callback: (data: PlaybackPositionData) => void
|
|
): (() => void) | undefined {
|
|
if (!this.supportsUpdates) {
|
|
return undefined;
|
|
}
|
|
|
|
return this.bridge?.onPlaybackPositionUpdate?.(callback);
|
|
}
|
|
|
|
private get bridge(): PlaybackPositionElectronBridge | undefined {
|
|
if (typeof window === 'undefined') {
|
|
return undefined;
|
|
}
|
|
|
|
return (window as PlaybackPositionRuntimeWindow).electron;
|
|
}
|
|
}
|