feat: add AdjustExistingTrackDrift use case to adjust drift scale of existing tracks
- Implemented AdjustExistingTrackDrift struct with execute method to modify drift scale of existing tracks in a project. - Added tests for adjusting drift scale, handling non-existent track IDs, and invalid scale values. - Updated MediaTrackInfo and AudioTrack entities to include drift_scale field. - Enhanced Project entity with needs_mkvmerge method to determine if mkvmerge is required based on drift scale. - Integrated drift scale adjustments into existing track list UI, allowing users to modify drift values. - Updated various use cases and UI components to support drift scale functionality, including add audio/subtitle forms and batch processing. - Implemented mkvmerge availability check and integrated it into the application workflow for conditional processing.
This commit is contained in:
@@ -12,6 +12,13 @@ pub trait MediaProcessorPort {
|
||||
fn execute(&self, args: Vec<String>) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Port para muxing com suporte a escala temporal (implementado pelo MkvmergeGateway).
|
||||
/// Estruturalmente idêntico ao `MediaProcessorPort` — separado por semântica, não por interface.
|
||||
#[allow(dead_code)]
|
||||
pub trait ContainerMuxPort {
|
||||
fn execute(&self, args: Vec<String>) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Port para abstrair acesso ao sistema de arquivos.
|
||||
#[allow(dead_code)]
|
||||
pub trait FileSystemPort {
|
||||
|
||||
@@ -13,9 +13,10 @@ impl AddAudioTrack {
|
||||
language: TrackLanguage,
|
||||
is_default: bool,
|
||||
title: String,
|
||||
drift_scale: f64,
|
||||
) -> Result<()> {
|
||||
let id = project.next_track_id();
|
||||
let track = AudioTrack::new(id, path, offset, language, is_default, title);
|
||||
let track = AudioTrack::new(id, path, offset, language, is_default, title, drift_scale);
|
||||
project.tracks.push(Track::Audio(track));
|
||||
Ok(())
|
||||
}
|
||||
@@ -43,6 +44,7 @@ mod tests {
|
||||
lang,
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -13,9 +13,10 @@ impl AddSubtitle {
|
||||
language: TrackLanguage,
|
||||
is_default: bool,
|
||||
title: String,
|
||||
drift_scale: f64,
|
||||
) -> Result<()> {
|
||||
let id = project.next_track_id();
|
||||
let track = SubtitleTrack::new(id, path, offset, language, is_default, title);
|
||||
let track = SubtitleTrack::new(id, path, offset, language, is_default, title, drift_scale);
|
||||
project.tracks.push(Track::Subtitle(track));
|
||||
Ok(())
|
||||
}
|
||||
@@ -42,6 +43,7 @@ mod tests {
|
||||
lang,
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
use crate::domain::entities::Project;
|
||||
use crate::domain::value_objects::TrackId;
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
/// Ajusta o fator de escala temporal (drift) de uma faixa externa pelo TrackId.
|
||||
///
|
||||
/// Um `scale != 1.0` indica que o projeto precisará usar o pipeline mkvmerge.
|
||||
#[allow(dead_code)]
|
||||
pub struct AdjustDrift;
|
||||
|
||||
impl AdjustDrift {
|
||||
#[allow(dead_code)]
|
||||
pub fn execute(project: &mut Project, id: TrackId, scale: f64) -> Result<()> {
|
||||
if scale <= 0.0 {
|
||||
return Err(anyhow!("O fator de escala deve ser maior que zero: {}", scale));
|
||||
}
|
||||
let track = project
|
||||
.find_track_mut(id)
|
||||
.ok_or_else(|| anyhow!("Faixa não encontrada: {:?}", id))?;
|
||||
track.set_drift_scale(scale);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::application::use_cases::add_audio_track::AddAudioTrack;
|
||||
use crate::domain::entities::{MkvOutput, VideoFile};
|
||||
use crate::domain::value_objects::{FilePath, SyncOffset, TrackLanguage};
|
||||
|
||||
fn make_project() -> Project {
|
||||
Project::new(
|
||||
VideoFile::new(FilePath::from("input.mkv")),
|
||||
MkvOutput::new(FilePath::from("output.mkv")),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ajusta_drift_existente() {
|
||||
let mut p = make_project();
|
||||
let lang = TrackLanguage::new("por").unwrap();
|
||||
AddAudioTrack::execute(
|
||||
&mut p,
|
||||
FilePath::from("audio.aac"),
|
||||
SyncOffset::default(),
|
||||
lang,
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
let id = p.tracks[0].id();
|
||||
|
||||
AdjustDrift::execute(&mut p, id, 0.99983).unwrap();
|
||||
assert!((p.tracks[0].drift_scale() - 0.99983).abs() < 1e-9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erro_se_id_inexistente() {
|
||||
let mut p = make_project();
|
||||
let result = AdjustDrift::execute(&mut p, TrackId::new(99), 0.99983);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erro_se_scale_zero() {
|
||||
let mut p = make_project();
|
||||
let lang = TrackLanguage::new("por").unwrap();
|
||||
AddAudioTrack::execute(
|
||||
&mut p,
|
||||
FilePath::from("audio.aac"),
|
||||
SyncOffset::default(),
|
||||
lang,
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
let id = p.tracks[0].id();
|
||||
let result = AdjustDrift::execute(&mut p, id, 0.0);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::domain::entities::Project;
|
||||
use crate::domain::value_objects::TrackId;
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
/// Ajusta o fator de escala temporal (drift) de uma faixa já presente no arquivo original.
|
||||
///
|
||||
/// Opera sobre `project.existing_tracks` (faixas lidas via ffprobe).
|
||||
/// Um `scale != 1.0` indica que o projeto precisará usar o pipeline mkvmerge.
|
||||
#[allow(dead_code)]
|
||||
pub struct AdjustExistingTrackDrift;
|
||||
|
||||
impl AdjustExistingTrackDrift {
|
||||
#[allow(dead_code)]
|
||||
pub fn execute(project: &mut Project, id: TrackId, scale: f64) -> Result<()> {
|
||||
if scale <= 0.0 {
|
||||
return Err(anyhow!("O fator de escala deve ser maior que zero: {}", scale));
|
||||
}
|
||||
let track = project
|
||||
.find_existing_track_mut(id)
|
||||
.ok_or_else(|| anyhow!("Faixa existente não encontrada: {:?}", id))?;
|
||||
track.drift_scale = scale;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain::entities::{MediaTrackInfo, MkvOutput, TrackKind, VideoFile};
|
||||
use crate::domain::value_objects::{FilePath, TrackId};
|
||||
|
||||
fn make_project() -> Project {
|
||||
Project::new(
|
||||
VideoFile::new(FilePath::from("input.mkv")),
|
||||
MkvOutput::new(FilePath::from("output.mkv")),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ajusta_drift_em_faixa_existente() {
|
||||
let mut p = make_project();
|
||||
let id = TrackId::new(1);
|
||||
p.existing_tracks.push(MediaTrackInfo::new(
|
||||
id,
|
||||
TrackKind::Audio,
|
||||
"aac",
|
||||
None,
|
||||
0,
|
||||
));
|
||||
|
||||
AdjustExistingTrackDrift::execute(&mut p, id, 0.99983).unwrap();
|
||||
assert!((p.existing_tracks[0].drift_scale - 0.99983).abs() < 1e-9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erro_se_id_inexistente() {
|
||||
let mut p = make_project();
|
||||
let result = AdjustExistingTrackDrift::execute(&mut p, TrackId::new(99), 0.99983);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erro_se_scale_zero() {
|
||||
let mut p = make_project();
|
||||
let id = TrackId::new(1);
|
||||
p.existing_tracks.push(MediaTrackInfo::new(
|
||||
id,
|
||||
TrackKind::Audio,
|
||||
"aac",
|
||||
None,
|
||||
0,
|
||||
));
|
||||
let result = AdjustExistingTrackDrift::execute(&mut p, id, 0.0);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ mod tests {
|
||||
lang,
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
let id = project.tracks[0].id();
|
||||
|
||||
@@ -51,6 +51,7 @@ mod tests {
|
||||
TrackLanguage::new("por").unwrap(),
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
pub mod add_audio_track;
|
||||
pub mod add_subtitle;
|
||||
pub mod adjust_drift;
|
||||
pub mod adjust_existing_track_drift;
|
||||
pub mod adjust_sync;
|
||||
pub mod edit_existing_track_sync;
|
||||
pub mod export_track;
|
||||
|
||||
@@ -40,6 +40,7 @@ mod tests {
|
||||
TrackLanguage::new("por").unwrap(),
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)));
|
||||
assert_eq!(project.tracks.len(), 1);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ mod tests {
|
||||
TrackLanguage::new("por").unwrap(),
|
||||
false,
|
||||
String::new(),
|
||||
1.0,
|
||||
)
|
||||
.unwrap();
|
||||
let id = project.tracks[0].id();
|
||||
|
||||
Reference in New Issue
Block a user