Files
iptvnator/apps/web/src/app/xtream-tauri/data-sources/electron-xtream-data-source.ts
T

390 lines
12 KiB
TypeScript

import { inject, Injectable } from '@angular/core';
import { DatabaseService, PlaybackPositionService } from 'services';
import {
XtreamCategory,
XtreamLiveStream,
XtreamSerieItem,
XtreamVodStream,
} from 'shared-interfaces';
import { CategoryType, StreamType, XtreamApiService, XtreamCredentials } from '../services/xtream-api.service';
import {
DbCategoryType,
IXtreamDataSource,
mapCategoryTypeToDbType,
ProgressCallback,
XtreamCategoryFromDb,
XtreamContentItem,
XtreamPlaylistData,
} from './xtream-data-source.interface';
/**
* Electron implementation of the Xtream data source.
* Uses DB-first strategy: check DB, fetch API if needed, cache to DB.
*/
@Injectable({ providedIn: 'root' })
export class ElectronXtreamDataSource implements IXtreamDataSource {
private readonly dbService = inject(DatabaseService);
private readonly playbackService = inject(PlaybackPositionService);
private readonly apiService = inject(XtreamApiService);
// =========================================================================
// Playlist Operations
// =========================================================================
async getPlaylist(playlistId: string): Promise<XtreamPlaylistData | null> {
const playlist = await this.dbService.getPlaylistById(playlistId);
return playlist as XtreamPlaylistData | null;
}
async createPlaylist(playlist: XtreamPlaylistData): Promise<void> {
await this.dbService.createPlaylist({
_id: playlist.id,
title: playlist.name,
serverUrl: playlist.serverUrl,
username: playlist.username,
password: playlist.password,
} as any);
}
async updatePlaylist(
playlistId: string,
updates: Partial<XtreamPlaylistData>
): Promise<void> {
await this.dbService.updateXtreamPlaylistDetails({
id: playlistId,
title: updates.name,
username: updates.username,
password: updates.password,
serverUrl: updates.serverUrl,
});
}
async deletePlaylist(playlistId: string): Promise<void> {
await this.dbService.deletePlaylist(playlistId);
}
// =========================================================================
// Category Operations
// =========================================================================
async hasCategories(
playlistId: string,
type: DbCategoryType
): Promise<boolean> {
return this.dbService.hasXtreamCategories(playlistId, type);
}
async getCategories(
playlistId: string,
credentials: XtreamCredentials,
type: CategoryType
): Promise<XtreamCategoryFromDb[]> {
const dbType = mapCategoryTypeToDbType(type);
// Check if we have cached data
const exists = await this.dbService.hasXtreamCategories(
playlistId,
dbType
);
if (exists) {
// Return from cache
return this.dbService.getXtreamCategories(playlistId, dbType);
}
// Fetch from API and cache
const remoteData = await this.apiService.getCategories(
credentials,
type
);
if (remoteData && Array.isArray(remoteData) && remoteData.length > 0) {
// Check if there are saved hidden categories to restore
const hiddenCategoryXtreamIds =
this.getHiddenCategoryXtreamIds(playlistId, dbType);
await this.dbService.saveXtreamCategories(
playlistId,
remoteData,
dbType,
hiddenCategoryXtreamIds
);
}
// Return from cache (now populated)
return this.dbService.getXtreamCategories(playlistId, dbType);
}
/**
* Get hidden category xtreamIds from localStorage for a specific playlist and type
* Used to restore visibility preferences after playlist refresh
*/
private getHiddenCategoryXtreamIds(
playlistId: string,
type: 'live' | 'movies' | 'series'
): number[] | undefined {
const restoreKey = `xtream-restore-${playlistId}`;
const restoreDataStr = localStorage.getItem(restoreKey);
if (!restoreDataStr) {
return undefined;
}
try {
const restoreData = JSON.parse(restoreDataStr);
const hiddenCategories = restoreData.hiddenCategories as
| { xtreamId: number; type: string }[]
| undefined;
if (!hiddenCategories || hiddenCategories.length === 0) {
return undefined;
}
// Filter for the specific type and return xtreamIds
return hiddenCategories
.filter((cat) => cat.type === type)
.map((cat) => cat.xtreamId);
} catch {
return undefined;
}
}
async getAllCategories(
playlistId: string,
type: DbCategoryType
): Promise<XtreamCategoryFromDb[]> {
return this.dbService.getAllXtreamCategories(playlistId, type);
}
async saveCategories(
playlistId: string,
categories: XtreamCategory[],
type: DbCategoryType
): Promise<void> {
await this.dbService.saveXtreamCategories(playlistId, categories, type);
}
async updateCategoryVisibility(
categoryIds: number[],
hidden: boolean
): Promise<void> {
await this.dbService.updateCategoryVisibility(categoryIds, hidden);
}
// =========================================================================
// Content/Stream Operations
// =========================================================================
async hasContent(
playlistId: string,
type: 'live' | 'movie' | 'series'
): Promise<boolean> {
return this.dbService.hasXtreamContent(playlistId, type);
}
async getContent(
playlistId: string,
credentials: XtreamCredentials,
type: StreamType,
onProgress?: (count: number) => void,
onTotal?: (total: number) => void
): Promise<XtreamContentItem[]> {
// Check if we have cached data
const exists = await this.dbService.hasXtreamContent(playlistId, type);
if (exists) {
// Return from cache
return this.dbService.getXtreamContent(playlistId, type);
}
// Fetch from API
const remoteData = await this.apiService.getStreams(credentials, type);
if (remoteData && Array.isArray(remoteData) && remoteData.length > 0) {
// Report total items to import
if (onTotal) {
onTotal(remoteData.length);
}
// Save to cache with progress tracking
await this.dbService.saveXtreamContent(
playlistId,
remoteData as any[],
type,
onProgress
);
}
// Return from cache (now populated)
return this.dbService.getXtreamContent(playlistId, type);
}
async saveContent(
playlistId: string,
streams: any[],
type: 'live' | 'movie' | 'series',
onProgress?: ProgressCallback
): Promise<number> {
return this.dbService.saveXtreamContent(
playlistId,
streams,
type,
onProgress
);
}
// =========================================================================
// Search Operations
// =========================================================================
async searchContent(
playlistId: string,
searchTerm: string,
types: string[],
excludeHidden?: boolean
): Promise<XtreamContentItem[]> {
return this.dbService.searchXtreamContent(playlistId, searchTerm, types, excludeHidden);
}
// =========================================================================
// Favorites Operations
// =========================================================================
async getFavorites(playlistId: string): Promise<XtreamContentItem[]> {
return this.dbService.getFavorites(playlistId);
}
async addFavorite(contentId: number, playlistId: string): Promise<void> {
await this.dbService.addToFavorites(contentId, playlistId);
}
async removeFavorite(contentId: number, playlistId: string): Promise<void> {
await this.dbService.removeFromFavorites(contentId, playlistId);
}
async isFavorite(contentId: number, playlistId: string): Promise<boolean> {
return this.dbService.isFavorite(contentId, playlistId);
}
// =========================================================================
// Recently Viewed Operations
// =========================================================================
async getRecentItems(playlistId: string): Promise<XtreamContentItem[]> {
return this.dbService.getRecentItems(playlistId);
}
async addRecentItem(contentId: number, playlistId: string): Promise<void> {
await this.dbService.addRecentItem(contentId, playlistId);
}
async removeRecentItem(
contentId: number,
playlistId: string
): Promise<void> {
await this.dbService.removeRecentItem(contentId, playlistId);
}
async clearRecentItems(playlistId: string): Promise<void> {
await this.dbService.clearPlaylistRecentItems(playlistId);
}
// =========================================================================
// Content Lookup
// =========================================================================
async getContentByXtreamId(
xtreamId: number,
playlistId: string
): Promise<XtreamContentItem | null> {
return this.dbService.getContentByXtreamId(xtreamId, playlistId);
}
// =========================================================================
// Playback Position Operations
// =========================================================================
async savePlaybackPosition(
playlistId: string,
data: any
): Promise<void> {
await this.playbackService.savePlaybackPosition(playlistId, data);
}
async getPlaybackPosition(
playlistId: string,
contentXtreamId: number,
contentType: 'vod' | 'episode'
): Promise<any | null> {
return this.playbackService.getPlaybackPosition(
playlistId,
contentXtreamId,
contentType
);
}
async getSeriesPlaybackPositions(
playlistId: string,
seriesXtreamId: number
): Promise<any[]> {
return this.playbackService.getSeriesPlaybackPositions(
playlistId,
seriesXtreamId
);
}
async getRecentPlaybackPositions(
playlistId: string,
limit?: number
): Promise<any[]> {
return this.playbackService.getRecentPlaybackPositions(
playlistId,
limit
);
}
async getAllPlaybackPositions(playlistId: string): Promise<any[]> {
return this.playbackService.getAllPlaybackPositions(playlistId);
}
async clearPlaybackPosition(
playlistId: string,
contentXtreamId: number,
contentType: 'vod' | 'episode'
): Promise<void> {
await this.playbackService.clearPlaybackPosition(
playlistId,
contentXtreamId,
contentType
);
}
// =========================================================================
// Cleanup Operations
// =========================================================================
async clearPlaylistContent(playlistId: string): Promise<{
favoritedXtreamIds: number[];
recentlyViewedXtreamIds: { xtreamId: number; viewedAt: string }[];
}> {
const result =
await this.dbService.deleteXtreamPlaylistContent(playlistId);
return {
favoritedXtreamIds: result.favoritedXtreamIds,
recentlyViewedXtreamIds: result.recentlyViewedXtreamIds,
};
}
async restoreUserData(
playlistId: string,
favoritedXtreamIds: number[],
recentlyViewedXtreamIds: { xtreamId: number; viewedAt: string }[]
): Promise<void> {
await this.dbService.restoreXtreamUserData(
playlistId,
favoritedXtreamIds,
recentlyViewedXtreamIds
);
}
}