@@ -32,18 +32,6 @@ export class VideoProcessor {
3232 url : "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/ffmpeg-darwin-arm64" ,
3333 sha256 : "a90e3db6a3fd35f6074b013f948b1aa45b31c6375489d39e572bea3f18336584"
3434 } ,
35- "linux-x64" : {
36- url : "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/ffmpeg-linux-x64" ,
37- sha256 : "ed652b2f32e0851d1946894fb8333f5b677c1b2ce6b9d187910a67f8b99da028"
38- } ,
39- "linux-arm64" : {
40- url : "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/ffmpeg-linux-arm64" ,
41- sha256 : "237800b37bb65a81ad47871c6c8b7c45c0a3ca62a5b3f9d2a7a9a2dd9a338271"
42- } ,
43- "linux-armv7l" : {
44- url : "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/ffmpeg-linux-arm" ,
45- sha256 : "1a9ddc19d0e071b6e1ff6f8f34dc05ec6dd4d8f3e79a649f5a9ec0e8c929c4cb"
46- } ,
4735 "win-x64" : {
4836 url : "https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/ffmpeg-win32-x64" ,
4937 sha256 : "e9fd5e711debab9d680955fc1e38a2c1160fd280b144476cc3f62bc43ef49db1"
@@ -202,13 +190,14 @@ export class VideoProcessor {
202190
203191 private static async getFFmpegPath ( window : BrowserWindow ) : Promise < string > {
204192 // Check for AdvantageScope install (user data folder)
193+ // Not compatible with Linux due to DNS issues, force a system install
205194 let ffmpegPath : string ;
206195 if ( process . platform === "win32" ) {
207196 ffmpegPath = path . join ( app . getPath ( "userData" ) , "ffmpeg.exe" ) ;
208197 } else {
209198 ffmpegPath = path . join ( app . getPath ( "userData" ) , "ffmpeg" ) ;
210199 }
211- if ( fs . existsSync ( ffmpegPath ) ) {
200+ if ( process . platform !== "linux" && fs . existsSync ( ffmpegPath ) ) {
212201 return ffmpegPath ;
213202 }
214203
@@ -232,7 +221,21 @@ export class VideoProcessor {
232221 return "ffmpeg" ;
233222 }
234223
235- // Download FFmpeg
224+ // Exit immediately on Linux
225+ if ( process . platform === "linux" ) {
226+ await dialog . showMessageBox ( window , {
227+ type : "question" ,
228+ title : "Alert" ,
229+ message : "FFmpeg Installation Required" ,
230+ detail : "FFmpeg is required to process videos files. Please install FFmpeg and add to the PATH." ,
231+ buttons : [ "OK" ] ,
232+ defaultId : 0 ,
233+ icon : WINDOW_ICON
234+ } ) ;
235+ throw "Failed" ;
236+ }
237+
238+ // Download FFmpeg on Windows and macOS
236239 let ffmpegDownloadResponse = await dialog . showMessageBox ( window , {
237240 type : "question" ,
238241 title : "Alert" ,
@@ -282,7 +285,7 @@ export class VideoProcessor {
282285 menuCoordinates : null | [ number , number ] ,
283286 dataCallback : ( data : any ) => void
284287 ) {
285- let loadPath = async ( videoPath : string , videoCache : string ) => {
288+ let loadPath = async ( videoPath : string , videoCache : string , httpHeaders ?: any ) => {
286289 // Find FFmpeg path
287290 let ffmpegPath : string ;
288291 try {
@@ -304,15 +307,27 @@ export class VideoProcessor {
304307
305308 // Start ffmpeg
306309 if ( uuid in VideoProcessor . processes ) VideoProcessor . processes [ uuid ] . kill ( ) ;
307- let ffmpeg = spawn ( ffmpegPath , [
310+
311+ const ffmpegArgs : string [ ] = [ ] ;
312+ ffmpegArgs . push ( "-reconnect" , "1" , "-reconnect_streamed" , "1" , "-reconnect_delay_max" , "5" ) ;
313+ if ( httpHeaders ) {
314+ let headersStr = "" ;
315+ for ( const [ key , value ] of Object . entries ( httpHeaders ) ) {
316+ headersStr += `${ key } : ${ value } \r\n` ;
317+ }
318+ ffmpegArgs . push ( "-headers" , headersStr ) ;
319+ }
320+ ffmpegArgs . push (
308321 "-i" ,
309322 videoPath ,
310323 "-vf" ,
311324 "scale=1920:-2,setsar=1:1" , // Limit to 1920px width
312325 "-q:v" ,
313326 "2" ,
314327 path . join ( cachePath , "%08d.jpg" )
315- ] ) ;
328+ ) ;
329+
330+ let ffmpeg = spawn ( ffmpegPath , ffmpegArgs ) ;
316331 VideoProcessor . processes [ uuid ] = ffmpeg ;
317332 let running = true ;
318333 let fullOutput = "" ;
@@ -482,7 +497,7 @@ export class VideoProcessor {
482497 } else if ( code === 1 ) {
483498 if ( videoCache === VIDEO_CACHE && fullOutput . includes ( "No space left on device" ) ) {
484499 fs . rmSync ( cachePath , { recursive : true } ) ;
485- loadPath ( videoPath , VIDEO_CACHE_FALLBACK ) ;
500+ loadPath ( videoPath , VIDEO_CACHE_FALLBACK , httpHeaders ) ;
486501 } else {
487502 sendError ( ) ;
488503 }
@@ -510,7 +525,7 @@ export class VideoProcessor {
510525 } ) ;
511526 } else {
512527 this . getDirectUrlFromYouTubeUrl ( clipboardText , window )
513- . then ( ( path ) => loadPath ( path , VIDEO_CACHE ) )
528+ . then ( ( result ) => loadPath ( result . url , VIDEO_CACHE , result . httpHeaders ) )
514529 . catch ( ( silent ) => {
515530 dataCallback ( { uuid : uuid , error : true } ) ;
516531 if ( silent === true ) return ;
@@ -529,7 +544,7 @@ export class VideoProcessor {
529544 this . getYouTubeUrlFromMatchInfo ( matchInfo ! , window , menuCoordinates ! )
530545 . then ( ( url ) => {
531546 this . getDirectUrlFromYouTubeUrl ( url , window )
532- . then ( ( path ) => loadPath ( path , VIDEO_CACHE ) )
547+ . then ( ( result ) => loadPath ( result . url , VIDEO_CACHE , result . httpHeaders ) )
533548 . catch ( ( silent ) => {
534549 dataCallback ( { uuid : uuid , error : true } ) ;
535550 if ( silent === true ) return ;
@@ -587,8 +602,11 @@ export class VideoProcessor {
587602 } ) ;
588603 }
589604
590- /** Gets the direct download URL based on a YouTube URL using youtube-dl-exec */
591- private static getDirectUrlFromYouTubeUrl ( youTubeUrl : string , window : BrowserWindow ) : Promise < string > {
605+ /** Gets the direct download URL and User Agent based on a YouTube URL using youtube-dl-exec */
606+ private static getDirectUrlFromYouTubeUrl (
607+ youTubeUrl : string ,
608+ window : BrowserWindow
609+ ) : Promise < { url : string ; httpHeaders : any } > {
592610 return new Promise ( async ( resolve , reject ) => {
593611 try {
594612 const videoId = VideoProcessor . getVideoId ( youTubeUrl ) ;
@@ -605,22 +623,23 @@ export class VideoProcessor {
605623 dumpSingleJson : true ,
606624 noWarnings : true ,
607625 noCheckCertificates : true ,
608- preferFreeFormats : true ,
609- youtubeSkipDashManifest : true
626+ forceIpv4 : true ,
627+ format : "best" ,
628+ extractorArgs : "youtube:player_client=android"
610629 } ,
611630 window
612631 ) ;
613632
614633 // Filter formats
615634 let formats = output . formats || [ ] ;
616- // Filter for formats that have video codec ( not 'none')
617- formats = formats . filter ( ( f : any ) => f . vcodec && f . vcodec !== "none" ) ;
635+ // Filter for formats that have a video codec and are not too large
636+ formats = formats . filter ( ( f : any ) => f . vcodec && f . vcodec !== "none" && f . height <= 1080 ) ;
618637 // Sort by quality (height/resolution) descending
619638 formats . sort ( ( a : any , b : any ) => ( b . height || 0 ) - ( a . height || 0 ) ) ;
620639
621- // Find best url
640+ // Find best url and User Agent
622641 if ( formats . length > 0 ) {
623- resolve ( formats [ 0 ] . url ) ;
642+ resolve ( { url : formats [ 0 ] . url , httpHeaders : output . http_headers } ) ;
624643 } else {
625644 reject ( ) ;
626645 }
0 commit comments