diff --git a/apps/electron-backend/src/app/events/player.events.ts b/apps/electron-backend/src/app/events/player.events.ts index 22be20597..7f1f221b0 100644 --- a/apps/electron-backend/src/app/events/player.events.ts +++ b/apps/electron-backend/src/app/events/player.events.ts @@ -53,87 +53,125 @@ function sendMpvCommand(command: string, args: string[]): Promise { }); } -ipcMain.handle('OPEN_MPV_PLAYER', async (event, url) => { - try { - const mpvPath = getMpvPath(); - const reuseInstance = store.get(MPV_REUSE_INSTANCE, false); +ipcMain.handle( + 'OPEN_MPV_PLAYER', + async ( + event, + url: string, + path: string, + title: string, + userAgent?: string, + referer?: string, + origin?: string + ) => { + try { + const mpvPath = getMpvPath(); + const reuseInstance = store.get(MPV_REUSE_INSTANCE, false); - console.log('Opening MPV player with path:', mpvPath); - console.log('Reuse instance:', reuseInstance); - console.log('URL:', url); + console.log('Opening MPV player with path:', mpvPath); + console.log('Reuse instance:', reuseInstance); + console.log('URL:', url); + console.log('User-Agent:', userAgent); + console.log('Referer:', referer); + console.log('Origin:', origin); - // If reuse is enabled and there's an existing process, try to use it - if ( - reuseInstance && - mpvProcess && - !mpvProcess.killed && - mpvSocketPath - ) { - console.log('Reusing existing MPV instance'); - try { - await sendMpvCommand('loadfile', [url, 'replace']); - console.log( - 'Successfully loaded new URL in existing MPV instance' - ); - return; - } catch (err) { - console.error('Failed to send command to existing MPV:', err); - // If it fails, clear the reference and create a new one + // If reuse is enabled and there's an existing process, try to use it + if ( + reuseInstance && + mpvProcess && + !mpvProcess.killed && + mpvSocketPath + ) { + console.log('Reusing existing MPV instance'); + try { + await sendMpvCommand('loadfile', [url, 'replace']); + console.log( + 'Successfully loaded new URL in existing MPV instance' + ); + return; + } catch (err) { + console.error('Failed to send command to existing MPV:', err); + // If it fails, clear the reference and create a new one + mpvProcess = null; + mpvSocketPath = null; + } + } + + // Create new MPV process + console.log('Creating new MPV instance'); + + // Generate unique socket path + const socketPath = + process.platform === 'win32' + ? `\\\\.\\pipe\\mpv-${Date.now()}` + : `/tmp/mpvsocket-${Date.now()}`; + + const args = [`--input-ipc-server=${socketPath}`, '--idle=yes']; + + // Add user agent if provided + if (userAgent) { + args.push(`--user-agent=${userAgent}`); + } + + // Add referer if provided + if (referer) { + args.push(`--referrer=${referer}`); + } + + // Add origin as custom HTTP header if provided + // MPV doesn't have a direct --origin flag, so we use --http-header-fields + if (origin) { + args.push(`--http-header-fields=Origin: ${origin}`); + } + + // Add title if provided + if (title) { + args.push(`--force-media-title=${title}`); + } + + // Add URL last + args.push(url); + + 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; - } - } - - // Create new MPV process - console.log('Creating new MPV instance'); - - // Generate unique socket path - const socketPath = - process.platform === 'win32' - ? `\\\\.\\pipe\\mpv-${Date.now()}` - : `/tmp/mpvsocket-${Date.now()}`; - - const args = [`--input-ipc-server=${socketPath}`, '--idle=yes', url]; - - 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, { + /* 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('exit', (code) => { - console.log(`MPV exited with code ${code}`); + 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(); + } + } catch (error) { + console.log(error); 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(); } - } catch (error) { - console.log(error); - mpvProcess = null; - mpvSocketPath = null; } -}); +); ipcMain.handle('SET_MPV_PLAYER_PATH', (_event, mpvPlayerPath) => { console.log('... setting mpv player path', mpvPlayerPath); @@ -153,28 +191,65 @@ ipcMain.handle('SET_MPV_REUSE_INSTANCE', (_event, reuseInstance: boolean) => { } }); -ipcMain.handle('OPEN_VLC_PLAYER', (event, url) => { - const vlcPath = getVlcPath(); - console.log('Opening VLC player with path:', vlcPath); - console.log('URL:', url); +ipcMain.handle( + 'OPEN_VLC_PLAYER', + ( + event, + url: string, + path: string, + title: string, + userAgent?: string, + 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); - const proc = spawn(vlcPath, [url as string], { - shell: false, - detached: true, - stdio: 'ignore', - }); + const args: string[] = []; - proc.on('error', (err) => { - console.error('Failed to start VLC player:', err); - }); + // Add user agent if provided (VLC uses :http-user-agent= format) + if (userAgent) { + args.push(`:http-user-agent=${userAgent}`); + } - proc.on('exit', (code) => { - console.log(`VLC exited with code ${code}`); - }); + // Add referer if provided (VLC uses :http-referrer= format) + if (referer) { + args.push(`:http-referrer=${referer}`); + } - // Detach the process so it can continue running independently - proc.unref(); -}); + // 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(); + } +); ipcMain.handle('SET_VLC_PLAYER_PATH', (_event, vlcPlayerPath) => { console.log('... setting vlc player path', vlcPlayerPath); diff --git a/apps/web/src/app/services/electron.service.ts b/apps/web/src/app/services/electron.service.ts index 64f475e56..8033e6619 100644 --- a/apps/web/src/app/services/electron.service.ts +++ b/apps/web/src/app/services/electron.service.ts @@ -80,6 +80,7 @@ export class ElectronService extends DataService { return await window.electron.openInVlc( data.url, data.vlcPlayerPath || '', + data.title ?? '', data['user-agent'] ?? undefined, data.referer ?? undefined, data.origin ?? undefined