From e14795c493270873ca57814304695d222ce4c641 Mon Sep 17 00:00:00 2001 From: Gregory Ballantine Date: Thu, 1 Sep 2022 15:29:24 -0400 Subject: [PATCH] Added a simple program loop to repeatedly check for new video files --- src/config.rs | 40 +++++++------- src/repository.rs | 136 +++++++++++++++++++++++----------------------- src/transcoder.rs | 94 ++++++++++++++++++-------------- 3 files changed, 141 insertions(+), 129 deletions(-) diff --git a/src/config.rs b/src/config.rs index b465140..8c7441e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,32 +3,32 @@ use std::fs; #[derive(Deserialize)] pub struct Config { - pub transcoder: Transcoder, + pub transcoder: Transcoder, } impl Config { - pub fn new(config_path: &str) -> Config { - let resolved_path = shellexpand::tilde(config_path); - let config_text = fs::read_to_string(&*resolved_path).unwrap(); - let c: Config = toml::from_str(&config_text).unwrap(); - return c; - } + pub fn new(config_path: &str) -> Config { + let resolved_path = shellexpand::tilde(config_path); + let config_text = fs::read_to_string(&*resolved_path).unwrap(); + let c: Config = toml::from_str(&config_text).unwrap(); + return c; + } - pub fn get_repository(&self) -> String { - let resolved_path = shellexpand::tilde(&self.transcoder.repository); - return String::from(&*resolved_path); - } + pub fn get_repository(&self) -> String { + let resolved_path = shellexpand::tilde(&self.transcoder.repository); + return String::from(&*resolved_path); + } } #[derive(Deserialize)] pub struct Transcoder { - pub repository: String, - pub interval: u16, - pub video_format: String, - pub video_codec: String, - pub video_profile: String, - pub video_resolution: String, - pub video_framerate: u8, - pub video_color: String, - pub audio_codec: String, + pub repository: String, + pub interval: u16, + pub video_format: String, + pub video_codec: String, + pub video_profile: String, + pub video_resolution: String, + pub video_framerate: u8, + pub video_color: String, + pub audio_codec: String, } diff --git a/src/repository.rs b/src/repository.rs index 1c92636..d0da1d8 100644 --- a/src/repository.rs +++ b/src/repository.rs @@ -2,86 +2,86 @@ use std::fs; use std::path::Path; pub struct Repository { - pub base_dir: String, - pub ingest_dir: String, - pub archive_dir: String, - pub output_dir: String, + pub base_dir: String, + pub ingest_dir: String, + pub archive_dir: String, + pub output_dir: String, } impl Repository { - pub fn new(base_path: &str) -> Repository { - // create the base directory path - create_directory(base_path); + pub fn new(base_path: &str) -> Repository { + // create the base directory path + create_directory(base_path); - // create the needed sub-directories - let ingest_path = Path::new(base_path).join("ingest"); - create_directory(ingest_path.to_str().unwrap()); - let archive_path = Path::new(base_path).join("archive"); - create_directory(archive_path.to_str().unwrap()); - let output_path = Path::new(base_path).join("output"); - create_directory(output_path.to_str().unwrap()); + // create the needed sub-directories + let ingest_path = Path::new(base_path).join("ingest"); + create_directory(ingest_path.to_str().unwrap()); + let archive_path = Path::new(base_path).join("archive"); + create_directory(archive_path.to_str().unwrap()); + let output_path = Path::new(base_path).join("output"); + create_directory(output_path.to_str().unwrap()); - return Repository { - base_dir: String::from(base_path), - ingest_dir: String::from(ingest_path.to_str().unwrap()), - archive_dir: String::from(archive_path.to_str().unwrap()), - output_dir: String::from(output_path.to_str().unwrap()), - }; - } + return Repository { + base_dir: String::from(base_path), + ingest_dir: String::from(ingest_path.to_str().unwrap()), + archive_dir: String::from(archive_path.to_str().unwrap()), + output_dir: String::from(output_path.to_str().unwrap()), + }; + } - pub fn search_ingest(&self) -> Vec { - // read file entries from ingest - let files = fs::read_dir(&self.ingest_dir).unwrap(); + pub fn search_ingest(&self) -> Vec { + // read file entries from ingest + let files = fs::read_dir(&self.ingest_dir).unwrap(); - // create vec object and loop through entries to find what we want - let mut ingest_files: Vec = vec![]; - for f in files { - let f = f.unwrap(); - let path = f.path(); - if path.is_file() { - let file_path = path.file_name().unwrap().to_str(); - ingest_files.push(String::from(file_path.unwrap())); - } - } + // create vec object and loop through entries to find what we want + let mut ingest_files: Vec = vec![]; + for f in files { + let f = f.unwrap(); + let path = f.path(); + if path.is_file() { + let file_path = path.file_name().unwrap().to_str(); + ingest_files.push(String::from(file_path.unwrap())); + } + } - return ingest_files; - } + return ingest_files; + } - pub fn archive_file(&self, file: &str) { - let ingest_file = Path::new(&self.ingest_dir).join(file); - let archive_file = Path::new(&self.archive_dir).join(file); + pub fn archive_file(&self, file: &str) { + let ingest_file = Path::new(&self.ingest_dir).join(file); + let archive_file = Path::new(&self.archive_dir).join(file); - match fs::copy(&ingest_file, &archive_file) { - Ok(_) => { - println!("Archiving video file {}.", ingest_file.to_str().unwrap()); - }, - Err(e) => { - eprintln!("Error archiving file {}: {}", ingest_file.to_str().unwrap(), e); - std::process::exit(1); - } - } - } + match fs::copy(&ingest_file, &archive_file) { + Ok(_) => { + println!("Archiving video file {}.", ingest_file.to_str().unwrap()); + }, + Err(e) => { + eprintln!("Error archiving file {}: {}", ingest_file.to_str().unwrap(), e); + std::process::exit(1); + } + } + } - pub fn cleanup_file(&self, file: &str) { - let ingest_file = Path::new(&self.ingest_dir).join(file); - fs::remove_file(&ingest_file) - .expect("File deletion failed."); - } + pub fn cleanup_file(&self, file: &str) { + let ingest_file = Path::new(&self.ingest_dir).join(file); + fs::remove_file(&ingest_file) + .expect("File deletion failed."); + } } fn create_directory(path: &str) { - let d = Path::new(path); - if d.is_dir() { - println!("Directory {} already exists.", path); - } else { - match fs::create_dir(path) { - Ok(_) => { - println!("Creating directory {}.", path); - }, - Err(e) => { - eprintln!("Error creating {}: {}", path, e); - std::process::exit(1); - } - } - } + let d = Path::new(path); + if d.is_dir() { + println!("Directory {} already exists.", path); + } else { + match fs::create_dir(path) { + Ok(_) => { + println!("Creating directory {}.", path); + }, + Err(e) => { + eprintln!("Error creating {}: {}", path, e); + std::process::exit(1); + } + } + } } diff --git a/src/transcoder.rs b/src/transcoder.rs index 77f3ddc..efeeb7c 100644 --- a/src/transcoder.rs +++ b/src/transcoder.rs @@ -1,61 +1,73 @@ use std::path::Path; use std::process; +use std::{thread, time}; use crate::config::Config; use crate::repository::Repository; pub struct Transcoder { - config: Config, - repository: Repository, + config: Config, + repository: Repository, } impl Transcoder { - pub fn new(config: Config, repository: Repository) -> Transcoder { - return Transcoder{ - config: config, - repository: repository, - } - } + pub fn new(config: Config, repository: Repository) -> Transcoder { + return Transcoder{ + config: config, + repository: repository, + } + } - pub fn start(self) { - println!("Starting transcoder..."); + pub fn start(self) { + println!("Starting transcoder..."); - // search for files in ingest - let ingest_files = self.repository.search_ingest(); + loop { + // search for files in ingest + let ingest_files = self.repository.search_ingest(); - for i in ingest_files { - // copy the file to the archive - self.repository.archive_file(&i); + // check if we found any files to transcode + if ingest_files.len() < 1 { + println!("There were no files found in ingest to transcode; skipping run."); + } else { + for i in ingest_files { + // copy the file to the archive + self.repository.archive_file(&i); - // perform the video transcode - self.transcode(&i); + // perform the video transcode + self.transcode(&i); - // remove the source file - self.repository.cleanup_file(&i); - } - } + // remove the source file + self.repository.cleanup_file(&i); + } + } - fn transcode(&self, file: &str) { - let ingest_file = Path::new(&self.repository.ingest_dir).join(file); - let output_file = Path::new(&self.repository.output_dir).join(file); + // put the loop to sleep for X minutes + let sleep_minutes = time::Duration::from_secs((self.config.transcoder.interval * 60).into()); + thread::sleep(sleep_minutes); + } + } - let cmd_output = process::Command::new("/usr/bin/ffmpeg") - .arg("-i") .arg(&*ingest_file.to_string_lossy()) - .arg("-y") - .arg("-f") .arg(&self.config.transcoder.video_format) - .arg("-c:v") .arg(&self.config.transcoder.video_codec) - .arg("-s") .arg(&self.config.transcoder.video_resolution) - .arg("-r") .arg(format!("{}", self.config.transcoder.video_framerate)) - .arg("-vf") .arg(format!("format={}", &self.config.transcoder.video_color)) - .arg("-profile:v").arg(&self.config.transcoder.video_profile) - .arg("-c:a") .arg(&self.config.transcoder.audio_codec) - .arg(&*output_file.to_string_lossy()) - .output() - .expect("Failed to execute command"); + fn transcode(&self, file: &str) { + let ingest_file = Path::new(&self.repository.ingest_dir).join(file); + let output_file = Path::new(&self.repository.output_dir).join(file); - assert!(cmd_output.status.success()); + let cmd_output = process::Command::new("/usr/bin/ffmpeg") + .arg("-i") .arg(&*ingest_file.to_string_lossy()) + .arg("-y") + .arg("-f") .arg(&self.config.transcoder.video_format) + .arg("-c:v") .arg(&self.config.transcoder.video_codec) + .arg("-s") .arg(&self.config.transcoder.video_resolution) + .arg("-r") .arg(format!("{}", self.config.transcoder.video_framerate)) + .arg("-vf") .arg(format!("format={}", &self.config.transcoder.video_color)) + .arg("-profile:v").arg(&self.config.transcoder.video_profile) + .arg("-c:a") .arg(&self.config.transcoder.audio_codec) + .arg(&*output_file.to_string_lossy()) + .output() + .expect("Failed to execute command"); - let results_raw = &String::from_utf8_lossy(&cmd_output.stderr); - println!("{}", results_raw); - } + assert!(cmd_output.status.success()); + + let results_raw = &String::from_utf8_lossy(&cmd_output.stderr); + println!("{}", results_raw); + } }