feat: implementa painel de debug para exibir o comando gerado pelo FFmpeg/mkvmerge na ExecutionPanel, com opção de cópia
This commit is contained in:
@@ -393,7 +393,7 @@ src/
|
||||
| DT-07 | Compatibilidade com Jellyfin mobile | Faixas não selecionadas automaticamente | Investigando — RF-10 (`title`) e RF-11 (`disposition`) implementados; comportamento no Jellyfin mobile requer testes com arquivo gerado |
|
||||
| DT-08 | mkvmerge não embutido | Usuário precisa instalar MKVToolNix | Dependência opcional; feat. de drift desabilitada visualmente se ausente; verificação soft na inicialização |
|
||||
| DT-09 | Testes de integração com FFmpeg real | Regressões em comandos gerados | Pendente — requer `ffmpeg` no CI; coberto indiretamente pelos testes unitários do `FfmpegCommandBuilder` |
|
||||
| DT-10 | Comando FFmpeg não visível ao usuário | Difícil de debugar manualmente | Planejado — painel colapsável com o comando gerado na `ExecutionPanel` (modo debug) |
|
||||
| DT-10 | Comando FFmpeg não visível ao usuário | Difícil de debugar manualmente | ✅ Resolvido — painel colapsável "🛠 Comando gerado" na `ExecutionPanel` e em cada item do modo Lote; botão de cópia para clipboard |
|
||||
|
||||
---
|
||||
|
||||
@@ -402,7 +402,6 @@ src/
|
||||
- Preview de vídeo embutido na interface
|
||||
- Detecção automática de sincronização entre faixas
|
||||
- Suporte a perfis de configuração reutilizáveis (presets de idioma, offset, drift)
|
||||
- Painel colapsável com o comando FFmpeg/mkvmerge gerado (modo debug, DT-10)
|
||||
- Mensagens de erro mais amigáveis a partir do stderr do FFmpeg
|
||||
- Testes de integração com FFmpeg real no CI (DT-09)
|
||||
- Testes de snapshot para o `FfmpegCommandBuilder` e `MkvmergeCommandBuilder` com projetos complexos
|
||||
@@ -431,6 +430,7 @@ src/
|
||||
- [x] É possível cancelar a geração em andamento
|
||||
- [x] Nenhum reencoding ocorre (verificável via `ffprobe` no arquivo de saída)
|
||||
- [x] O flag `-c copy` está sempre presente no comando FFmpeg gerado (verificável via testes unitários)
|
||||
- [x] O comando FFmpeg/mkvmerge gerado é exibido em painel colapsável na ExecutionPanel (DT-10)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Implementação — DT-10: Painel de Debug do Comando Gerado
|
||||
|
||||
**Data:** 01/03/2026
|
||||
**Status:** Planejado
|
||||
**Status:** ✅ Implementado
|
||||
**Prioridade:** Baixa — melhoria de DX para usuário técnico
|
||||
**Arquivos afetados:**
|
||||
|
||||
@@ -151,12 +151,12 @@ Ao concluir a implementação:
|
||||
|
||||
## Critério de aceitação
|
||||
|
||||
- [ ] O painel "🛠 Comando gerado" aparece na `ExecutionPanel` após a primeira geração
|
||||
- [ ] O painel está colapsado por padrão
|
||||
- [ ] O conteúdo exibe o binário (`ffmpeg` ou `mkvmerge`) seguido de todos os argumentos
|
||||
- [ ] Argumentos com espaços internos são envolvidos em aspas duplas
|
||||
- [ ] O botão "📋 Copiar" coloca o texto no clipboard
|
||||
- [ ] O comando é exibido também para gerações canceladas
|
||||
- [ ] O painel **não** aparece antes da primeira geração (estado `Idle` inicial)
|
||||
- [ ] Modo Lote exibe o comando do último item processado
|
||||
- [ ] `cargo test` — 33 testes passando sem regressão
|
||||
- [x] O painel "🛠 Comando gerado" aparece na `ExecutionPanel` após a primeira geração
|
||||
- [x] O painel está colapsado por padrão
|
||||
- [x] O conteúdo exibe o binário (`ffmpeg` ou `mkvmerge`) seguido de todos os argumentos
|
||||
- [x] Argumentos com espaços internos são envolvidos em aspas duplas
|
||||
- [x] O botão "📋 Copiar" coloca o texto no clipboard
|
||||
- [x] O comando é exibido também para gerações canceladas
|
||||
- [x] O painel **não** aparece antes da primeira geração (estado `Idle` inicial)
|
||||
- [x] Modo Lote exibe o comando do último item processado
|
||||
- [x] `cargo test` — 68 testes passando sem regressão
|
||||
|
||||
@@ -245,6 +245,19 @@ impl App {
|
||||
} else {
|
||||
FfmpegCommandBuilder::build(&project)
|
||||
};
|
||||
|
||||
// Formatar comando legível para o painel de debug
|
||||
let binary = if uses_mkvmerge { "mkvmerge" } else { "ffmpeg" };
|
||||
let cmd_str = format!(
|
||||
"{} {}",
|
||||
binary,
|
||||
args.iter()
|
||||
.map(|a| if a.contains(' ') { format!("\"{}\"", a) } else { a.clone() })
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
self.execution_panel.last_command = Some(cmd_str);
|
||||
|
||||
let (tx, rx) = mpsc::channel::<BackgroundMsg>();
|
||||
self.bg_rx = Some(rx);
|
||||
self.execution_panel.state = ExecutionState::Running;
|
||||
@@ -311,6 +324,19 @@ impl App {
|
||||
} else {
|
||||
FfmpegCommandBuilder::build(&project)
|
||||
};
|
||||
|
||||
// Formatar comando legível para o painel de debug do item de lote
|
||||
let binary = if uses_mkvmerge { "mkvmerge" } else { "ffmpeg" };
|
||||
let cmd_str = format!(
|
||||
"{} {}",
|
||||
binary,
|
||||
args.iter()
|
||||
.map(|a| if a.contains(' ') { format!("\"{}\"", a) } else { a.clone() })
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
);
|
||||
self.batch_items[index].last_command = Some(cmd_str);
|
||||
|
||||
let (tx, rx) = mpsc::channel::<BackgroundMsg>();
|
||||
self.bg_rx = Some(rx);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ pub struct BatchItem {
|
||||
pub project: Project,
|
||||
pub state: ExecutionState,
|
||||
pub log_lines: Vec<String>,
|
||||
/// Comando completo enviado ao FFmpeg/mkvmerge na última geração deste item.
|
||||
pub last_command: Option<String>,
|
||||
}
|
||||
|
||||
impl BatchItem {
|
||||
@@ -25,6 +27,7 @@ impl BatchItem {
|
||||
project,
|
||||
state: ExecutionState::Idle,
|
||||
log_lines: Vec::new(),
|
||||
last_command: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,7 +257,20 @@ impl BatchPanel {
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
if let Some(cmd) = &item.last_command {
|
||||
ui.collapsing("🛠 Comando gerado", |ui| {
|
||||
let mut cmd_str = cmd.as_str();
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut cmd_str)
|
||||
.desired_rows(3)
|
||||
.desired_width(f32::INFINITY)
|
||||
.font(egui::TextStyle::Monospace),
|
||||
);
|
||||
if ui.small_button("📋 Copiar").clicked() {
|
||||
ui.output_mut(|o| o.copied_text = cmd.clone());
|
||||
}
|
||||
});
|
||||
} });
|
||||
}); // push_id
|
||||
ui.add_space(4.0);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ pub enum ExecutionState {
|
||||
pub struct ExecutionPanel {
|
||||
pub state: ExecutionState,
|
||||
pub log_lines: Vec<String>,
|
||||
/// Comando completo enviado ao FFmpeg/mkvmerge na última geração.
|
||||
/// Exemplo: "ffmpeg -i input.mkv -c copy ... output.mkv"
|
||||
pub last_command: Option<String>,
|
||||
}
|
||||
|
||||
impl ExecutionPanel {
|
||||
@@ -22,6 +25,7 @@ impl ExecutionPanel {
|
||||
ExecutionPanel {
|
||||
state: ExecutionState::Idle,
|
||||
log_lines: Vec::new(),
|
||||
last_command: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +107,22 @@ impl ExecutionPanel {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Painel de debug: comando gerado
|
||||
if let Some(cmd) = &self.last_command {
|
||||
ui.collapsing("🛠 Comando gerado", |ui| {
|
||||
let mut cmd_str = cmd.as_str();
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut cmd_str)
|
||||
.desired_rows(3)
|
||||
.desired_width(f32::INFINITY)
|
||||
.font(egui::TextStyle::Monospace),
|
||||
);
|
||||
if ui.small_button("📋 Copiar").clicked() {
|
||||
ui.output_mut(|o| o.copied_text = cmd.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
(generate_clicked, cancel_clicked)
|
||||
|
||||
Reference in New Issue
Block a user