feats: added renice module added jobs added ffmpeg command builders.
This commit is contained in:
parent
c43fd906cd
commit
a905cdd4b9
10 changed files with 205 additions and 36 deletions
|
@ -9,4 +9,3 @@ edition = "2021"
|
|||
simple-log = "1.6.0"
|
||||
toml = "0.7.3"
|
||||
serde = { version = "1.0.162", features = ["derive"] }
|
||||
glob = "0.3.0"
|
21
src/configuration/ffmpeg.rs
Normal file
21
src/configuration/ffmpeg.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use crate::configuration::ConfigFFmpeg;
|
||||
use crate::ffmpeg::FFmpegCommandOptions;
|
||||
|
||||
impl ConfigFFmpeg {
|
||||
pub fn build_command_options<S: Into<String>>(&self, input_path: S, output_path: S) -> FFmpegCommandOptions {
|
||||
FFmpegCommandOptions {
|
||||
input: input_path.into(),
|
||||
output: output_path.into(),
|
||||
|
||||
video_codec: self.output.video.codec.clone(),
|
||||
video_bitrate: if self.output.video.bitrate > 0 { Some(self.output.video.bitrate) } else { None },
|
||||
video_crf: if self.output.video.crf > 0 { Some(self.output.video.crf) } else { None },
|
||||
|
||||
audio_codec: self.output.audio.codec.clone(),
|
||||
audio_bitrate: if self.output.audio.bitrate > 0 { Some(self.output.audio.bitrate) } else { None },
|
||||
|
||||
threads: if self.process.threads > 0 { Some(self.process.threads) } else { None },
|
||||
niceness: if self.process.niceness > 0 { Some(self.process.niceness) } else { None },
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
use std::error::Error;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::ffmpeg::FFmpegCommandOptions;
|
||||
|
||||
mod ffmpeg;
|
||||
|
||||
#[allow(non_camel_case_types)] // this is allowed cuz we want to use snake case in the config file
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
@ -72,6 +77,7 @@ pub struct ConfigFFmpeg {
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Config {
|
||||
pub debug: Option<bool>,
|
||||
pub files: ConfigFiles,
|
||||
pub ffmpeg: ConfigFFmpeg,
|
||||
}
|
||||
|
@ -79,6 +85,7 @@ pub struct Config {
|
|||
impl Config {
|
||||
pub fn new() -> Config {
|
||||
Config {
|
||||
debug: None,
|
||||
files: ConfigFiles {
|
||||
keep_file_structure: false,
|
||||
input_path: String::from("/data/input"),
|
||||
|
@ -123,10 +130,15 @@ impl Config {
|
|||
match toml::from_str(&config_file) {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
error!("Failed to parse config file: {}", e.message());
|
||||
error!("Please check your config file and try again.");
|
||||
std::process::exit(1);
|
||||
panic!("Failed to parse config file: {}", e.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_debug(&self) -> bool {
|
||||
match self.debug {
|
||||
Some(debug) => debug,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
88
src/ffmpeg.rs
Normal file
88
src/ffmpeg.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
use std::process::Command;
|
||||
|
||||
pub struct FFmpegCommandOptions {
|
||||
pub input: String,
|
||||
pub output: String,
|
||||
|
||||
pub video_codec: String,
|
||||
pub video_bitrate: Option<u32>,
|
||||
pub video_crf: Option<u8>,
|
||||
|
||||
pub audio_codec: String,
|
||||
pub audio_bitrate: Option<u32>,
|
||||
|
||||
pub threads: Option<u8>,
|
||||
pub niceness: Option<u8>
|
||||
}
|
||||
|
||||
impl FFmpegCommandOptions {
|
||||
pub fn to_args(&self) -> Vec<String> {
|
||||
let mut args = Vec::new();
|
||||
args.push("-i".to_string());
|
||||
args.push(self.input.clone());
|
||||
args.push("-c:v".to_string());
|
||||
args.push(self.video_codec.clone());
|
||||
args.push("-c:a".to_string());
|
||||
args.push(self.audio_codec.clone());
|
||||
|
||||
if let Some(bitrate) = self.video_bitrate {
|
||||
args.push("-b:v".to_string());
|
||||
args.push(bitrate.to_string());
|
||||
}
|
||||
|
||||
if let Some(crf) = self.video_crf {
|
||||
args.push("-crf".to_string());
|
||||
args.push(crf.to_string());
|
||||
}
|
||||
|
||||
if let Some(bitrate) = self.audio_bitrate {
|
||||
args.push("-b:a".to_string());
|
||||
args.push(bitrate.to_string());
|
||||
}
|
||||
|
||||
if let Some(threads) = self.threads {
|
||||
args.push("-threads".to_string());
|
||||
args.push(threads.to_string());
|
||||
}
|
||||
|
||||
if let Some(niceness) = self.niceness {
|
||||
args.push("-threads".to_string());
|
||||
args.push(niceness.to_string());
|
||||
}
|
||||
|
||||
args.push(self.output.clone());
|
||||
|
||||
args
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_command(program: &str, options: &FFmpegCommandOptions) -> Command {
|
||||
let mut command = Command::new(program);
|
||||
command.arg("-i").arg(&options.input);
|
||||
command.arg("-c:v").arg(&options.video_codec);
|
||||
command.arg("-c:a").arg(&options.audio_codec);
|
||||
|
||||
if let Some(bitrate) = options.video_bitrate {
|
||||
command.arg("-b:v").arg(bitrate.to_string());
|
||||
}
|
||||
|
||||
if let Some(crf) = options.video_crf {
|
||||
command.arg("-crf").arg(crf.to_string());
|
||||
}
|
||||
|
||||
if let Some(bitrate) = options.audio_bitrate {
|
||||
command.arg("-b:a").arg(bitrate.to_string());
|
||||
}
|
||||
|
||||
if let Some(threads) = options.threads {
|
||||
command.arg("-threads").arg(threads.to_string());
|
||||
}
|
||||
|
||||
if let Some(niceness) = options.niceness {
|
||||
command.arg("-threads").arg(niceness.to_string());
|
||||
}
|
||||
|
||||
command.arg(&options.output);
|
||||
|
||||
command
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
use crate::configuration::Config;
|
||||
|
||||
pub fn get_files<S: Into<String>>(path: S) -> Vec<PathBuf> {
|
||||
let mut files = Vec::new();
|
||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -2,15 +2,20 @@
|
|||
extern crate simple_log;
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use simple_log::LogConfigBuilder;
|
||||
use renice::renice;
|
||||
use transcode::job::TranscodeJob;
|
||||
|
||||
mod configuration;
|
||||
mod files;
|
||||
mod processing;
|
||||
mod ffmpeg;
|
||||
mod renice;
|
||||
mod transcode;
|
||||
|
||||
fn main() {
|
||||
setup_logger();
|
||||
let config = configuration::Config::from_file(&env::var("CONFIG").unwrap_or(String::from("./config.toml")));
|
||||
setup_logger(&config);
|
||||
debug!("Config: {:#?}", &config);
|
||||
|
||||
let input_files = files::get_files(&config.files.input_path)
|
||||
|
@ -22,22 +27,43 @@ fn main() {
|
|||
.collect::<Vec<_>>();
|
||||
info!("Found {} file(s) to be processed.", input_files.len());
|
||||
|
||||
for files in input_files {
|
||||
let operation = processing::TranscodeOperation::new(&config.ffmpeg, files);
|
||||
operation.run();
|
||||
for file in input_files {
|
||||
let mut output_path = file.clone();
|
||||
output_path.set_extension(&config.ffmpeg.output.format);
|
||||
let output_path = PathBuf::from(&config.files.output_path).join(output_path.file_name().unwrap()); // TODO: This is a bit of a mess.
|
||||
|
||||
let job = TranscodeJob::new(
|
||||
file.to_str().unwrap(),
|
||||
output_path.to_str().unwrap()
|
||||
);
|
||||
|
||||
if job.check_if_exists() {
|
||||
info!("Skipping file {} because it already exists.", job.output);
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Processing file {}.", job.input);
|
||||
let mut child = job.run(&config.ffmpeg).unwrap();
|
||||
if (config.ffmpeg.process.niceness > 0) {
|
||||
renice(child.id(), config.ffmpeg.process.niceness)
|
||||
.expect("Failed to renice process.");
|
||||
}
|
||||
child.wait().expect("Failed to wait for process.");
|
||||
|
||||
// TODO: Cleanup
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_logger() {
|
||||
let config = LogConfigBuilder::builder()
|
||||
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("debug")
|
||||
.level(if config.is_debug() { "debug" } else { "info" })
|
||||
.output_file()
|
||||
.output_console()
|
||||
.build();
|
||||
|
||||
simple_log::new(config).unwrap();
|
||||
simple_log::new(log_config).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
use std::path::PathBuf;
|
||||
use crate::configuration::ConfigFFmpeg;
|
||||
|
||||
pub struct TranscodeOperation<'f> {
|
||||
pub ffmpeg: &'f ConfigFFmpeg,
|
||||
pub file: PathBuf,
|
||||
}
|
||||
|
||||
impl<'f> TranscodeOperation<'f> {
|
||||
pub fn new(ffmpeg: &'f ConfigFFmpeg, file: PathBuf) -> TranscodeOperation {
|
||||
TranscodeOperation {
|
||||
ffmpeg,
|
||||
file,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
info!("Transcoding file: {:?}...", &self.file);
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
10
src/renice.rs
Normal file
10
src/renice.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use std::error::Error;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn renice(pid: u32, niceness: u8) -> Result<(), Box<dyn Error>> {
|
||||
let mut command = Command::new("renice");
|
||||
command.arg(niceness.to_string());
|
||||
command.arg(pid.to_string());
|
||||
command.spawn()?;
|
||||
Ok(())
|
||||
}
|
33
src/transcode/job.rs
Normal file
33
src/transcode/job.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command};
|
||||
use crate::configuration::{Config, ConfigFFmpeg};
|
||||
|
||||
pub struct TranscodeJob {
|
||||
pub input: String,
|
||||
pub output: String
|
||||
}
|
||||
|
||||
impl TranscodeJob {
|
||||
pub fn new<S: Into<String>>(input: S, output: S) -> TranscodeJob {
|
||||
TranscodeJob {
|
||||
input: input.into(),
|
||||
output: output.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_command(&self, ffmpeg_config: &ConfigFFmpeg) -> Command {
|
||||
let options = ffmpeg_config.build_command_options(&self.input, &self.output);
|
||||
let mut command = Command::new("ffmpeg");
|
||||
command.args(options.to_args());
|
||||
command
|
||||
}
|
||||
|
||||
pub fn check_if_exists(&self) -> bool {
|
||||
std::path::Path::new(&self.output).exists()
|
||||
}
|
||||
|
||||
pub fn run(&self, ffmpeg_config: &ConfigFFmpeg) -> Result<Child, std::io::Error> {
|
||||
let mut command = self.build_command(ffmpeg_config);
|
||||
command.spawn()
|
||||
}
|
||||
}
|
1
src/transcode/mod.rs
Normal file
1
src/transcode/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod job;
|
Loading…
Reference in a new issue