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:
@@ -7,19 +7,24 @@ use std::collections::HashMap;
|
||||
/// Evento emitido por interações com a lista de faixas existentes.
|
||||
pub enum ExistingTrackEvent {
|
||||
OffsetChanged(TrackId, SyncOffset),
|
||||
/// O fator de escala temporal de uma faixa existente foi alterado.
|
||||
DriftChanged(TrackId, f64),
|
||||
/// O usuário solicitou exportar a faixa identificada por `TrackId`.
|
||||
ExportRequested(TrackId),
|
||||
}
|
||||
|
||||
/// Lista as faixas detectadas no arquivo de vídeo e permite editar o offset de cada uma.
|
||||
/// Lista as faixas detectadas no arquivo de vídeo e permite editar o offset e o drift de cada uma.
|
||||
pub struct ExistingTrackList {
|
||||
offset_fields: HashMap<u32, SyncOffsetField>,
|
||||
/// Campos de drift raw por track id (percentual digitado pelo usuário).
|
||||
drift_fields: HashMap<u32, String>,
|
||||
}
|
||||
|
||||
impl ExistingTrackList {
|
||||
pub fn new() -> Self {
|
||||
ExistingTrackList {
|
||||
offset_fields: HashMap::new(),
|
||||
drift_fields: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,14 +34,20 @@ impl ExistingTrackList {
|
||||
self.offset_fields
|
||||
.entry(track.id.val())
|
||||
.or_insert_with(|| SyncOffsetField::new(track.offset));
|
||||
self.drift_fields
|
||||
.entry(track.id.val())
|
||||
.or_insert_with(|| format!("{:.5}", track.drift_scale * 100.0));
|
||||
}
|
||||
}
|
||||
|
||||
/// Renderiza a lista. Retorna eventos de alteração de offset.
|
||||
/// Renderiza a lista. Retorna eventos de alteração de offset e drift.
|
||||
///
|
||||
/// `mkvmerge_available`: quando `true`, exibe a coluna de velocidade original (%).
|
||||
pub fn ui(
|
||||
&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
tracks: &[MediaTrackInfo],
|
||||
mkvmerge_available: bool,
|
||||
) -> Vec<ExistingTrackEvent> {
|
||||
let mut events = Vec::new();
|
||||
|
||||
@@ -48,18 +59,24 @@ impl ExistingTrackList {
|
||||
return;
|
||||
}
|
||||
|
||||
let num_cols = if mkvmerge_available { 7 } else { 6 };
|
||||
egui::Grid::new("existing_tracks_grid")
|
||||
.num_columns(5)
|
||||
.num_columns(num_cols)
|
||||
.striped(true)
|
||||
.show(ui, |ui| {
|
||||
ui.strong("#");
|
||||
ui.strong("Tipo");
|
||||
ui.strong("Codec");
|
||||
ui.strong("Duração");
|
||||
ui.strong("Atraso");
|
||||
if mkvmerge_available {
|
||||
ui.strong("Velocidade (%)");
|
||||
}
|
||||
ui.strong("");
|
||||
ui.end_row();
|
||||
|
||||
for track in tracks {
|
||||
let editable = matches!(track.kind, TrackKind::Audio | TrackKind::Subtitle);
|
||||
let kind_label = match track.kind {
|
||||
TrackKind::Video => "Vídeo",
|
||||
TrackKind::Audio => "Áudio",
|
||||
@@ -76,6 +93,12 @@ impl ExistingTrackList {
|
||||
ui.label(format!("{}{}", kind_label, lang));
|
||||
ui.label(&track.codec);
|
||||
|
||||
// Duração
|
||||
match track.duration_ms {
|
||||
Some(ms) => ui.label(format_duration(ms)),
|
||||
None => ui.label("-"),
|
||||
};
|
||||
|
||||
// Offset field
|
||||
let field = self
|
||||
.offset_fields
|
||||
@@ -89,6 +112,33 @@ impl ExistingTrackList {
|
||||
}
|
||||
}
|
||||
|
||||
// Drift field (somente para áudio e legenda quando mkvmerge disponível)
|
||||
if mkvmerge_available {
|
||||
if editable {
|
||||
let drift_raw = self
|
||||
.drift_fields
|
||||
.entry(track.id.val())
|
||||
.or_insert_with(|| format!("{:.5}", track.drift_scale * 100.0));
|
||||
let resp = ui.add(
|
||||
egui::TextEdit::singleline(drift_raw).desired_width(80.0),
|
||||
);
|
||||
if resp.changed() {
|
||||
if let Ok(pct) = drift_raw.trim().parse::<f64>() {
|
||||
if pct >= 1.0 && pct <= 999.99 {
|
||||
let scale = pct / 100.0;
|
||||
if (scale - track.drift_scale).abs() > 1e-9 {
|
||||
events.push(ExistingTrackEvent::DriftChanged(
|
||||
track.id, scale,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui.label(""); // célula vazia para vídeo/dados
|
||||
}
|
||||
}
|
||||
|
||||
// Botão de exportar (somente Áudio e Legenda)
|
||||
match track.kind {
|
||||
TrackKind::Audio | TrackKind::Subtitle => {
|
||||
@@ -109,3 +159,17 @@ impl ExistingTrackList {
|
||||
events
|
||||
}
|
||||
}
|
||||
|
||||
/// Formata duração em ms para exibição: "3:45" ou "1:23:45".
|
||||
fn format_duration(ms: u64) -> String {
|
||||
let total_secs = ms / 1000;
|
||||
let h = total_secs / 3600;
|
||||
let m = (total_secs % 3600) / 60;
|
||||
let s = total_secs % 60;
|
||||
if h > 0 {
|
||||
format!("{h}:{m:02}:{s:02}")
|
||||
} else {
|
||||
format!("{m}:{s:02}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user