Files
simple-multimidia-track-aud…/src/infrastructure/process/mod.rs
T

74 lines
2.3 KiB
Rust

use anyhow::{Context, Result, anyhow};
use std::sync::mpsc::Sender;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
/// Executa o FFmpeg de forma assíncrona, capturando stderr em tempo real.
/// As linhas de saída são enviadas pelo canal `progress_tx` (std::sync::mpsc).
/// Um sinal enviado em `cancel_rx` encerra o processo imediatamente.
pub async fn run_ffmpeg_async(
args: Vec<String>,
progress_tx: Sender<String>,
cancel_rx: tokio::sync::oneshot::Receiver<()>,
) -> Result<()> {
let mut child = Command::new("ffmpeg")
.args(&args)
.stdin(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.spawn()
.context("Falha ao iniciar ffmpeg")?;
// Lê stderr em stream para exibir progresso em tempo real
if let Some(stderr) = child.stderr.take() {
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
let tx = progress_tx.clone();
tokio::spawn(async move {
while let Ok(Some(line)) = lines.next_line().await {
let _ = tx.send(line);
}
});
}
tokio::select! {
result = child.wait() => {
let status = result.context("Erro ao aguardar ffmpeg")?;
if !status.success() {
return Err(anyhow!(
"FFmpeg encerrou com código de saída: {:?}",
status.code()
));
}
Ok(())
}
_ = cancel_rx => {
let _ = child.kill().await;
Err(anyhow!("Cancelado pelo usuário"))
}
}
}
/// Verifica se um binário está disponível no PATH.
pub fn check_binary_available(binary: &str) -> Result<()> {
std::process::Command::new(binary)
.arg("-version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map_err(|_| {
anyhow!(
"'{}' não encontrado no PATH. Por favor, instale o FFmpeg.",
binary
)
})?;
Ok(())
}
/// Verifica se ffmpeg e ffprobe estão disponíveis.
pub fn validate_dependencies() -> Result<()> {
check_binary_available("ffmpeg")?;
check_binary_available("ffprobe")?;
Ok(())
}