diff --git a/src/app/services/database.service.ts b/src/app/services/database.service.ts new file mode 100644 index 000000000..98257fd20 --- /dev/null +++ b/src/app/services/database.service.ts @@ -0,0 +1,131 @@ +import { Injectable } from '@angular/core'; +import Database from '@tauri-apps/plugin-sql'; + +@Injectable({ + providedIn: 'root', +}) +export class DatabaseService { + private static db: Database | null = null; + + async getConnection(): Promise { + if (!DatabaseService.db) { + DatabaseService.db = await Database.load('sqlite:database.db'); + } + return DatabaseService.db; + } + + /** + * Delete a playlist and all its related data + * @param playlistId ID of the playlist to delete + * @returns True if deletion was successful + */ + async deletePlaylist(playlistId: string): Promise { + try { + const db = await this.getConnection(); + + // Start a transaction to ensure all related data is deleted + await db.execute('BEGIN TRANSACTION'); + + try { + // Delete from recently_viewed table (related to content which is related to playlist) + await db.execute( + ` + DELETE FROM recently_viewed + WHERE content_id IN ( + SELECT c.id + FROM content c + JOIN categories cat ON c.category_id = cat.id + WHERE cat.playlist_id = ? + ) + `, + [playlistId] + ); + + // Delete content related to the playlist's categories + await db.execute( + ` + DELETE FROM content + WHERE category_id IN ( + SELECT id FROM categories WHERE playlist_id = ? + ) + `, + [playlistId] + ); + + // Delete categories related to the playlist + await db.execute( + 'DELETE FROM categories WHERE playlist_id = ?', + [playlistId] + ); + + // Finally, delete the playlist itself + await db.execute('DELETE FROM playlists WHERE id = ?', [ + playlistId, + ]); + + // Commit the transaction + await db.execute('COMMIT'); + return true; + } catch (error) { + // If any error occurs, rollback the transaction + await db.execute('ROLLBACK'); + throw error; + } + } catch (error) { + console.error('Error deleting playlist:', error); + return false; + } + } + + async updateXtreamPlaylist(playlist: any): Promise { + try { + const db = await this.getConnection(); + await db.execute('UPDATE playlists SET name = ? WHERE id = ?', [ + playlist.name, + playlist.id, + ]); + return true; + } catch (error) { + console.error('Error updating playlist:', error); + return false; + } + } + + async updateXtreamPlaylistDetails(playlist: { + id: string; + title: string; + username?: string; + password?: string; + serverUrl?: string; + }): Promise { + try { + const db = await this.getConnection(); + const updateFields: string[] = ['name = ?']; + const params: any[] = [playlist.title]; + + if (playlist.username) { + updateFields.push('username = ?'); + params.push(playlist.username); + } + if (playlist.password) { + updateFields.push('password = ?'); + params.push(playlist.password); + } + if (playlist.serverUrl) { + updateFields.push('serverUrl = ?'); + params.push(playlist.serverUrl); + } + + params.push(playlist.id); + + console.log(params); + + const query = `UPDATE playlists SET ${updateFields.join(', ')} WHERE id = ?`; + await db.execute(query, params); + return true; + } catch (error) { + console.error('Error updating playlist details:', error); + return false; + } + } +} diff --git a/src/app/services/tauri.service.ts b/src/app/services/tauri.service.ts index 8c8017ea2..b6bf2cc20 100644 --- a/src/app/services/tauri.service.ts +++ b/src/app/services/tauri.service.ts @@ -49,6 +49,8 @@ export class TauriService extends DataService { return invoke('open_in_mpv', { url: (payload as any).url, path: (payload as any).mpvPlayerPath || '', + title: (payload as any).title ?? '', + thumbnail: (payload as any).thumbnail ?? '', }).catch((error) => { window.postMessage({ type: ERROR, diff --git a/src/app/shared/services/mpv-player.service.ts b/src/app/shared/services/mpv-player.service.ts new file mode 100644 index 000000000..416b5e3d4 --- /dev/null +++ b/src/app/shared/services/mpv-player.service.ts @@ -0,0 +1,88 @@ +import { Injectable } from '@angular/core'; +import { invoke } from '@tauri-apps/api/core'; +import { listen } from '@tauri-apps/api/event'; +import { BehaviorSubject } from 'rxjs'; + +export interface MpvProcess { + id: number; + url: string; + start_time: number; + last_known_time: number | null; + thumbnail?: string; + title: string; +} + +@Injectable({ + providedIn: 'root', +}) +export class MpvPlayerService { + private activeProcessesSubject = new BehaviorSubject([]); + public activeProcesses$ = this.activeProcessesSubject.asObservable(); + + constructor() { + this.initializeEventListeners(); + this.loadActiveProcesses(); + } + + private async initializeEventListeners() { + // Listen for new processes + await listen('mpv-process-added', (event: any) => { + const newProcess = event.payload as MpvProcess; + if (newProcess) { + const currentProcesses = this.activeProcessesSubject.value; + this.activeProcessesSubject.next([ + ...currentProcesses, + newProcess, + ]); + } + }); + + // Listen for removed processes + await listen('mpv-process-removed', (event: any) => { + const removedProcess = event.payload as MpvProcess; + if (removedProcess) { + const currentProcesses = this.activeProcessesSubject.value; + this.activeProcessesSubject.next( + currentProcesses.filter((p) => p.id !== removedProcess.id) + ); + } + }); + } + + private async loadActiveProcesses() { + try { + const processes = await invoke( + 'get_active_mpv_processes' + ); + this.activeProcessesSubject.next(processes); + } catch (error) { + console.error('Failed to load active MPV processes:', error); + } + } + + async openStream( + url: string, + title: string, + thumbnail?: string, + mpvPath: string = '' + ): Promise { + return await invoke('open_in_mpv', { + url, + path: mpvPath, + title, + thumbnail, + }); + } + + async playStream(processId: number): Promise { + await invoke('mpv_play', { processId }); + } + + async pauseStream(processId: number): Promise { + await invoke('mpv_pause', { processId }); + } + + async closeStream(processId: number): Promise { + await invoke('close_mpv_process', { processId }); + } +}