feat: argparse and dry-run.

This commit is contained in:
2026-01-04 01:46:39 +08:00
parent 21e0e96841
commit 5ce56bda36
10 changed files with 656 additions and 90 deletions
+148
View File
@@ -0,0 +1,148 @@
use crate::dry_run::ShellScript;
use std::io::Result;
use std::path::Path;
use std::process::Command;
use std::io;
pub struct FFmpegCommand {
inputs: Vec<String>,
output: Option<String>,
codec: Option<String>,
overwrite: bool,
}
impl FFmpegCommand {
pub fn new() -> Self {
Self {
inputs: Vec::new(),
output: None,
codec: None,
overwrite: false,
}
}
pub fn input(mut self, path: &Path) -> Self {
self.inputs.push(path.display().to_string());
self
}
pub fn input_var(mut self, var: &str) -> Self {
self.inputs.push(format!("${}", var));
self
}
pub fn output(mut self, path: &Path) -> Self {
self.output = Some(path.display().to_string());
self
}
pub fn output_var(mut self, var: &str) -> Self {
self.output = Some(format!("${}", var));
self
}
pub fn codec(mut self, codec: &str) -> Self {
self.codec = Some(codec.to_string());
self
}
pub fn overwrite(mut self, overwrite: bool) -> Self {
self.overwrite = overwrite;
self
}
pub fn execute(self, dry_run: bool) -> Result<()> {
if dry_run {
self.execute_dry_run()
} else {
self.execute_real()
}
}
fn execute_dry_run(self) -> Result<()> {
let mut script = ShellScript::new();
let mut cmd_parts = vec!["ffmpeg".to_string()];
if self.overwrite {
cmd_parts.push("-y".to_string());
}
cmd_parts.push("-hide_banner".to_string());
for input in &self.inputs {
cmd_parts.push("-i".to_string());
cmd_parts.push(input.clone());
}
if let Some(ref codec) = self.codec {
cmd_parts.push("-c".to_string());
cmd_parts.push(codec.clone());
}
if let Some(ref output) = self.output {
cmd_parts.push(output.clone());
}
let cmd_str = cmd_parts.iter()
.map(|arg| {
if arg.starts_with('$') {
format!("\"{}\"", arg)
} else if arg.contains(' ') || arg.contains('$') {
format!("\"{}\"", arg.replace('"', r#"\""#))
} else {
arg.clone()
}
})
.collect::<Vec<_>>()
.join(" ");
script.add_command(&cmd_str);
crate::dry_run::print_shell_script(&script);
Ok(())
}
fn execute_real(self) -> Result<()> {
let mut cmd = Command::new("ffmpeg");
if self.overwrite {
cmd.arg("-y");
}
cmd.arg("-hide_banner");
for input in &self.inputs {
cmd.arg("-i");
if input.starts_with('$') {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Cannot execute with variable input: {}", input)
));
}
cmd.arg(input);
}
if let Some(ref codec) = self.codec {
cmd.arg("-c");
cmd.arg(codec);
}
if let Some(ref output) = self.output {
if output.starts_with('$') {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Cannot execute with variable output: {}", output)
));
}
cmd.arg(output);
}
let output = cmd.spawn()?.wait_with_output()?;
if !output.status.success() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("ffmpeg exited with status {}", output.status)
));
}
Ok(())
}
}