feat: atualiza versão do projeto para 1.3 e revisa o plano de desenvolvimento e PRD com novas funcionalidades e requisitos
This commit is contained in:
+66
-54
@@ -2,7 +2,7 @@
|
||||
|
||||
**Data:** 28/02/2026
|
||||
**Status:** Fases 1–6 concluídas + exportação de faixa + exibição/remoção de faixas adicionadas + layout responsivo + progresso em tempo real + cancelamento de geração — compilando, 33 testes passando, zero warnings do projeto, aplicação executável
|
||||
**Referência:** DEVELOPMENT_PLAN.md v1.1
|
||||
**Referência:** DEVELOPMENT_PLAN.md v1.3
|
||||
|
||||
---
|
||||
|
||||
@@ -14,6 +14,7 @@ Todas as 6 fases do plano de desenvolvimento foram implementadas. O projeto comp
|
||||
Todos os 14 warnings de `unused`/`dead_code` foram resolvidos: assignment morto em `command_builder.rs` removido, lifetime explícito em `FilePath::to_string_lossy` corrigido, e APIs arquiteturais não chamadas pela UI suprimidas com `#[allow(dead_code)]`.
|
||||
|
||||
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
|
||||
@@ -24,14 +25,14 @@ Após a conclusão das fases, foram implementadas funcionalidades adicionais:
|
||||
|
||||
## 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 |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
@@ -110,61 +111,67 @@ src/
|
||||
## 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"` | ✅ |
|
||||
|
||||
| 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 | ✅ |
|
||||
|
||||
| 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` | ✅ |
|
||||
|
||||
| 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` | ✅ |
|
||||
|
||||
| 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` | ✅ |
|
||||
|
||||
| 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 | ✅ |
|
||||
|
||||
| Teste | Resultado |
|
||||
| ---------------------------------------------------------------------- | --------- |
|
||||
| `remove_track::remove_faixa_existente` — faixa removida do Vec | ✅ |
|
||||
| `remove_track::erro_se_id_inexistente` — retorna erro se id não existe | ✅ |
|
||||
|
||||
---
|
||||
|
||||
@@ -195,12 +202,16 @@ rfd = "0.14"
|
||||
## 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
|
||||
- [x] Cancelamento de geração — botão "Cancelar" visível durante `Running`; sinal via `tokio::sync::oneshot`; `child.kill().await` no `tokio::select!`; estado `ExecutionState::Cancelled` exibido em amarelo
|
||||
- [ ] **Fase 7 — Persistência do Estado** — salvar/carregar `Project` em JSON (`~/.config/...`); botões "Salvar sessão" / "Carregar sessão" no header; novo módulo `infrastructure/persistence` — ver DEVELOPMENT_PLAN.md Fase 7
|
||||
- [ ] **Fase 8 — Modo Lote via Abas** — navegação por abas (`Projeto Único` / `Lote`); `Vec<BatchItem>` em `App`; processamento sequencial (um por vez); novo componente `BatchPanel` — ver DEVELOPMENT_PLAN.md Fase 8
|
||||
|
||||
### Qualidade
|
||||
|
||||
- [x] Corrigir 14 warnings de `unused`/`dead_code` identificados por `cargo build`:
|
||||
- `next_input_idx += 1` removido do loop externo em `command_builder.rs` (assignment nunca lido)
|
||||
- Lifetime explícito `Cow<'_, str>` adicionado em `FilePath::to_string_lossy`
|
||||
@@ -210,6 +221,7 @@ rfd = "0.14"
|
||||
- [ ] Testes de snapshot para o `FfmpegCommandBuilder` com projetos mais complexos
|
||||
|
||||
### UI
|
||||
|
||||
- [x] Layout responsivo mais refinado
|
||||
- [ ] Exibição do comando FFmpeg gerado em modo debug (ex: painel colapsável na `ExecutionPanel`)
|
||||
- [ ] Persistência do estado do projeto em disco (`serde` já disponível)
|
||||
|
||||
Reference in New Issue
Block a user