@@ -271,16 +271,38 @@ async fn process_job(job: Job) -> Result<()> {
271271 }
272272 }
273273
274- let output_path = format ! ( "{}/output.mp4" , temp_dir) ;
275- info ! ( "Output path: {}" , output_path) ;
276-
277274 // Build FFmpeg filter complex based on quadrant selection
278275 let filter_complex = build_filter_complex ( & job. selection ) ?;
279276 info ! ( "FFmpeg filter: {}" , filter_complex) ;
280277
281- info ! ( "Starting FFmpeg..." ) ;
278+ // Create the output folder on WebDAV if needed (before FFmpeg runs)
279+ // job.output_path is like "processed/filename.mp4"
280+ if let Some ( folder_end) = job. output_path . rfind ( '/' ) {
281+ let folder = & job. output_path [ ..folder_end] ;
282+ if !folder. is_empty ( ) {
283+ info ! ( "Creating WebDAV client to ensure folder exists..." ) ;
284+ let dav_client = WebDavClient :: new ( & job. webdav_config ) ?;
285+ info ! ( "Ensuring folder exists: {}" , folder) ;
286+ if let Err ( e) = dav_client. ensure_folder_exists ( folder) . await {
287+ warn ! ( "Could not create folder {}: {} (may already exist)" , folder, e) ;
288+ }
289+ }
290+ }
291+
292+ // Build WebDAV URL for direct output from FFmpeg
293+ let output_url = build_webdav_upload_url ( & job. webdav_config , & job. output_path ) ;
294+ info ! ( "FFmpeg will output directly to WebDAV: {}" , job. output_path) ;
295+ // Log URL without credentials for debugging
296+ let output_url_safe = output_url. replacen (
297+ & format ! ( "://{}:{}@" , encode( & job. webdav_config. username) , encode( & job. webdav_config. password) ) ,
298+ "://[credentials]@" ,
299+ 1
300+ ) ;
301+ info ! ( "Output URL (redacted): {}" , output_url_safe) ;
282302
283- // Run FFmpeg command
303+ info ! ( "Starting FFmpeg with direct WebDAV output..." ) ;
304+
305+ // Run FFmpeg command - output directly to WebDAV
284306 let ffmpeg_result = tokio:: process:: Command :: new ( "ffmpeg" )
285307 . arg ( "-y" ) // Overwrite output
286308 . arg ( "-i" ) . arg ( & video_url) // Input video
@@ -293,7 +315,8 @@ async fn process_job(job: Job) -> Result<()> {
293315 . arg ( "-preset" ) . arg ( "veryfast" )
294316 . arg ( "-threads" ) . arg ( "0" )
295317 . arg ( "-c:a" ) . arg ( "copy" )
296- . arg ( & output_path)
318+ . arg ( "-method" ) . arg ( "PUT" ) // Use HTTP PUT for WebDAV
319+ . arg ( & output_url)
297320 . output ( )
298321 . await ;
299322
@@ -310,43 +333,7 @@ async fn process_job(job: Job) -> Result<()> {
310333 }
311334
312335 if output. status . success ( ) {
313- info ! ( "FFmpeg processing successful!" ) ;
314-
315- // Check output file
316- match fs:: metadata ( & output_path) {
317- Ok ( meta) => info ! ( "Output file size: {} bytes" , meta. len( ) ) ,
318- Err ( e) => error ! ( "Failed to stat output file: {}" , e) ,
319- }
320-
321- // Upload result back to WebDAV
322- info ! ( "Reading output file..." ) ;
323- let output_data = fs:: read ( & output_path) ?;
324- info ! ( "Output file read, size: {} bytes" , output_data. len( ) ) ;
325-
326- info ! ( "Creating WebDAV client..." ) ;
327- let dav_client = WebDavClient :: new ( & job. webdav_config ) ?;
328-
329- // job.output_path is like "processed/filename.mp4"
330- // We need to create the "processed" folder if it doesn't exist
331- let upload_path = & job. output_path ;
332-
333- // Check if output_path contains a folder (has a /)
334- if let Some ( folder_end) = upload_path. rfind ( '/' ) {
335- let folder = & upload_path[ ..folder_end] ;
336- if !folder. is_empty ( ) {
337- info ! ( "Ensuring folder exists: {}" , folder) ;
338- if let Err ( e) = dav_client. ensure_folder_exists ( folder) . await {
339- warn ! ( "Could not create folder {}: {} (may already exist)" , folder, e) ;
340- }
341- }
342- }
343-
344- info ! ( "Uploading processed video to: {}" , upload_path) ;
345- match dav_client. upload_file ( upload_path, output_data) . await {
346- Ok ( _) => info ! ( "Upload successful!" ) ,
347- Err ( e) => error ! ( "Upload FAILED: {}" , e) ,
348- }
349-
336+ info ! ( "FFmpeg processing and direct WebDAV upload successful!" ) ;
350337 info ! ( "Job {} completed successfully" , job. id) ;
351338
352339 // Update job to completed via queue URL
@@ -359,6 +346,7 @@ async fn process_job(job: Job) -> Result<()> {
359346 }
360347 } else {
361348 error ! ( "FFmpeg FAILED with exit code: {}" , output. status) ;
349+ error ! ( "Direct WebDAV output failed - check FFmpeg stderr above for details" ) ;
362350
363351 if let Some ( queue_url) = & job. webdav_config . queue_url {
364352 let _ = update_job_status_remote ( queue_url, & job. id , JobStatus :: Failed , None ) . await ;
@@ -434,6 +422,18 @@ fn build_webdav_download_url(config: &WebDavConfig, path: &str) -> String {
434422 . replacen ( "://" , & format ! ( "://{}:{}@" , encode( & config. username) , encode( & config. password) ) , 1 )
435423}
436424
425+ fn build_webdav_upload_url ( config : & WebDavConfig , output_path : & str ) -> String {
426+ // Build full WebDAV URL for uploading
427+ // config.url is like: https://cloud.example.com/remote.php/dav/files/user/VideoTest
428+ // output_path is like: processed/filename.mp4
429+ // Result: https://user:pass@cloud.example.com/remote.php/dav/files/user/VideoTest/processed/filename.mp4
430+
431+ let base_url = config. url . trim_end_matches ( '/' ) ;
432+ let full_url = format ! ( "{}/{}" , base_url, output_path) ;
433+
434+ full_url. replacen ( "://" , & format ! ( "://{}:{}@" , encode( & config. username) , encode( & config. password) ) , 1 )
435+ }
436+
437437async fn update_job_status_remote (
438438 queue_url : & str ,
439439 job_id : & str ,
0 commit comments