@@ -72,6 +72,49 @@ pub fn auto_download() -> Result<()> {
7272 Ok ( ( ) )
7373}
7474
75+ pub enum FfmpegDownloadProgressEvent {
76+ Starting ,
77+ Downloading {
78+ total_bytes : u64 ,
79+ downloaded_bytes : u64 ,
80+ } ,
81+ UnpackingArchive ,
82+ Done ,
83+ }
84+
85+ /// Check if FFmpeg is installed, and if it's not, download and unpack it.
86+ /// Automatically selects the correct binaries for Windows, Linux, and MacOS.
87+ /// The binaries will be placed in the same directory as the Rust executable.
88+ ///
89+ /// Provides progress tracking via callback.
90+ ///
91+ /// If FFmpeg is already installed, the method exits early without downloading
92+ /// anything.
93+ #[ cfg( feature = "download_ffmpeg" ) ]
94+ pub fn auto_download_with_progress (
95+ progress_callback : impl Fn ( FfmpegDownloadProgressEvent ) ,
96+ ) -> Result < ( ) > {
97+ use crate :: { command:: ffmpeg_is_installed, paths:: sidecar_dir} ;
98+
99+ if ffmpeg_is_installed ( ) {
100+ return Ok ( ( ) ) ;
101+ }
102+
103+ progress_callback ( FfmpegDownloadProgressEvent :: Starting ) ;
104+ let download_url = ffmpeg_download_url ( ) ?;
105+ let destination = sidecar_dir ( ) ?;
106+ let archive_path = download_ffmpeg_package_with_progress ( download_url, & destination, |e| progress_callback ( e) ) ?;
107+ progress_callback ( FfmpegDownloadProgressEvent :: UnpackingArchive ) ;
108+ unpack_ffmpeg ( & archive_path, & destination) ?;
109+ progress_callback ( FfmpegDownloadProgressEvent :: Done ) ;
110+
111+ if !ffmpeg_is_installed ( ) {
112+ anyhow:: bail!( "FFmpeg failed to install, please install manually." ) ;
113+ }
114+
115+ Ok ( ( ) )
116+ }
117+
75118/// Parse the the MacOS version number from a JSON string manifest file.
76119///
77120/// Example input: <https://evermeet.cx/ffmpeg/info/ffmpeg/release>
@@ -164,6 +207,70 @@ pub fn download_ffmpeg_package(url: &str, download_dir: &Path) -> Result<PathBuf
164207 Ok ( archive_path)
165208}
166209
210+ /// Make an HTTP request to download an archive from the latest published release online with progress tracking.
211+ #[ cfg( feature = "download_ffmpeg" ) ]
212+ pub fn download_ffmpeg_package_with_progress (
213+ url : & str ,
214+ download_dir : & Path ,
215+ progress_callback : impl Fn ( FfmpegDownloadProgressEvent ) ,
216+ ) -> Result < PathBuf > {
217+ use anyhow:: Context ;
218+ use std:: {
219+ fs:: File ,
220+ io:: { copy, Read } ,
221+ path:: Path ,
222+ } ;
223+
224+ let filename = Path :: new ( url)
225+ . file_name ( )
226+ . context ( "Failed to get filename" ) ?;
227+
228+ let archive_path = download_dir. join ( filename) ;
229+
230+ let mut response = ureq:: get ( url) . call ( ) . context ( "Failed to download ffmpeg" ) ?;
231+
232+ let total_size = response
233+ . headers ( )
234+ . get ( "Content-Length" )
235+ . and_then ( |s| s. to_str ( ) . ok ( ) )
236+ . and_then ( |s| s. parse :: < u64 > ( ) . ok ( ) )
237+ . unwrap_or ( 0 ) ;
238+
239+ let mut file =
240+ File :: create ( & archive_path) . context ( "Failed to create file for ffmpeg download" ) ?;
241+
242+ // Wrapper to track progress during io::copy
243+ struct ProgressReader < R , F > {
244+ inner : R ,
245+ progress_callback : F ,
246+ downloaded : u64 ,
247+ total : u64 ,
248+ }
249+
250+ impl < R : Read , F : Fn ( FfmpegDownloadProgressEvent ) > Read for ProgressReader < R , F > {
251+ fn read ( & mut self , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
252+ let n = self . inner . read ( buf) ?;
253+ self . downloaded += n as u64 ;
254+ ( self . progress_callback ) ( FfmpegDownloadProgressEvent :: Downloading {
255+ total_bytes : self . total ,
256+ downloaded_bytes : self . downloaded ,
257+ } ) ;
258+ Ok ( n)
259+ }
260+ }
261+
262+ let mut progress_reader = ProgressReader {
263+ inner : response. body_mut ( ) . as_reader ( ) ,
264+ progress_callback,
265+ downloaded : 0 ,
266+ total : total_size,
267+ } ;
268+
269+ copy ( & mut progress_reader, & mut file) . context ( "Failed to write ffmpeg download to file" ) ?;
270+
271+ Ok ( archive_path)
272+ }
273+
167274/// After downloading, unpacks the archive to a folder, moves the binaries to
168275/// their final location, and deletes the archive and temporary folder.
169276#[ cfg( feature = "download_ffmpeg" ) ]
0 commit comments