feat: implementa funcionalidade de exportação de faixas de áudio e legenda

This commit is contained in:
2026-02-28 13:59:57 -03:00
parent a588ecd382
commit 4aa7cf2dd4
7 changed files with 334 additions and 12 deletions
+53 -3
View File
@@ -1,4 +1,5 @@
use crate::domain::entities::{Project, Track, TrackKind};
use crate::domain::entities::{MediaTrackInfo, Project, Track, TrackKind};
use crate::domain::value_objects::FilePath;
/// Constrói os argumentos do FFmpeg a partir de um Project.
///
@@ -126,14 +127,59 @@ impl FfmpegCommandBuilder {
args
}
/// Constrói args para exportar uma faixa individual de um arquivo de mídia.
///
/// - Áudio: usa `-c:a copy` para preservar o stream sem reencoding (RNF-01 mantido).
/// - Legenda: **omite** `-c copy` — o FFmpeg precisa converter o container de texto
/// (ex: stream `subrip` dentro de MKV → arquivo `.srt`). Essa é a única
/// exceção documentada ao invariante RNF-01, restrita a este método.
/// - Vídeo/outros: usa `-c copy`.
///
/// O `-y` é incluído para evitar que o processo bloqueie ao sobrescrever um arquivo
/// (a confirmação é responsabilidade do diálogo de salvamento da UI).
pub fn build_export(
source: &FilePath,
track: &MediaTrackInfo,
output: &FilePath,
) -> Vec<String> {
let mut args: Vec<String> = Vec::new();
args.push("-i".to_string());
args.push(source.to_string_lossy().to_string());
args.push("-map".to_string());
args.push(format!("0:{}", track.stream_index));
match &track.kind {
TrackKind::Audio => {
args.push("-c:a".to_string());
args.push("copy".to_string());
}
TrackKind::Subtitle => {
// Sem -c copy: o FFmpeg extrai a legenda convertendo o container de texto
// automaticamente. Não há reencoding de mídia envolvido.
}
_ => {
args.push("-c".to_string());
args.push("copy".to_string());
}
}
// Sobrescrever sem prompt interativo (o diálogo de salvamento já confirmou)
args.push("-y".to_string());
args.push(output.to_string_lossy().to_string());
args
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::application::use_cases::add_audio_track::AddAudioTrack;
use crate::domain::entities::{MkvOutput, Project, VideoFile};
use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage};
use crate::application::use_cases::add_audio_track::AddAudioTrack;
fn base_project() -> Project {
Project::new(
@@ -188,7 +234,11 @@ mod tests {
.unwrap();
let args = FfmpegCommandBuilder::build(&project);
assert!(args.contains(&"1:a".to_string()), "faltou map 1:a — args: {:?}", args);
assert!(
args.contains(&"1:a".to_string()),
"faltou map 1:a — args: {:?}",
args
);
}
#[test]