use std::path::Path;
use std::time::Duration;
use super::Executor;
use crate::error::{Error, Result};
use crate::utils::fs::remove_temp_file;
pub struct FfmpegArgs {
parts: Vec<String>,
output: Option<String>,
overwrite: bool,
}
impl FfmpegArgs {
pub fn new() -> Self {
Self {
parts: Vec::new(),
output: None,
overwrite: false,
}
}
pub fn input(mut self, path: impl AsRef<str>) -> Self {
self.parts.push("-i".to_string());
self.parts.push(path.as_ref().to_string());
self
}
pub fn codec_copy(mut self) -> Self {
self.parts.push("-c".to_string());
self.parts.push("copy".to_string());
self
}
pub fn overwrite(mut self) -> Self {
self.overwrite = true;
self
}
pub fn output(mut self, path: impl AsRef<str>) -> Self {
self.output = Some(path.as_ref().to_string());
self
}
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.parts.extend(args.into_iter().map(Into::into));
self
}
pub fn arg(mut self, arg: impl Into<String>) -> Self {
self.parts.push(arg.into());
self
}
pub fn build(mut self) -> Vec<String> {
if self.overwrite {
self.parts.push("-y".to_string());
}
if let Some(output) = self.output {
self.parts.push(output);
}
self.parts
}
}
impl Default for FfmpegArgs {
fn default() -> Self {
Self::new()
}
}
pub async fn run_ffmpeg_with_tempfile(
ffmpeg_path: &Path,
base_path: &Path,
extension: &str,
args: FfmpegArgs,
timeout: Duration,
) -> Result<()> {
let temp_output_path = crate::utils::fs::create_temp_path(base_path, extension);
let temp_output_str = temp_output_path
.to_str()
.ok_or_else(|| Error::path_validation(&temp_output_path, "Invalid output path"))?;
tracing::debug!(
base_path = ?base_path,
temp_path = ?temp_output_path,
timeout_secs = timeout.as_secs(),
"✂️ Running ffmpeg with temp file"
);
let final_args = args.overwrite().output(temp_output_str).build();
let executor = Executor::new(ffmpeg_path.to_path_buf(), final_args, timeout);
if let Err(e) = executor.execute().await {
if temp_output_path.exists() {
remove_temp_file(&temp_output_path).await;
}
return Err(e);
}
tokio::fs::rename(&temp_output_path, base_path).await?;
tracing::debug!(base_path = ?base_path, "✅ ffmpeg temp file renamed to final path");
Ok(())
}