feat: implement project management and media track handling
- Added domain entities for audio, subtitle, and video tracks. - Created a Project entity to manage media editing sessions. - Implemented value objects for file paths, sync offsets, track IDs, and languages. - Developed infrastructure for asynchronous FFmpeg process execution. - Built a user interface for selecting video files, adding audio and subtitle tracks, and managing output settings. - Integrated error handling and logging for media processing tasks.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
use eframe::egui;
|
||||
use crate::domain::entities::{MediaTrackInfo, TrackKind};
|
||||
use crate::domain::value_objects::{SyncOffset, TrackId};
|
||||
use crate::ui::components::sync_offset_field::SyncOffsetField;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Evento emitido por interações com a lista de faixas existentes.
|
||||
pub enum ExistingTrackEvent {
|
||||
OffsetChanged(TrackId, SyncOffset),
|
||||
}
|
||||
|
||||
/// Lista as faixas detectadas no arquivo de vídeo e permite editar o offset de cada uma.
|
||||
pub struct ExistingTrackList {
|
||||
offset_fields: HashMap<u32, SyncOffsetField>,
|
||||
}
|
||||
|
||||
impl ExistingTrackList {
|
||||
pub fn new() -> Self {
|
||||
ExistingTrackList {
|
||||
offset_fields: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sincroniza os campos de offset com as faixas atuais do projeto.
|
||||
pub fn sync_tracks(&mut self, tracks: &[MediaTrackInfo]) {
|
||||
for track in tracks {
|
||||
self.offset_fields
|
||||
.entry(track.id.val())
|
||||
.or_insert_with(|| SyncOffsetField::new(track.offset));
|
||||
}
|
||||
}
|
||||
|
||||
/// Renderiza a lista. Retorna eventos de alteração de offset.
|
||||
pub fn ui(
|
||||
&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
tracks: &[MediaTrackInfo],
|
||||
) -> Vec<ExistingTrackEvent> {
|
||||
let mut events = Vec::new();
|
||||
|
||||
ui.group(|ui| {
|
||||
ui.heading("Faixas existentes");
|
||||
|
||||
if tracks.is_empty() {
|
||||
ui.label("Nenhuma faixa detectada.");
|
||||
return;
|
||||
}
|
||||
|
||||
egui::Grid::new("existing_tracks_grid")
|
||||
.num_columns(4)
|
||||
.striped(true)
|
||||
.show(ui, |ui| {
|
||||
ui.strong("#");
|
||||
ui.strong("Tipo");
|
||||
ui.strong("Codec");
|
||||
ui.strong("Atraso");
|
||||
ui.end_row();
|
||||
|
||||
for track in tracks {
|
||||
let kind_label = match track.kind {
|
||||
TrackKind::Video => "Vídeo",
|
||||
TrackKind::Audio => "Áudio",
|
||||
TrackKind::Subtitle => "Legenda",
|
||||
TrackKind::Data => "Dados",
|
||||
};
|
||||
let lang = track
|
||||
.language
|
||||
.as_ref()
|
||||
.map(|l| format!(" ({})", l))
|
||||
.unwrap_or_default();
|
||||
|
||||
ui.label(format!("{}", track.stream_index));
|
||||
ui.label(format!("{}{}", kind_label, lang));
|
||||
ui.label(&track.codec);
|
||||
|
||||
// Offset field
|
||||
let field = self
|
||||
.offset_fields
|
||||
.entry(track.id.val())
|
||||
.or_insert_with(|| SyncOffsetField::new(track.offset));
|
||||
|
||||
field.ui(ui, "");
|
||||
if let Some(offset) = field.parse() {
|
||||
if offset != track.offset {
|
||||
events.push(ExistingTrackEvent::OffsetChanged(track.id, offset));
|
||||
}
|
||||
}
|
||||
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
events
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user