diff --git a/apps/electron-backend/src/app/events/player.events.ts b/apps/electron-backend/src/app/events/player.events.ts index 1fe7fd90a..05b97e016 100644 --- a/apps/electron-backend/src/app/events/player.events.ts +++ b/apps/electron-backend/src/app/events/player.events.ts @@ -96,6 +96,7 @@ ipcMain.handle( // If it fails, clear the reference and create a new one mpvProcess = null; mpvSocketPath = null; + // Fall through to create new instance } } @@ -134,43 +135,57 @@ ipcMain.handle( // Add URL last args.push(url); - const proc = spawn(mpvPath, args, { - shell: false, - detached: !reuseInstance, - stdio: 'ignore', - }); + // Wrap spawn in a promise to catch startup errors + await new Promise((resolve, reject) => { + const proc = spawn(mpvPath, args, { + shell: false, + detached: !reuseInstance, + stdio: 'ignore', + }); - proc.on('error', (err) => { - console.error('Failed to start MPV player:', err); - mpvProcess = null; - mpvSocketPath = null; - /* event.sender.send(ERROR, { - message: `Error: Failed to start MPV player. Make sure that mpv player is installed on your system and the path is correct.`, - }); */ - }); + proc.on('error', (err) => { + console.error('Failed to start MPV player:', err); + mpvProcess = null; + mpvSocketPath = null; + reject( + new Error( + `Failed to start MPV player: ${err.message}. Make sure MPV is installed and the path '${mpvPath}' is correct.` + ) + ); + }); - proc.on('exit', (code) => { - console.log(`MPV exited with code ${code}`); - mpvProcess = null; - mpvSocketPath = null; - }); + proc.on('exit', (code) => { + console.log(`MPV exited with code ${code}`); + mpvProcess = null; + mpvSocketPath = null; + }); - // Store the process reference if reuse is enabled - if (reuseInstance) { - mpvProcess = proc; - mpvSocketPath = socketPath; - console.log( - 'Stored MPV process for reuse with socket:', - socketPath - ); - } else { - // Detach the process so it can continue running independently - proc.unref(); - } + // Store the process reference if reuse is enabled + if (reuseInstance) { + mpvProcess = proc; + mpvSocketPath = socketPath; + console.log( + 'Stored MPV process for reuse with socket:', + socketPath + ); + } else { + // Detach the process so it can continue running independently + proc.unref(); + } + + // Resolve immediately if spawn succeeds (error event would fire if it fails) + // Give a small window to catch immediate spawn errors + setTimeout(() => { + if (!proc.killed) { + resolve(); + } + }, 100); + }); } catch (error) { - console.log(error); + console.error('Error opening MPV player:', error); mpvProcess = null; mpvSocketPath = null; + throw error; } } ); @@ -195,7 +210,7 @@ ipcMain.handle('SET_MPV_REUSE_INSTANCE', (_event, reuseInstance: boolean) => { ipcMain.handle( 'OPEN_VLC_PLAYER', - ( + async ( event, url: string, title: string, @@ -203,52 +218,73 @@ ipcMain.handle( referer?: string, origin?: string ) => { - const vlcPath = getVlcPath(); - console.log('Opening VLC player with path:', vlcPath); - console.log('URL:', url); - console.log('User-Agent:', userAgent); - console.log('Referer:', referer); - console.log('Origin:', origin); + try { + const vlcPath = getVlcPath(); + console.log('Opening VLC player with path:', vlcPath); + console.log('URL:', url); + console.log('User-Agent:', userAgent); + console.log('Referer:', referer); + console.log('Origin:', origin); - const args: string[] = []; + const args: string[] = []; - // Add user agent if provided (VLC uses :http-user-agent= format) - if (userAgent) { - args.push(`:http-user-agent=${userAgent}`); + // Add user agent if provided (VLC uses :http-user-agent= format) + if (userAgent) { + args.push(`:http-user-agent=${userAgent}`); + } + + // Add referer if provided (VLC uses :http-referrer= format) + if (referer) { + args.push(`:http-referrer=${referer}`); + } + + // Note: VLC doesn't have a direct origin option, but origin is typically + // included in referer for most IPTV use cases + + // Add title if provided + if (title) { + args.push(`--meta-title=${title}`); + } + + // Add URL last + args.push(url); + + // Wrap spawn in a promise to catch startup errors + await new Promise((resolve, reject) => { + const proc = spawn(vlcPath, args, { + shell: false, + detached: true, + stdio: 'ignore', + }); + + proc.on('error', (err) => { + console.error('Failed to start VLC player:', err); + reject( + new Error( + `Failed to start VLC player: ${err.message}. Make sure VLC is installed and the path '${vlcPath}' is correct.` + ) + ); + }); + + proc.on('exit', (code) => { + console.log(`VLC exited with code ${code}`); + }); + + // Detach the process so it can continue running independently + proc.unref(); + + // Resolve immediately if spawn succeeds (error event would fire if it fails) + // Give a small window to catch immediate spawn errors + setTimeout(() => { + if (!proc.killed) { + resolve(); + } + }, 100); + }); + } catch (error) { + console.error('Error opening VLC player:', error); + throw error; } - - // Add referer if provided (VLC uses :http-referrer= format) - if (referer) { - args.push(`:http-referrer=${referer}`); - } - - // Note: VLC doesn't have a direct origin option, but origin is typically - // included in referer for most IPTV use cases - - // Add title if provided - if (title) { - args.push(`--meta-title=${title}`); - } - - // Add URL last - args.push(url); - - const proc = spawn(vlcPath, args, { - shell: false, - detached: true, - stdio: 'ignore', - }); - - proc.on('error', (err) => { - console.error('Failed to start VLC player:', err); - }); - - proc.on('exit', (code) => { - console.log(`VLC exited with code ${code}`); - }); - - // Detach the process so it can continue running independently - proc.unref(); } ); diff --git a/apps/web/src/app/services/electron.service.ts b/apps/web/src/app/services/electron.service.ts index 20a666d6c..5b6cc0135 100644 --- a/apps/web/src/app/services/electron.service.ts +++ b/apps/web/src/app/services/electron.service.ts @@ -68,8 +68,9 @@ export class ElectronService extends DataService { data.origin ?? undefined ); /* thumbnail: data.thumbnail ?? '', */ - } catch (error) { - this.snackBar.open(`Error launching MPV: ${error}`, 'Close', { + } catch (error: any) { + const errorMessage = error?.message || String(error); + this.snackBar.open(`Error launching MPV: ${errorMessage}`, 'Close', { duration: 5000, }); console.error('MPV launch error:', error); @@ -85,8 +86,9 @@ export class ElectronService extends DataService { data.referer ?? undefined, data.origin ?? undefined ); - } catch (error) { - this.snackBar.open(`Error launching VLC: ${error}`, 'Close', { + } catch (error: any) { + const errorMessage = error?.message || String(error); + this.snackBar.open(`Error launching VLC: ${errorMessage}`, 'Close', { duration: 5000, }); console.error('VLC launch error:', error);