Atualiza PRD para versão 1.4, adiciona novos requisitos funcionais (RF-12 a RF-15) e remove o arquivo PROGRESS.md. Implementa painel de debug para exibir o comando gerado pelo FFmpeg/mkvmerge na ExecutionPanel, permitindo cópia e inspeção do comando. Melhora a interface e a persistência de estado do projeto.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# PRD — Simple Multimedia Track Audio Editor
|
||||
|
||||
**Versão:** 1.2
|
||||
**Data:** 28/02/2026
|
||||
**Status:** Em desenvolvimento
|
||||
**Revisão:** v1.2 — Adicionado RF-09 (Modo Lote); batch promovido de backlog para requisito planejado
|
||||
**Versão:** 1.4
|
||||
**Data:** 01/03/2026
|
||||
**Status:** v1.0 funcional — Fases 1–9 concluídas; 33 testes passando; aplicação executável
|
||||
**Revisão:** v1.4 — Adicionado RF-15 (Excluir faixa existente do output)
|
||||
|
||||
---
|
||||
|
||||
@@ -42,9 +42,8 @@ As ferramentas existentes são complexas (ex: interface direta do FFmpeg via CLI
|
||||
- Reencoding / transcodificação de vídeo ou áudio
|
||||
- Preview de vídeo embutido
|
||||
- Detecção automática de sincronização
|
||||
- Remoção de faixas existentes do arquivo original
|
||||
- Seleção de faixa padrão no container
|
||||
- Edição de corte ou splice de vídeo
|
||||
- Salvamento automático em disco (sem intervenção do usuário)
|
||||
|
||||
---
|
||||
|
||||
@@ -127,8 +126,46 @@ As ferramentas existentes são complexas (ex: interface direta do FFmpeg via CLI
|
||||
- A seleção de arquivos usa **diálogos nativos** (igual ao modo Projeto Único) — nenhum path é digitado manualmente
|
||||
- O processamento é **sequencial** — um projeto por vez, sem paralelismo, para evitar sobrecarga de I/O
|
||||
- Durante o processamento, o carrinho é bloqueado: não é possível adicionar nem remover itens
|
||||
- Cada item exibe seu estado individual: `Aguardando | Processando | Concluído | Erro`
|
||||
- Cada item exibe seu estado individual: `⏳ Aguardando | ⟳ Processando | ✓ Concluído | ⊸ Cancelado | ✗ Erro`
|
||||
- É possível cancelar o item em execução; os demais permanecem no carrinho com estado `Aguardando`
|
||||
- O dispatch FFmpeg/mkvmerge por item é automático: se qualquer faixa do item tiver `drift_scale ≠ 1.0`, o motor mkvmerge é usado
|
||||
|
||||
### RF-12 — Correção de drift progressivo de sincronização
|
||||
|
||||
- O usuário pode especificar um fator de velocidade original da faixa em porcentagem (ex: `99.983%`)
|
||||
- Internamente representado como `drift_scale: f64` em cada faixa (`AudioTrack`, `SubtitleTrack`, `MediaTrackInfo`)
|
||||
- Implementado via `mkvmerge --sync TID:DELAY,NUM/DEN` — aplica escala de timestamps no nível do container, sem reencoding
|
||||
- **Dispatch automático:** se qualquer faixa tiver `scale ≠ 1.0`, o projeto inteiro usa o pipeline `mkvmerge` em vez do FFmpeg
|
||||
- Campo exibido na interface como **"Velocidade original (%)"** — jamais expõe termos técnicos como `scale`, `drift` ou `mkvmerge`
|
||||
- O campo é desabilitado com tooltip explicativo quando `mkvmerge` não está disponível no `PATH`
|
||||
- Funciona tanto no modo Projeto Único quanto no modo Lote
|
||||
- Preserva a invariante RNF-01: nenhum byte de mídia é reprocessado
|
||||
|
||||
### RF-13 — Exportar faixa existente
|
||||
|
||||
- A partir da lista de faixas detectadas pelo `ffprobe`, o usuário pode exportar qualquer faixa de áudio ou legenda para um arquivo separado
|
||||
- O formato do arquivo de saída é inferido a partir do codec da faixa (ex: AAC → `.aac`, SRT → `.srt`)
|
||||
- Implementado via `FfmpegCommandBuilder::build_export()` com `-c:a copy` para áudio; legendas omitem `-c` (conversão de container de texto, sem processamento de mídia)
|
||||
- O diálogo de salvamento usa filtro por extensão correspondente ao codec detectado
|
||||
|
||||
### RF-14 — Persistência de sessão
|
||||
|
||||
- O usuário pode salvar o estado completo do projeto em disco com o botão **"💾 Salvar sessão"**
|
||||
- O estado é restaurado com o botão **"📂 Carregar sessão"**
|
||||
- Formato: JSON via `serde_json`; local: `~/.config/simple-mkv-editor/session.json` (Linux) / `AppData` (Windows)
|
||||
- Feedback visual no header: indicador verde (sucesso) ou vermelho (erro) com botão ✕ para dispensar
|
||||
- Erros de desserialização (arquivo corrompido ou versão incompatível) são tratados com mensagem amigável
|
||||
- Salvamento automático não é realizado — apenas sob demanda do usuário
|
||||
|
||||
### RF-15 — Excluir faixa existente do arquivo de saída
|
||||
|
||||
- O usuário pode marcar qualquer faixa de áudio ou legenda **já presente no arquivo de entrada** para ser omitida do arquivo de saída
|
||||
- A operação é reversível: a faixa pode ser restaurada antes da geração do MKV
|
||||
- Faixas marcadas como excluídas são exibidas com texto tachado e cor esmaecida na lista; seus campos de offset e drift ficam desabilitados
|
||||
- Faixas de vídeo e dados **não** podem ser excluídas — somente áudio e legenda
|
||||
- Implementado via campo `excluded: bool` em `MediaTrackInfo`; método `toggle_existing_track_excluded()` em `Project`
|
||||
- Ambos os pipelines (FFmpeg e mkvmerge) respeitam o flag: faixas excluídas são filtradas antes da geração dos argumentos de `-map` / `--audio-tracks` / `--subtitle-tracks`
|
||||
- O estado `excluded` é persistido junto com a sessão (RF-14)
|
||||
|
||||
---
|
||||
|
||||
@@ -159,9 +196,11 @@ A operação de mux deve ser concluída em tempo proporcional ao tamanho do arqu
|
||||
|
||||
O arquivo de saída deve ser compatível com players modernos que suportam MKV (ex: VLC, mpv, Jellyfin).
|
||||
|
||||
### RNF-04 — Dependência externa
|
||||
### RNF-04 — Dependências externas
|
||||
|
||||
FFmpeg deve estar instalado no sistema. A aplicação não o embute por padrão, porém pode ser distribuída junto.
|
||||
FFmpeg e ffprobe devem estar instalados no sistema. A aplicação verifica a presença de ambos na inicialização e exibe erro global se ausentes.
|
||||
|
||||
mkvmerge (MKVToolNix) é uma dependência opcional. Quando ausente, a funcionalidade de correção de drift (RF-12) fica desabilitada visualmente; o restante do produto continua funcional. A verificação é **soft** — não bloqueia a inicialização.
|
||||
|
||||
### RNF-05 — Interface responsiva
|
||||
|
||||
@@ -183,15 +222,17 @@ Arquivo de saída (.mkv)
|
||||
|
||||
### Stack
|
||||
|
||||
| Camada | Tecnologia | Motivo |
|
||||
| ------------ | ------------------------------ | ------------------------------------ |
|
||||
| Interface | `egui` / `eframe` | Nativo, simples, multiplataforma |
|
||||
| Backend | Rust (`std::process::Command`) | Estável, sem dependências externas |
|
||||
| Erros | `anyhow` | Propagação de erros simplificada |
|
||||
| Configuração | `serde` | Serialização de perfis e histórico |
|
||||
| Async | `tokio` (opcional) | Execução não bloqueante da interface |
|
||||
| Mídia | FFmpeg (externo) | Maduro, estável, amplamente testado |
|
||||
| Container | MKV | Melhor suporte a múltiplas faixas |
|
||||
| Camada | Tecnologia | Motivo |
|
||||
| ------------- | ------------------------------- | ------------------------------------------- |
|
||||
| Interface | `egui` / `eframe` | Nativo, simples, multiplataforma |
|
||||
| Backend | Rust (`std::process::Command`) | Estável, sem dependências externas |
|
||||
| Erros | `anyhow` | Propagação de erros simplificada |
|
||||
| Configuração | `serde` + `serde_json` | Serialização de sessão em JSON |
|
||||
| Async | `tokio` | Execução não bloqueante da interface |
|
||||
| Diálogos | `rfd` | Diálogos nativos de arquivo multiplataforma |
|
||||
| Mídia (mux) | FFmpeg (externo) | Mux sem reencoding; caminho padrão |
|
||||
| Mídia (drift) | mkvmerge / MKVToolNix (externo) | Correção de drift sem reencoding (opcional) |
|
||||
| Container | MKV | Melhor suporte a múltiplas faixas |
|
||||
|
||||
### Dependências Cargo
|
||||
|
||||
@@ -199,7 +240,9 @@ Arquivo de saída (.mkv)
|
||||
eframe = "0.27"
|
||||
anyhow = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
tokio = { version = "1", features = ["process"] } # opcional
|
||||
serde_json = "1"
|
||||
tokio = { version = "1", features = ["process", "rt-multi-thread", "macros", "io-util", "sync"] }
|
||||
rfd = "0.14"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -236,31 +279,35 @@ Contém as entidades e objetos de valor do negócio. Não depende de nada extern
|
||||
| Tipo | Exemplos |
|
||||
| ------------- | ------------------------------------------------------------------------------------ |
|
||||
| Entities | `Project`, `VideoFile`, `AudioTrack`, `SubtitleTrack`, `MkvOutput`, `MediaTrackInfo` |
|
||||
| Value Objects | `TrackId`, `SyncOffset`, `TrackLanguage`, `FilePath` |
|
||||
| Value Objects | `TrackId`, `SyncOffset`, `SyncTransform`, `TrackLanguage`, `FilePath` |
|
||||
|
||||
> **`Project`** é a entidade central do domínio. Representa a sessão de edição completa do usuário e deve ser a única fonte de verdade do estado em memória.
|
||||
>
|
||||
> ```
|
||||
> Project
|
||||
> ├── source: VideoFile
|
||||
> ├── tracks: Vec<Track> // faixas externas adicionadas
|
||||
> ├── tracks: Vec<Track> // faixas externas adicionadas
|
||||
> ├── existing_tracks: Vec<MediaTrackInfo> // faixas lidas do arquivo via ffprobe
|
||||
> └── output: MkvOutput
|
||||
> ```
|
||||
>
|
||||
> **`TrackId`** abstrai os índices de stream do FFmpeg. Internamente é um identificador opaco (`u32` ou `String`). Nenhuma lógica de negócio deve depender de índices FFmpeg diretamente — isso é responsabilidade do adapter.
|
||||
> Método relevante: `needs_mkvmerge() -> bool` — retorna `true` se qualquer faixa tiver `drift_scale ≠ 1.0`, sinalizando que o projeto deve usar o pipeline `mkvmerge` em vez do FFmpeg.
|
||||
>
|
||||
> **`TrackId`** abstrai os índices de stream do FFmpeg. Internamente é um identificador opaco (`u32`). Nenhuma lógica de negócio deve depender de índices FFmpeg diretamente — isso é responsabilidade do adapter.
|
||||
>
|
||||
> **`SyncOffset`** armazena o offset de sincronização em **milissegundos como inteiro** (`i64`). Nunca como `f64`. O uso de ponto flutuante acumula erro de precisão; o adapter é responsável por converter para o formato exigido pelo FFmpeg (`-itsoffset`).
|
||||
>
|
||||
> **`SyncTransform`** é o value object que combina deslocamento e escala temporal: `{ offset_ms: i64, scale: f64 }`. Usado internamente para representar a transformação completa de uma faixa no pipeline mkvmerge (RF-12). `scale = 1.0` representa identidade; `scale ≠ 1.0` ativa o correto motor de dispatch.
|
||||
|
||||
#### Application (casos de uso)
|
||||
|
||||
Contém as regras de negócio da aplicação. Orquestra as entidades do domínio.
|
||||
Define traits (ports) que as camadas externas devem implementar.
|
||||
|
||||
| Tipo | Exemplos |
|
||||
| --------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Use Cases | `LoadMediaInfo`, `AddAudioTrack`, `AddSubtitle`, `AdjustSync`, `EditExistingTrackSync`, `SetTrackLanguage`, `GenerateOutput` |
|
||||
| Ports | `MediaProcessorPort`, `MediaInfoPort`, `FileSystemPort` |
|
||||
| Tipo | Exemplos |
|
||||
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Use Cases | `LoadMediaInfo`, `AddAudioTrack`, `AddSubtitle`, `AdjustSync`, `AdjustDrift`, `AdjustExistingTrackDrift`, `EditExistingTrackSync`, `ExportTrack`, `RemoveTrack`, `SetTrackLanguage`, `GenerateOutput` |
|
||||
| Ports | `MediaProcessorPort`, `MediaInfoPort`, `FileSystemPort`, `ContainerMuxPort` |
|
||||
|
||||
> **`MediaInfoPort`** é o port responsável por inspecionar arquivos de mídia existentes. Deve ser definido na camada Application e implementado na camada Adapters via `FfprobeGateway`.
|
||||
>
|
||||
@@ -278,15 +325,20 @@ Define traits (ports) que as camadas externas devem implementar.
|
||||
|
||||
Implementam os ports definidos na camada de Application. Traduzem dados entre o domínio e o mundo externo.
|
||||
|
||||
| Tipo | Exemplos |
|
||||
| ------------ | --------------------------------------------------------- |
|
||||
| Gateway | `FfmpegCommandBuilder`, `FfmpegGateway`, `FfprobeGateway` |
|
||||
| Presenter | `ErrorPresenter` (formata stderr do FFmpeg) |
|
||||
| File Adapter | `FilePickerAdapter` |
|
||||
| Tipo | Exemplos |
|
||||
| ------------ | ------------------------------------------------------------------------- |
|
||||
| Gateway | `FfmpegCommandBuilder`, `FfmpegGateway`, `FfprobeGateway` |
|
||||
| Gateway | `MkvmergeCommandBuilder`, `MkvmergeGateway` (motor de drift — RF-12) |
|
||||
| Presenter | `ErrorPresenter` (formata stderr do FFmpeg / stdout do mkvmerge) |
|
||||
| File Adapter | `FilePickerAdapter` (inclui `save_audio(codec)` e `save_subtitle(codec)`) |
|
||||
|
||||
> **`FfprobeGateway`** implementa `MediaInfoPort`. Executa `ffprobe -v quiet -print_format json -show_streams` e mapeia a saída para `Vec<MediaTrackInfo>`. Não conhece o domínio além das structs que está populando.
|
||||
>
|
||||
> **`FfmpegCommandBuilder`** é responsável por converter `TrackId` para índices `-map 0:a:N` do FFmpeg e `SyncOffset` (ms inteiro) para o formato `-itsoffset 1.200` aceito pelo binário. **Toda conversão de tipos internos para argumentos FFmpeg fica aqui e somente aqui.**
|
||||
>
|
||||
> Além do `-c copy`, sempre emite `-max_interleave_delta 0` (evita descarte silencioso de pacotes em streams com grande delta de timestamps) e `-avoid_negative_ts make_zero` (normaliza timestamps negativos comuns em M4A/AAC de serviços de streaming).
|
||||
>
|
||||
> **`MkvmergeCommandBuilder`** converte `Project` em argumentos para o `mkvmerge`. Usa `--sync TID:DELAY,NUM/DEN` para aplicar deslocamento e escala de timestamps sem reencoding. A escala `f64` é convertida para fração racionl `(NUM, DEN)` com precisão de 6 casas decimais.
|
||||
|
||||
#### Infrastructure / UI
|
||||
|
||||
@@ -306,18 +358,23 @@ Camada mais externa. Contém o framework de UI e a execução real de processos.
|
||||
src/
|
||||
├── domain/
|
||||
│ ├── entities/ # Project, VideoFile, AudioTrack, SubtitleTrack, MkvOutput, MediaTrackInfo
|
||||
│ └── value_objects/ # TrackId, SyncOffset (ms/i64), TrackLanguage, FilePath
|
||||
│ └── value_objects/ # TrackId, SyncOffset (ms/i64), SyncTransform (offset+scale), TrackLanguage, FilePath
|
||||
├── application/
|
||||
│ ├── use_cases/ # LoadMediaInfo, AddAudioTrack, AddSubtitle, AdjustSync, GenerateOutput
|
||||
│ └── ports/ # Traits: MediaProcessorPort, MediaInfoPort, FileSystemPort
|
||||
│ ├── use_cases/ # LoadMediaInfo, AddAudioTrack, AddSubtitle, AdjustSync, AdjustDrift,
|
||||
│ │ # AdjustExistingTrackDrift, EditExistingTrackSync, ExportTrack,
|
||||
│ │ # RemoveTrack, SetTrackLanguage, GenerateOutput
|
||||
│ └── ports/ # Traits: MediaProcessorPort, MediaInfoPort, FileSystemPort, ContainerMuxPort
|
||||
├── adapters/
|
||||
│ ├── ffmpeg/ # FfmpegCommandBuilder, FfmpegGateway, FfprobeGateway
|
||||
│ ├── mkvmerge/ # MkvmergeCommandBuilder, MkvmergeGateway (RF-12 — drift)
|
||||
│ └── filesystem/ # FilePickerAdapter
|
||||
├── infrastructure/
|
||||
│ └── process/ # Execução real do FFmpeg / ffprobe
|
||||
│ ├── process/ # run_ffmpeg_async, run_mkvmerge_async, validate_dependencies, mkvmerge_available
|
||||
│ └── persistence/ # save_session, load_session (RF-14)
|
||||
├── ui/
|
||||
│ ├── app.rs # eframe App (ponto de entrada da interface)
|
||||
│ └── components/ # Componentes egui reutilizáveis
|
||||
│ ├── app.rs # eframe App; ActiveTab (Single/Batch); dispatch FFmpeg/mkvmerge
|
||||
│ └── components/ # VideoSelector, ExistingTrackList, AddAudioTrackForm, AddSubtitleForm,
|
||||
│ # SyncOffsetField (+ campo drift), OutputSelector, ExecutionPanel, BatchPanel
|
||||
└── main.rs
|
||||
```
|
||||
|
||||
@@ -325,15 +382,18 @@ src/
|
||||
|
||||
## 10. Débitos Técnicos
|
||||
|
||||
| ID | Descrição | Impacto | Mitigação |
|
||||
| ----- | ----------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------ |
|
||||
| DT-01 | FFmpeg não embutido | Usuário precisa instalar | Distribuir junto com a aplicação |
|
||||
| DT-02 | Dependência de processo externo | Menor controle interno | Encapsular via módulo de serviço |
|
||||
| DT-03 | Parsing de erros do FFmpeg (stderr) | Necessário tratamento manual | Parsear saída e exibir mensagem amigável |
|
||||
| DT-04 | Compatibilidade de codecs | Alguns codecs podem não ser aceitos | Usar MKV como container padrão |
|
||||
| DT-05 | Performance de processo externo | Pequeno overhead | Aceitável — mux é rápido |
|
||||
| DT-06 | ffprobe como dependência adicional | Parsing de JSON da saída do ffprobe | Validar presença de ffprobe na inicialização; exibir mensagem clara se ausente |
|
||||
| DT-07 | Compatibilidade com Jellyfin mobile | Faixas não selecionadas automaticamente | Investigando; as RFC RF-10 e RF-11 mitigam parcialmente — `title` e `disposition` são emitidos corretamente, porém o Jellyfin mobile pode ter comportamento diferente do web; requer testes com arquivo gerado |
|
||||
| ID | Descrição | Impacto | Mitigação / Status |
|
||||
| ----- | ------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| DT-01 | FFmpeg não embutido | Usuário precisa instalar | Distribuir junto com a aplicação |
|
||||
| DT-02 | Dependência de processo externo | Menor controle interno | Encapsulado via `infrastructure/process`; `validate_dependencies()` na inicialização |
|
||||
| DT-03 | Parsing de erros do FFmpeg (stderr) | Mensagens brutas exibidas ao usuário | Stderr capturado linha a linha via `run_ffmpeg_async`; exibido no `ExecutionPanel`; parse amigável futuro |
|
||||
| DT-04 | Compatibilidade de codecs | Alguns codecs podem não ser aceitos | MKV como container padrão; `-c copy` garante que não há transcodificação |
|
||||
| DT-05 | Performance de processo externo | Pequeno overhead | Aceitável — mux é rápido; sem impacto perceptível |
|
||||
| DT-06 | ffprobe como dependência adicional | Parsing de JSON da saída do ffprobe | ✅ Resolvido — `validate_dependencies()` verifica ffprobe na inicialização; erro global exibido se ausente |
|
||||
| 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) |
|
||||
|
||||
---
|
||||
|
||||
@@ -341,9 +401,11 @@ src/
|
||||
|
||||
- Preview de vídeo embutido na interface
|
||||
- Detecção automática de sincronização entre faixas
|
||||
- Seleção de faixa padrão no container MKV
|
||||
- Remoção de faixas existentes do arquivo original
|
||||
- Suporte a perfis de configuração salvos
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
@@ -356,11 +418,19 @@ src/
|
||||
- [x] É possível definir offset de sincronização por faixa ao adicionar uma nova faixa
|
||||
- [x] É possível editar o offset de sincronização de uma faixa de áudio ou legenda já existente no arquivo
|
||||
- [x] É possível atribuir idioma a cada faixa
|
||||
- [x] É possível definir um título/nome para cada faixa externa (RF-10)
|
||||
- [x] É possível marcar uma faixa externa como faixa padrão do container (RF-11)
|
||||
- [x] É possível exportar uma faixa existente para arquivo separado (RF-13)
|
||||
- [x] É possível salvar e restaurar a sessão em disco (RF-14)
|
||||
- [x] É possível excluir faixas de áudio ou legenda existentes do arquivo de saída, com possibilidade de restauração antes da geração (RF-15)
|
||||
- [x] O modo lote permite configurar e processar múltiplos projetos sequencialmente (RF-09)
|
||||
- [x] É possível definir fator de correção de drift por faixa (RF-12; requer mkvmerge)
|
||||
- [x] O arquivo MKV é gerado corretamente ao confirmar
|
||||
- [x] Erros do FFmpeg são exibidos de forma legível
|
||||
- [x] A interface não trava durante o processamento
|
||||
- [x] Nenhum reencoding ocorre (verificável via `ffprobe`)
|
||||
- [x] O flag `-c copy` está sempre presente no comando gerado (verificável via testes unitários e modo debug)
|
||||
- [x] Erros do FFmpeg/mkvmerge são exibidos de forma legível na `ExecutionPanel`
|
||||
- [x] A interface não trava durante o processamento (execução assíncrona via `tokio`)
|
||||
- [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)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user