@@ -136,28 +136,97 @@ export class RtspStreamService extends EventEmitter {
136136
137137 /**
138138 * Check if ffmpeg is available on the system
139+ * On macOS, Electron apps don't inherit user PATH, so check common install locations explicitly
140+ * Also checks common paths on Linux (Snap, Flatpak, etc.) and Windows
139141 */
140142 private async checkFfmpegAvailability ( ) : Promise < void > {
141- try {
142- const { stdout } = await execAsync ( 'ffmpeg -version' ) ;
143- const versionMatch = stdout . match ( / f f m p e g v e r s i o n ( [ ^ \s ] + ) / ) ;
144- const version = versionMatch ? versionMatch [ 1 ] : 'unknown' ;
145-
146- this . ffmpegStatus = {
147- available : true ,
148- version
149- } ;
150-
151- console . log ( `[RtspStreamService] ffmpeg found: version ${ version } ` ) ;
152- } catch ( error ) {
153- const errorMessage = error instanceof Error ? error . message : String ( error ) ;
154- this . ffmpegStatus = {
155- available : false ,
156- error : errorMessage
157- } ;
143+ // Common ffmpeg installation paths across platforms
144+ // Order matters: try PATH first, then check platform-specific locations
145+ const ffmpegPaths = [
146+ 'ffmpeg' , // Try PATH first (works if launched from terminal or properly configured)
147+
148+ // ===== macOS =====
149+ // Homebrew (most common on macOS)
150+ '/opt/homebrew/bin/ffmpeg' , // Homebrew on Apple Silicon (M1/M2/M3)
151+ '/usr/local/bin/ffmpeg' , // Homebrew on Intel Mac
152+ '/opt/local/bin/ffmpeg' , // MacPorts
153+
154+ // ===== Linux =====
155+ // Standard package manager locations (usually in PATH, but checking explicitly doesn't hurt)
156+ '/usr/bin/ffmpeg' , // apt (Debian/Ubuntu), yum/dnf (Fedora/RHEL), pacman (Arch)
157+
158+ // Universal package managers (often not in Electron's PATH)
159+ '/snap/bin/ffmpeg' , // Snap packages
160+ '/var/lib/flatpak/exports/bin/ffmpeg' , // Flatpak system-wide
161+ '~/.local/share/flatpak/exports/bin/ffmpeg' , // Flatpak user install
162+
163+ // Manual/compiled installations
164+ '/usr/local/bin/ffmpeg' , // Common manual install location
165+ '~/bin/ffmpeg' , // User home bin directory
166+
167+ // ===== Windows =====
168+ 'C:\\ffmpeg\\bin\\ffmpeg.exe' , // Common manual install
169+ 'C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe' , // Program Files install
170+ 'C:\\Program Files (x86)\\ffmpeg\\bin\\ffmpeg.exe' , // 32-bit on 64-bit system
171+ ] ;
172+
173+ let lastError = '' ;
174+
175+ // Try each path in order
176+ for ( const ffmpegPath of ffmpegPaths ) {
177+ try {
178+ // Expand ~ to home directory if present
179+ const expandedPath = ffmpegPath . replace ( / ^ ~ / , process . env . HOME || process . env . USERPROFILE || '' ) ;
180+
181+ // Quote the path to handle spaces
182+ const { stdout } = await execAsync ( `"${ expandedPath } " -version` ) ;
183+ const versionMatch = stdout . match ( / f f m p e g v e r s i o n ( [ ^ \s ] + ) / ) ;
184+ const version = versionMatch ? versionMatch [ 1 ] : 'unknown' ;
185+
186+ this . ffmpegStatus = {
187+ available : true ,
188+ version
189+ } ;
190+
191+ // Add ffmpeg directory to PATH so node-rtsp-stream can spawn it
192+ // This is critical for macOS GUI launches where PATH doesn't include Homebrew paths
193+ if ( expandedPath !== 'ffmpeg' ) { // Only for explicit paths, not PATH-based
194+ const lastSlashIndex = expandedPath . lastIndexOf ( '/' ) ;
195+ const lastBackslashIndex = expandedPath . lastIndexOf ( '\\' ) ;
196+ const separatorIndex = Math . max ( lastSlashIndex , lastBackslashIndex ) ;
197+
198+ if ( separatorIndex > 0 ) {
199+ const ffmpegDir = expandedPath . substring ( 0 , separatorIndex ) ;
200+ const pathSep = process . platform === 'win32' ? ';' : ':' ;
201+
202+ // Add to beginning of PATH so it takes precedence
203+ process . env . PATH = `${ ffmpegDir } ${ pathSep } ${ process . env . PATH || '' } ` ;
204+ console . log ( `[RtspStreamService] Added ${ ffmpegDir } to PATH for node-rtsp-stream` ) ;
205+ }
206+ }
158207
159- console . warn ( '[RtspStreamService] ffmpeg not found:' , errorMessage ) ;
208+ console . log ( `[RtspStreamService] ffmpeg found at ${ expandedPath } : version ${ version } ` ) ;
209+ return ; // Success! Exit the function
210+ } catch ( error ) {
211+ lastError = error instanceof Error ? error . message : String ( error ) ;
212+ // Continue to next path
213+ }
160214 }
215+
216+ // If we get here, ffmpeg wasn't found in any location
217+ this . ffmpegStatus = {
218+ available : false ,
219+ error : `ffmpeg not found in any common location. Last error: ${ lastError } `
220+ } ;
221+
222+ console . warn ( '[RtspStreamService] ffmpeg not found in any location' ) ;
223+ console . warn ( '[RtspStreamService] Searched paths:' , ffmpegPaths ) ;
224+ console . warn ( '[RtspStreamService] Install ffmpeg to enable RTSP camera viewing:' ) ;
225+ console . warn ( '[RtspStreamService] - macOS: brew install ffmpeg' ) ;
226+ console . warn ( '[RtspStreamService] - Ubuntu/Debian: sudo apt install ffmpeg' ) ;
227+ console . warn ( '[RtspStreamService] - Fedora/RHEL: sudo dnf install ffmpeg' ) ;
228+ console . warn ( '[RtspStreamService] - Arch: sudo pacman -S ffmpeg' ) ;
229+ console . warn ( '[RtspStreamService] - Windows: Download from ffmpeg.org' ) ;
161230 }
162231
163232 // ============================================================================
0 commit comments