#[macro_use] extern crate simple_log; use std::env; use std::path::PathBuf; use simple_log::LogConfigBuilder; use renice::renice; use transcode::job::TranscodeJob; use crate::configuration::Config; mod configuration; mod files; mod ffmpeg; mod renice; mod transcode; mod error; fn main() { let config = configuration::Config::from_file(&env::var("CONFIG").unwrap_or(String::from("./config.toml"))); setup_logger(&config); trace!("Config: {:#?}", &config); debug!("commit: {}", env!("GIT_COMMIT_HASH")); debug!("tag: {}", env!("GIT_TAG")); let input_files = files::get_files(&config.files.input_path) .into_iter() .filter(|path| { let path = path.to_str().unwrap(); config.files.include.iter().any(|include| path.contains(include)) }) .collect::>(); info!("Found {} file(s) to be processed.", input_files.len()); let success_count = start_transcode_run(&input_files, &config); info!("Finished processing {}/{} file(s).", success_count, input_files.len()); } fn start_transcode_run(input_files: &Vec, config: &Config) -> i32 { let mut success_count = 0; for file in input_files { let fail = |job: &TranscodeJob, remarks: &str| { error!("Failed to process file {}: {}", job.input, remarks); }; let mut output_path = if let Some(keep_directory_structure) = config.files.keep_directory_structure { if keep_directory_structure { let mut output_path = PathBuf::from(&config.files.output_path); output_path.push(file.strip_prefix(&config.files.input_path).unwrap()); output_path } else { PathBuf::from(&config.files.output_path) .join(file.file_name().unwrap()) } } else { PathBuf::from(&config.files.output_path) .join(file.file_name().unwrap()) }; output_path.set_extension(&config.ffmpeg.output.format); let job = TranscodeJob::new( file.to_str().unwrap(), output_path.to_str().unwrap(), ); // check if overwriting if job.check_if_exists() { if config.ffmpeg.overwrite.unwrap_or(false) { info!("Overwriting file {}.", job.input); } else { info!("Skipping file {} as it already exists.", job.input); continue; } } info!("Processing file {}.", job.input); let mut child = match job.run(&config.ffmpeg) { Ok(child) => child, Err(e) => { fail(&job, &format!("Failed to start ffmpeg process: {}", e)); continue; } }; if let Some(process) = &config.ffmpeg.process { if (process.niceness.unwrap_or(0)) > 0 { if let Err(e) = renice(child.id(), process.niceness.unwrap_or(0)) { error!("Failed to renice ffmpeg process: {}", e); } } } if let Err(e) = child.wait() { fail(&job, &format!("Failed to wait for ffmpeg process: {}", e)); continue; } if let Some(cleanup) = &config.files.cleanup { if !cleanup.enabled { () } match cleanup.original_cleanup_behavior { configuration::ConfigFilesCleanupOriginalBehavior::delete => { if let Err(e) = std::fs::remove_file(&job.input) { error!("Failed to delete original file {}: {}", job.input, e); } }, configuration::ConfigFilesCleanupOriginalBehavior::archive => { let mut archive_path = PathBuf::from(&cleanup.archive.path); archive_path.push(job.input.strip_prefix(&config.files.input_path).unwrap()); if let Err(e) = std::fs::create_dir_all(archive_path.parent().unwrap()) { error!("Failed to create archive directory {}: {}", archive_path.parent().unwrap().to_str().unwrap(), e); } if let Err(e) = std::fs::rename(&job.input, &archive_path) { error!("Failed to archive original file {}: {}", job.input, e); } }, configuration::ConfigFilesCleanupOriginalBehavior::keep => (), } } info!("Finished processing file {}.", job.input); success_count += 1; } success_count } fn setup_logger(config: &configuration::Config) { let log_config = LogConfigBuilder::builder() .path("backups.log") .size(10 * 1000) .roll_count(10) .time_format("%Y-%m-%d %H:%M:%S") .level(if config.is_debug() { "trace" } else { "info" }) .output_file() .output_console() .build(); simple_log::new(log_config).unwrap(); }