use std::path::PathBuf; use anyhow::Result; use notify::{Watcher, RecursiveMode, Event, EventKind}; use tokio::sync::mpsc; mod services; use services::sermon_converter::VideoConverter; #[tokio::main] async fn main() -> Result<()> { let watch_path = PathBuf::from("/home/rockvilleav/Sync/Sermons"); let output_path = PathBuf::from("/media/archive/jellyfin/sermons"); // Ensure directories exist if !watch_path.exists() { println!("Creating watch directory: {}", watch_path.display()); std::fs::create_dir_all(&watch_path)?; } if !output_path.exists() { println!("Creating output directory: {}", output_path.display()); std::fs::create_dir_all(&output_path)?; } println!("Starting sermon converter service..."); println!("Watching directory: {}", watch_path.display()); println!("Output directory: {}", output_path.display()); let converter = VideoConverter::new(output_path); // Process existing files first println!("Checking for existing files..."); if let Ok(entries) = std::fs::read_dir(&watch_path) { for entry in entries { if let Ok(entry) = entry { let path = entry.path(); if path.to_string_lossy().contains(".syncthing.") { println!("Skipping temporary file: {}", path.display()); continue; } if let Some(ext) = path.extension() { if ext == "mp4" { println!("Processing existing MP4 file: {}", path.display()); if let Err(e) = converter.process_file(path).await { eprintln!("Error processing existing file: {}", e); } } } } } } // Set up file watcher let (tx, mut rx) = mpsc::channel(100); let mut watcher = notify::recommended_watcher(move |res: Result| { let tx = tx.clone(); match res { Ok(event) => { match &event.kind { EventKind::Create(_) => println!("File created: {:?}", event.paths), EventKind::Modify(notify::event::ModifyKind::Name(mode)) => { match mode { notify::event::RenameMode::From => println!("File renamed from: {:?}", event.paths), notify::event::RenameMode::To => println!("File renamed to: {:?}", event.paths), notify::event::RenameMode::Both => println!("File renamed: {:?}", event.paths), _ => println!("Other rename event: {:?}", event), } }, _ => println!("Other file event: {:?}", event), } if let Err(e) = tx.blocking_send(event) { eprintln!("Error sending event: {}", e); } } Err(e) => eprintln!("Watch error: {}", e), } })?; watcher.watch(&watch_path, RecursiveMode::NonRecursive)?; println!("Watcher started successfully"); while let Some(event) = rx.recv().await { match event.kind { EventKind::Create(_) | EventKind::Modify(notify::event::ModifyKind::Name(notify::event::RenameMode::To)) => { for path in event.paths { // Skip temporary and non-MP4 files if path.to_string_lossy().contains(".syncthing.") { println!("Skipping temporary Syncthing file: {}", path.display()); continue; } if let Some(ext) = path.extension() { if ext == "mp4" { println!("New MP4 file detected: {}", path.display()); // Wait a short time to ensure file is fully written tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; println!("Starting processing for: {}", path.display()); if let Err(e) = converter.process_file(path.clone()).await { eprintln!("Error processing file {}: {}", path.display(), e); } else { println!("Successfully processed: {}", path.display()); } } else { println!("Ignoring non-MP4 file: {}", path.display()); } } else { println!("Ignoring file without extension: {}", path.display()); } } } _ => { // Log other events at debug level println!("Ignoring event: {:?}", event); } } } Ok(()) }