Files
simple-multimidia-track-aud…/PROGRESS.md
T

224 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Progresso de Implementação
**Data:** 28/02/2026
**Status:** Fases 16 concluídas + exportação de faixa + exibição/remoção de faixas adicionadas + layout responsivo + progresso em tempo real — compilando, testes passando, aplicação executável
**Referência:** DEVELOPMENT_PLAN.md v1.0
---
## Resumo Executivo
Todas as 6 fases do plano de desenvolvimento foram implementadas. O projeto compila sem erros,
31 testes unitários passam e a aplicação pode ser executada com `cargo run`.
Após a conclusão das fases, foram implementadas funcionalidades adicionais:
- Exportação de faixa de áudio ou legenda diretamente da lista de faixas existentes
- Exibição das faixas externas adicionadas com opção de remoção individual
- Layout responsivo com painéis fixos (cabeçalho, rodapé, barra lateral) e área central com scroll
- Progresso em tempo real via `run_ffmpeg_async` (Opção A: `std::sync::mpsc` em toda a cadeia)
---
## Status por Fase
| Fase | Descrição | Status |
|------|----------------------------------|-------------|
| 1 | Setup do projeto | ✅ Concluída |
| 2 | Domain (núcleo puro) | ✅ Concluída |
| 3 | Application (use cases + ports) | ✅ Concluída |
| 4 | Adapters (FFmpeg + filesystem) | ✅ Concluída |
| 5 | Infrastructure (processo async) | ✅ Concluída |
| 6 | UI (eframe/egui) | ✅ Concluída |
---
## Estrutura de Arquivos Criados
```
src/
├── main.rs — entrada, configuração da janela eframe
├── domain/
│ ├── mod.rs
│ ├── value_objects/
│ │ ├── mod.rs
│ │ ├── file_path.rs — FilePath (newtype sobre PathBuf)
│ │ ├── track_id.rs — TrackId (newtype opaco sobre u32)
│ │ ├── sync_offset.rs — SyncOffset (i64 ms, nunca f64)
│ │ └── track_language.rs — TrackLanguage (ISO 639-2, validado)
│ └── entities/
│ ├── mod.rs
│ ├── project.rs — Project (entidade raiz / fonte de verdade)
│ ├── video_file.rs — VideoFile
│ ├── audio_track.rs — AudioTrack
│ ├── subtitle_track.rs — SubtitleTrack
│ ├── media_track_info.rs — MediaTrackInfo + TrackKind
│ ├── mkv_output.rs — MkvOutput
│ └── track.rs — Track (enum: Audio | Subtitle)
├── application/
│ ├── mod.rs
│ ├── ports/
│ │ └── mod.rs — MediaInfoPort, MediaProcessorPort, FileSystemPort
│ └── use_cases/
│ ├── mod.rs
│ ├── load_media_info.rs — popula Project::existing_tracks via ffprobe
│ ├── add_audio_track.rs — adiciona AudioTrack ao Project
│ ├── add_subtitle.rs — adiciona SubtitleTrack ao Project
│ ├── adjust_sync.rs — altera SyncOffset de faixa externa
│ ├── edit_existing_track_sync.rs — altera SyncOffset de faixa existente
│ ├── export_track.rs — exporta faixa de áudio ou legenda para arquivo
│ ├── remove_track.rs — remove faixa externa do projeto pelo TrackId
│ ├── set_track_language.rs — altera idioma de faixa externa
│ └── generate_output.rs — constrói comando e delega ao MediaProcessorPort
├── adapters/
│ ├── mod.rs
│ ├── ffmpeg/
│ │ ├── mod.rs
│ │ ├── command_builder.rs — FfmpegCommandBuilder: Project → Vec<String>; build_export()
│ │ ├── ffprobe_gateway.rs — FfprobeGateway impl MediaInfoPort
│ │ └── ffmpeg_gateway.rs — FfmpegGateway impl MediaProcessorPort
│ └── filesystem/
│ ├── mod.rs — RealFileSystem impl FileSystemPort
│ └── file_picker.rs — FilePickerAdapter; save_audio(codec), save_subtitle(codec)
├── infrastructure/
│ ├── mod.rs
│ └── process/
│ └── mod.rs — run_ffmpeg_async (tokio), validate_dependencies()
└── ui/
├── mod.rs
├── app.rs — eframe::App; Project como única fonte de verdade
└── components/
├── mod.rs
├── video_selector.rs — seleção do vídeo base
├── output_selector.rs — seleção do arquivo de saída (.mkv)
├── existing_track_list.rs — lista faixas detectadas + edição de offset│ ├── added_track_list.rs — lista faixas externas adicionadas + botão remover ├── add_audio_track_form.rs — formulário: áudio externo
├── add_subtitle_form.rs — formulário: legenda externa
├── sync_offset_field.rs — campo de atraso em segundos
├── language_field.rs — campo de idioma ISO 639-2
└── execution_panel.rs — botão gerar, progresso, erros
```
---
## Testes Unitários (33/33 passando)
### Domain — Value Objects
| Teste | Resultado |
|-------|-----------|
| `sync_offset::from_seconds_str_positivo``"1.2"``SyncOffset(1200)` | ✅ |
| `sync_offset::from_seconds_str_negativo``"-0.5"``SyncOffset(-500)` | ✅ |
| `sync_offset::from_seconds_str_com_sufixo_s` | ✅ |
| `sync_offset::from_seconds_str_invalido` | ✅ |
| `sync_offset::from_seconds_str_zero` | ✅ |
| `track_language::idioma_valido``"por"`, `"eng"` | ✅ |
| `track_language::idioma_invalido_curto``"pt"` | ✅ |
| `track_language::idioma_invalido_maiusculo``"POR"` | ✅ |
| `track_language::idioma_invalido_longo``"port"` | ✅ |
### Domain — Entities
| Teste | Resultado |
|-------|-----------|
| `project::projeto_valido` | ✅ |
| `project::projeto_invalido_mesmo_caminho` — output == source retorna erro | ✅ |
| `project::track_id_opaco` — TrackId não expõe indexação interna | ✅ |
### Application — Use Cases (todos com mocks, sem I/O real)
| Teste | Resultado |
|-------|-----------|
| `load_media_info::carrega_faixas_no_projeto` | ✅ |
| `add_audio_track::adiciona_audio_no_projeto` | ✅ |
| `add_subtitle::adiciona_legenda_no_projeto` | ✅ |
| `adjust_sync::ajusta_offset_existente` | ✅ |
| `adjust_sync::erro_se_id_inexistente` | ✅ |
| `edit_existing_track_sync::edita_offset_de_faixa_existente` | ✅ |
| `edit_existing_track_sync::erro_se_faixa_existente_nao_encontrada` | ✅ |
| `set_track_language::altera_idioma_da_faixa` | ✅ |
| `generate_output::sempre_inclui_c_copy` | ✅ |
### Adapters — FfmpegCommandBuilder
| Teste | Resultado |
|-------|-----------|
| `sempre_contem_c_copy` — invariante RNF-01 | ✅ |
| `output_e_o_ultimo_argumento` | ✅ |
| `itsoffset_formato_correto``1200ms``"1.200"` | ✅ |
| `mapa_faixa_externa_de_audio``map 1:a` | ✅ |
| `metadata_idioma_audio``-metadata:s:a:0 language=por` | ✅ |
### Application — ExportTrack
| Teste | Resultado |
|-------|-----------|
| `export_track::exporta_audio_com_c_a_copy``-c:a copy` presente, sem `-c copy` genérico | ✅ |
| `export_track::exporta_audio_mapeia_stream_correto``0:stream_index` correto | ✅ |
| `export_track::exporta_legenda_sem_c_copy` — nenhuma forma de `-c` para legendas | ✅ |
| `export_track::exporta_legenda_output_e_ultimo_argumento` | ✅ |
| `export_track::rejeita_faixa_de_video` — retorna erro para `TrackKind::Video` | ✅ |
### Application — RemoveTrack
| Teste | Resultado |
|-------|-----------|
| `remove_track::remove_faixa_existente` — faixa removida do Vec | ✅ |
| `remove_track::erro_se_id_inexistente` — retorna erro se id não existe | ✅ |
---
## Dependências (Cargo.toml)
```toml
eframe = "0.27"
anyhow = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["process", "rt-multi-thread", "macros", "io-util", "sync"] }
rfd = "0.14"
```
---
## Invariantes Garantidas
- **`-c copy` sempre presente** — `FfmpegCommandBuilder::build()` emite incondicionalmente (RNF-01)
- **`SyncOffset` nunca usa `f64`** — armazenado como `i64` ms; conversão para string FFmpeg feita exclusivamente no `FfmpegCommandBuilder`
- **`TrackId` é opaco** — não expõe índice interno; mapeamento para `-map N:tipo` feito apenas no adapter
- **`Project` rejeita `output == source`** — validado no construtor
- **Termos FFmpeg nunca aparecem na UI** — a interface usa linguagem do usuário final
- **Exceção documentada ao RNF-01** — `build_export()` omite `-c copy` exclusivamente para legendas (conversão de container de texto, sem processamento de mídia); comentário inline explica a exceção
---
## O Que Falta (Backlog de Refinamento)
### Funcional
- [x] Exportar faixa de áudio ou legenda existente para arquivo separado
- [x] Exibição de faixas externas adicionadas com opção de remover
- [x] Progresso em tempo real via `run_ffmpeg_async``run_ffmpeg_async` usa `std::sync::mpsc::Sender<String>`; `start_generation` cria tokio Runtime + thread encaminhadora; cada linha de stderr do FFmpeg aparece no log antes do término
- [ ] Exibição do comando gerado em modo debug
### Qualidade
- [ ] Testes de integração com FFmpeg real (requer ffmpeg instalado no CI)
- [ ] Testes de snapshot para o `FfmpegCommandBuilder` com projetos mais complexos
- [ ] Corrigir warnings de `unused` e `dead_code` remanescentes
### UI
- [x] Layout responsivo mais refinado
- [ ] Persistência do estado do projeto em disco (serde já disponível)
- [ ] Mensagens de erro mais amigáveis na UI
---
## Como Executar
```bash
# Verificar compilação
cargo check
# Rodar testes
cargo test
# Executar a aplicação (requer ffmpeg e ffprobe no PATH)
cargo run
```