From 2cfc722b0a920f7860ab677eadd63e2d89f0245b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Nov 2025 14:46:02 +0000 Subject: [PATCH 1/2] fix: enhance ffmpeg detection to support macOS and cross-platform installations Fixes #13 - RTSP camera streaming failing on macOS due to ffmpeg not being detected. Problem: - Electron apps launched from Finder on macOS don't inherit user shell PATH - Homebrew installs ffmpeg to /opt/homebrew/bin (Apple Silicon) or /usr/local/bin (Intel Mac) - Current implementation only checks PATH, causing ffmpeg to not be found Solution: - Enhanced checkFfmpegAvailability() to check common installation paths across all platforms - Tries PATH first (backward compatible), then checks platform-specific locations: * macOS: Homebrew (Apple Silicon & Intel), MacPorts * Linux: apt/yum/pacman, Snap, Flatpak, manual installs * Windows: common Program Files locations - Provides helpful error messages with platform-specific installation instructions Impact: - macOS users with Homebrew-installed ffmpeg can now use RTSP cameras - Linux users with Snap/Flatpak installations are now supported - Better diagnostics when ffmpeg is not found - No breaking changes - fully backward compatible --- src/services/RtspStreamService.ts | 90 ++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/src/services/RtspStreamService.ts b/src/services/RtspStreamService.ts index ccb6727..6dbd74a 100644 --- a/src/services/RtspStreamService.ts +++ b/src/services/RtspStreamService.ts @@ -136,28 +136,80 @@ export class RtspStreamService extends EventEmitter { /** * Check if ffmpeg is available on the system + * On macOS, Electron apps don't inherit user PATH, so check common install locations explicitly + * Also checks common paths on Linux (Snap, Flatpak, etc.) and Windows */ private async checkFfmpegAvailability(): Promise { - try { - const { stdout } = await execAsync('ffmpeg -version'); - const versionMatch = stdout.match(/ffmpeg version ([^\s]+)/); - const version = versionMatch ? versionMatch[1] : 'unknown'; - - this.ffmpegStatus = { - available: true, - version - }; - - console.log(`[RtspStreamService] ffmpeg found: version ${version}`); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - this.ffmpegStatus = { - available: false, - error: errorMessage - }; - - console.warn('[RtspStreamService] ffmpeg not found:', errorMessage); + // Common ffmpeg installation paths across platforms + // Order matters: try PATH first, then check platform-specific locations + const ffmpegPaths = [ + 'ffmpeg', // Try PATH first (works if launched from terminal or properly configured) + + // ===== macOS ===== + // Homebrew (most common on macOS) + '/opt/homebrew/bin/ffmpeg', // Homebrew on Apple Silicon (M1/M2/M3) + '/usr/local/bin/ffmpeg', // Homebrew on Intel Mac + '/opt/local/bin/ffmpeg', // MacPorts + + // ===== Linux ===== + // Standard package manager locations (usually in PATH, but checking explicitly doesn't hurt) + '/usr/bin/ffmpeg', // apt (Debian/Ubuntu), yum/dnf (Fedora/RHEL), pacman (Arch) + + // Universal package managers (often not in Electron's PATH) + '/snap/bin/ffmpeg', // Snap packages + '/var/lib/flatpak/exports/bin/ffmpeg', // Flatpak system-wide + '~/.local/share/flatpak/exports/bin/ffmpeg', // Flatpak user install + + // Manual/compiled installations + '/usr/local/bin/ffmpeg', // Common manual install location + '~/bin/ffmpeg', // User home bin directory + + // ===== Windows ===== + 'C:\\ffmpeg\\bin\\ffmpeg.exe', // Common manual install + 'C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe', // Program Files install + 'C:\\Program Files (x86)\\ffmpeg\\bin\\ffmpeg.exe', // 32-bit on 64-bit system + ]; + + let lastError = ''; + + // Try each path in order + for (const ffmpegPath of ffmpegPaths) { + try { + // Expand ~ to home directory if present + const expandedPath = ffmpegPath.replace(/^~/, process.env.HOME || process.env.USERPROFILE || ''); + + // Quote the path to handle spaces + const { stdout } = await execAsync(`"${expandedPath}" -version`); + const versionMatch = stdout.match(/ffmpeg version ([^\s]+)/); + const version = versionMatch ? versionMatch[1] : 'unknown'; + + this.ffmpegStatus = { + available: true, + version + }; + + console.log(`[RtspStreamService] ffmpeg found at ${expandedPath}: version ${version}`); + return; // Success! Exit the function + } catch (error) { + lastError = error instanceof Error ? error.message : String(error); + // Continue to next path + } } + + // If we get here, ffmpeg wasn't found in any location + this.ffmpegStatus = { + available: false, + error: `ffmpeg not found in any common location. Last error: ${lastError}` + }; + + console.warn('[RtspStreamService] ffmpeg not found in any location'); + console.warn('[RtspStreamService] Searched paths:', ffmpegPaths); + console.warn('[RtspStreamService] Install ffmpeg to enable RTSP camera viewing:'); + console.warn('[RtspStreamService] - macOS: brew install ffmpeg'); + console.warn('[RtspStreamService] - Ubuntu/Debian: sudo apt install ffmpeg'); + console.warn('[RtspStreamService] - Fedora/RHEL: sudo dnf install ffmpeg'); + console.warn('[RtspStreamService] - Arch: sudo pacman -S ffmpeg'); + console.warn('[RtspStreamService] - Windows: Download from ffmpeg.org'); } // ============================================================================ From 0bbd3d02d3d438069dd904dd0de4617e764a3608 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 3 Nov 2025 22:01:22 +0000 Subject: [PATCH 2/2] fix: add ffmpeg directory to PATH so node-rtsp-stream can spawn it Fixes the "spawn ffmpeg ENOENT" error when launching from Finder on macOS. Problem: - Detection code finds ffmpeg at full path (e.g., /opt/homebrew/bin/ffmpeg) - But node-rtsp-stream doesn't support custom ffmpeg path option - When node-rtsp-stream spawns ffmpeg, it uses 'ffmpeg' which requires PATH - macOS GUI launches have minimal PATH without Homebrew directories Solution: - After finding ffmpeg, extract its directory and add to process.env.PATH - This makes ffmpeg available when node-rtsp-stream spawns it later - Works for both forward slashes (macOS/Linux) and backslashes (Windows) - Only modifies PATH when using explicit paths (not when ffmpeg already in PATH) Testing: - Launch from Finder: ffmpeg should now be found and spawn successfully - Launch from Terminal: continues to work as before - Cross-platform compatible with proper path separator handling --- src/services/RtspStreamService.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/services/RtspStreamService.ts b/src/services/RtspStreamService.ts index 6dbd74a..b4dc364 100644 --- a/src/services/RtspStreamService.ts +++ b/src/services/RtspStreamService.ts @@ -188,6 +188,23 @@ export class RtspStreamService extends EventEmitter { version }; + // Add ffmpeg directory to PATH so node-rtsp-stream can spawn it + // This is critical for macOS GUI launches where PATH doesn't include Homebrew paths + if (expandedPath !== 'ffmpeg') { // Only for explicit paths, not PATH-based + const lastSlashIndex = expandedPath.lastIndexOf('/'); + const lastBackslashIndex = expandedPath.lastIndexOf('\\'); + const separatorIndex = Math.max(lastSlashIndex, lastBackslashIndex); + + if (separatorIndex > 0) { + const ffmpegDir = expandedPath.substring(0, separatorIndex); + const pathSep = process.platform === 'win32' ? ';' : ':'; + + // Add to beginning of PATH so it takes precedence + process.env.PATH = `${ffmpegDir}${pathSep}${process.env.PATH || ''}`; + console.log(`[RtspStreamService] Added ${ffmpegDir} to PATH for node-rtsp-stream`); + } + } + console.log(`[RtspStreamService] ffmpeg found at ${expandedPath}: version ${version}`); return; // Success! Exit the function } catch (error) {