feat: add initial functionality
This commit is contained in:
parent
b85c1e6220
commit
c43fd906cd
7 changed files with 278 additions and 0 deletions
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# IDE files
|
||||
*.iml
|
||||
.idea/
|
||||
|
||||
# build files
|
||||
/target
|
||||
|
||||
# runtime files
|
||||
*.log
|
||||
/files
|
||||
|
||||
# config
|
||||
config.toml
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "bumblebee"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
simple-log = "1.6.0"
|
||||
toml = "0.7.3"
|
||||
serde = { version = "1.0.162", features = ["derive"] }
|
||||
glob = "0.3.0"
|
35
config.example.toml
Normal file
35
config.example.toml
Normal file
|
@ -0,0 +1,35 @@
|
|||
[files]
|
||||
input_path = "/data/input"
|
||||
output_path = "/data/output"
|
||||
include = [ 'mp4', 'avi', 'mkv' ] # file extensions to include
|
||||
keep_file_structure = true # e.g. /data/input/foo/bar.mp4 -> /data/output/foo/bar.webm
|
||||
|
||||
[files.cleanup]
|
||||
enabled = true
|
||||
original_cleanup_behavior = "delete" # delete, archive or keep
|
||||
|
||||
[files.cleanup.archive]
|
||||
path = "/data/archive"
|
||||
keep_file_structure = true # e.g. /data/input/foo/bar.mp4 -> /data/archive/foo/bar.mp4
|
||||
|
||||
[files.cleanup.delete]
|
||||
remove_empty_directories = true # if folder is empty after deleting file, delete folder
|
||||
|
||||
[ffmpeg]
|
||||
path = "/usr/bin/ffmpeg" # path to ffmpeg executable
|
||||
|
||||
[ffmpeg.process]
|
||||
niceness = 10 # 0 = highest priority
|
||||
threads = 4 # 0 = auto
|
||||
|
||||
[ffmpeg.output]
|
||||
format = "webm" # webm, mp4, mkv, etc.
|
||||
|
||||
[ffmpeg.output.video]
|
||||
codec = "libsvtav1"
|
||||
bitrate = 0 # 0 = auto
|
||||
crf = 30 # 0 = lossless
|
||||
|
||||
[ffmpeg.output.audio]
|
||||
codec = "libopus"
|
||||
bitrate = 0 # 0 = auto
|
132
src/configuration.rs
Normal file
132
src/configuration.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
use std::error::Error;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[allow(non_camel_case_types)] // this is allowed cuz we want to use snake case in the config file
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum ConfigFilesCleanupOriginalBehavior {
|
||||
delete,
|
||||
archive,
|
||||
keep,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFilesCleanupArchive {
|
||||
pub path: String,
|
||||
pub keep_file_structure: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFilesCleanupDelete {
|
||||
pub remove_empty_directories: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFilesCleanup {
|
||||
pub enabled: bool,
|
||||
pub original_cleanup_behavior: ConfigFilesCleanupOriginalBehavior,
|
||||
pub archive: ConfigFilesCleanupArchive,
|
||||
pub delete: ConfigFilesCleanupDelete,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFiles {
|
||||
pub keep_file_structure: bool,
|
||||
pub input_path: String,
|
||||
pub output_path: String,
|
||||
pub include: Vec<String>,
|
||||
pub cleanup: ConfigFilesCleanup,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFFmpegProcess {
|
||||
pub threads: u8,
|
||||
pub niceness: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFFmpegOutputVideo {
|
||||
pub codec: String,
|
||||
pub bitrate: u32,
|
||||
pub crf: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFFmpegOutputAudio {
|
||||
pub codec: String,
|
||||
pub bitrate: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFFmpegOutput {
|
||||
pub format: String,
|
||||
pub video: ConfigFFmpegOutputVideo,
|
||||
pub audio: ConfigFFmpegOutputAudio,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ConfigFFmpeg {
|
||||
pub path: String,
|
||||
pub process: ConfigFFmpegProcess,
|
||||
pub output: ConfigFFmpegOutput,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Config {
|
||||
pub files: ConfigFiles,
|
||||
pub ffmpeg: ConfigFFmpeg,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Config {
|
||||
Config {
|
||||
files: ConfigFiles {
|
||||
keep_file_structure: false,
|
||||
input_path: String::from("/data/input"),
|
||||
output_path: String::from("/data/output"),
|
||||
include: Vec::new(),
|
||||
cleanup: ConfigFilesCleanup {
|
||||
enabled: false,
|
||||
original_cleanup_behavior: ConfigFilesCleanupOriginalBehavior::delete,
|
||||
archive: ConfigFilesCleanupArchive {
|
||||
path: String::from("/data/archive"),
|
||||
keep_file_structure: false,
|
||||
},
|
||||
delete: ConfigFilesCleanupDelete {
|
||||
remove_empty_directories: false
|
||||
},
|
||||
},
|
||||
},
|
||||
ffmpeg: ConfigFFmpeg {
|
||||
path: String::from("/usr/bin/ffmpeg"),
|
||||
process: ConfigFFmpegProcess {
|
||||
threads: 0,
|
||||
niceness: 0,
|
||||
},
|
||||
output: ConfigFFmpegOutput {
|
||||
format: String::from("webm"),
|
||||
video: ConfigFFmpegOutputVideo {
|
||||
codec: String::from("libsvtav1"),
|
||||
bitrate: 0,
|
||||
crf: 0,
|
||||
},
|
||||
audio: ConfigFFmpegOutputAudio {
|
||||
codec: String::from("libopus"),
|
||||
bitrate: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_file(path: &str) -> Config {
|
||||
let config_file = std::fs::read_to_string(path).expect("Failed to read config file");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
src/files.rs
Normal file
21
src/files.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
pub fn get_files<S: Into<String>>(path: S) -> Vec<PathBuf> {
|
||||
let mut files = Vec::new();
|
||||
let mut dirs = Vec::new();
|
||||
dirs.push(PathBuf::from(path.into()));
|
||||
|
||||
while let Some(dir) = dirs.pop() {
|
||||
for entry in std::fs::read_dir(dir).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
dirs.push(path);
|
||||
} else {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files
|
||||
}
|
43
src/main.rs
Normal file
43
src/main.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
#[macro_use]
|
||||
extern crate simple_log;
|
||||
|
||||
use std::env;
|
||||
use simple_log::LogConfigBuilder;
|
||||
|
||||
mod configuration;
|
||||
mod files;
|
||||
mod processing;
|
||||
|
||||
fn main() {
|
||||
setup_logger();
|
||||
let config = configuration::Config::from_file(&env::var("CONFIG").unwrap_or(String::from("./config.toml")));
|
||||
debug!("Config: {:#?}", &config);
|
||||
|
||||
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::<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();
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_logger() {
|
||||
let config = LogConfigBuilder::builder()
|
||||
.path("backups.log")
|
||||
.size(10 * 1000)
|
||||
.roll_count(10)
|
||||
.time_format("%Y-%m-%d %H:%M:%S")
|
||||
.level("debug")
|
||||
.output_file()
|
||||
.output_console()
|
||||
.build();
|
||||
|
||||
simple_log::new(config).unwrap();
|
||||
}
|
22
src/processing.rs
Normal file
22
src/processing.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
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!();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue