feat: aprimorada a edição por voz com validação de frases, margens

- aprimorada a edição por voz com validação de frases, margens seguras e backup único antes da timeline.
- adicionada consulta expansível dos detalhes de cada trilha diretamente do banco

Resumo:
- 23 arquivos alterados
- 5 novos
- 18 modificados
- 0 removidos

 18 files changed, 321 insertions(+), 216 deletions(-)

Arquivos:
  - .gitignore
  - .jhonny/analises.db
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - code/engine/analisar_trilhas.py
  - code/engine/aplicar_plano_de_edicao.py
  - code/engine/editor/__init__.py
  - code/engine/editor/backup_de_sequencia.py
  - code/engine/persistencia/__init__.py
  - code/engine/testes/test_aplicar_plano_de_edicao.py
  - code/plugins/premiere-pro/skills/edit-video-by-voice/SKILL.md
  - code/plugins/premiere-pro/skills/editar-por-voz/SKILL.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/05-zoom.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/06-texto-corte-marcador.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/08-formato-de-saida.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/10-revisao-humana.md
  - code/plugins/premiere-pro/skills/transcript-to-edit-actions/SKILL.md
  - code/engine/consultar_detalhes_de_trilha.py
  - code/engine/editor/validacao_semantica.py
  - code/engine/persistencia/leitura_semantica_do_plano.py
  - code/engine/testes/test_leitura_semantica_do_plano.py
  - code/engine/testes/test_validador_semantico_de_plano.py
This commit is contained in:
João Henrique
2026-09-10 14:38:42 -04:00
parent b038d9b2e4
commit 9edde2df37
23 changed files with 873 additions and 216 deletions
@@ -49,23 +49,17 @@ automático acerta na quase totalidade dos casos.
## Escala
| Valor | Uso | Vira, na tela de revisão |
|---|---|---|
| 1,15 | sutil | ênfase **1 — Leve** |
| 1,18 – 1,3 | padrão | ênfase **2 — Média** |
| 1,5 | forte | ênfase **3 — Forte** |
| Valor | Uso |
|---|---|
| 1,15 | sutil |
| 1,18 – 1,3 | padrão |
| 1,5 | forte |
Em vídeo institucional, fique na faixa baixa. Acima de 3,0 é rejeitado.
**A escala tem um segundo efeito, e ele é maior que o zoom.** A frase que
recebe um zoom é marcada como **ênfase** na etapa 5, e frase de ênfase recebe
**legenda dinâmica**; as demais ficam com legenda comum. Ou seja: escolher onde
dar zoom é também escolher onde o texto ganha tratamento tipográfico.
Consequência prática: **não espalhe zoom "por segurança"**. Cada um promove uma
frase a destaque em duas dimensões ao mesmo tempo. Na dúvida, deixe sem — o
editor promove numa tecla, e despromover custa mais que promover.
Detalhe: `10-revisao-humana.md`.
Não espalhe zoom "por segurança". Cada ação é aplicada diretamente ao clipe e
excesso de escala vira trabalho de correção manual. Zoom não ativa legenda nem
muda o texto por efeito colateral no fluxo atual.
O zoom é **relativo ao enquadramento existente**: se o clipe já tem escala
1,77 (material gravado de lado e reenquadrado), um zoom 1,18 anima de 1,77
@@ -63,11 +63,12 @@ proibido acima) — aqui a pausa **já existe** entre o fim de um bloco mantido
e o início do próximo, e o corte está comendo justamente essa margem.
Ao escrever a borda de um `cut` que encosta em fala mantida (não em silêncio
puro), recue **~0,15–0,25s** para dentro do próprio corte, nos dois lados:
puro), recue **no mínimo 0,30s** para dentro do próprio corte, nos dois lados.
Essa margem cobre a imprecisão observada entre transcrição e frame de corte:
- o `start` do corte fica ~0,2s **depois** do fim real da última palavra
- o `start` do corte fica ≥0,30s **depois** do fim real da última palavra
mantida;
- o `end` do corte fica ~0,2s **antes** do início real da próxima palavra
- o `end` do corte fica ≥0,30s **antes** do início real da próxima palavra
mantida.
Caso real (projeto Mastopexia): um corte escrito rente (`10.77 → 95.50`,
@@ -81,6 +82,33 @@ primeira palavra e depois da última também leva `cut`, com a mesma folga —
não é "silêncio dentro da fala" (isso é `remove_media_silence`), é o mesmo
corte de tomada/bastidor que você já está decidindo.
### Segmento de transcrição não é frase
O Whisper pode terminar um segmento no meio de uma oração e continuar o
segmento seguinte em minúscula. Portanto, **nunca** transforme automaticamente
`segment.end` ou `next_segment.start` em borda de corte.
Antes de aceitar cada emenda, leia em voz contínua:
1. a última oração que ficará antes do `start`;
2. a primeira oração que ficará depois do `end`;
3. a junção formada por essas duas partes.
Bloqueie o plano se a última palavra mantida não fechar a oração, se a próxima
fala começar como continuação gramatical, ou se qualquer borda cair dentro de
uma palavra. Caso real bloqueante: remover até `934.65s` faria a fala sobreviver
em `"conseguir achar um profissional..."`, continuação da frase anterior; cortar
em `962.60s`, exatamente após `"segurança."`, pode produzir
`"trazer uma seguran..."` por falta de cauda acústica.
Checklist obrigatório antes de salvar qualquer plano com `cut`:
- zero palavras interceptadas;
- zero trechos mantidos começando no meio de frase;
- zero frases mantidas sem fechamento antes do corte;
- ao menos 0,30s de margem junto às palavras mantidas;
- leitura do texto sobrevivente completa, na ordem final.
### O que continua NÃO sendo seu trabalho
| Tarefa | Ferramenta | Por quê |
@@ -57,18 +57,18 @@ uma. Um `reason` vazio é sinal de decisão sem critério.
Inclua o dado que embasou: *"abertura: 'Aquela mama' (ênfase 0.42)"* é útil;
*"zoom"* não é.
Não é campo de log: o texto é **exibido na tela de revisão**, ao lado da frase,
e é o que o editor lê antes de manter ou desfazer o que você decidiu.
Não é campo de log: o texto permanece no JSON e no histórico persistido para
o editor conferir por que a ação foi proposta.
### 6. Corte: alinhe à intenção
A tela lê cada `cut` contra as frases da transcrição:
### 6. Corte: preserve a integridade da fala
- cobre **≥ 60%** de uma frase → aquela frase é **removida**;
- toca só o **começo** ou só o **fim** → vira **trim** (a frase fica, aparada).
O aplicador executa literalmente o intervalo informado: ele não converte o
corte em decisão por frase nem encaixa a borda na palavra mais próxima.
Então corte a frase **inteira** quando quiser removê-la, e corte **só da borda
até a palavra** quando quiser aparar uma hesitação. Um corte de meia frase é
ambíguo — passa de 60% e apaga a linha toda. Detalhe: `10-revisao-humana.md`.
Portanto, reconstrua o texto sobrevivente, preserve frases completas e deixe
ao menos 0,30s de respiro junto às palavras mantidas. A engine bloqueia borda
dentro de palavra, continuação gramatical e margem insuficiente. Detalhes em
`06-texto-corte-marcador.md` e `10-revisao-humana.md`.
## Persistência
@@ -79,8 +79,10 @@ confirmada.
## Como o programa trata erros
- **Ação inválida** → rejeitada e reportada **individualmente**. Uma linha
malformada nunca derruba as outras.
- **Plano ou ação inválida** → o plano inteiro é bloqueado antes de alterar a
timeline.
- **Borda de corte semanticamente insegura** → o plano inteiro é bloqueado
antes da conexão com o Premiere e antes do backup.
- **Ação apontando para material cortado** → descartada e reportada, nunca
deslizada para o conteúdo vizinho.
- **Ação fora da mídia** → reportada como não colocada.
@@ -1,122 +1,45 @@
# 10 — A revisão humana: o que acontece com o seu JSON
# 10 — Revisão humana no fluxo atual
> **Escopo:** O que o app faz com o seu JSON na etapa 5 — muda como escrever as ações.
> **Quando:** ler antes da Fase 5 — ver a ordem de trabalho em `../SKILL.md`.
> **Escopo:** O que o painel realmente permite revisar antes da aplicação.
> **Quando:** ler antes de gerar e aprovar o plano.
> Leia antes de decidir cortes e zooms. Muda **como** escrever as ações, não
> apenas quais.
O painel atual exibe o JSON do plano e o aplica pela engine Python depois da
confirmação. Ele **não possui ainda** uma tela frase a frase, não encaixa trims
automaticamente em palavras e não gera `_phrase_review.json`. Por isso, não
presuma que uma etapa posterior corrigirá bordas editoriais imprecisas.
Seu JSON não vai direto para a timeline. Ele é salvo como plano de revisão e
abre no fluxo de revisão humana do painel, na **etapa 5 do Assistente**, uma
tela onde o editor vê cada frase do roteiro com a sua
decisão já aplicada e lapida antes de gerar.
## Preview obrigatório
Isso tem duas consequências práticas:
Antes de pedir aprovação, apresente para cada corte:
1. **Suas decisões são lidas por uma pessoa, frase a frase.** Uma decisão sem
motivo explícito parece arbitrária — e será desfeita.
2. **A tela traduz suas ações para o vocabulário dela.** Se você não escrever
as ações do jeito que essa tradução espera, a intenção se perde no caminho.
- intervalo removido e motivo;
- última frase completa que ficará antes;
- primeira frase completa que ficará depois;
- texto da emenda resultante;
- margem acústica em cada lado;
- qualquer incerteza que exija escuta humana.
---
O editor deve aprovar o conteúdo sobrevivente, não apenas a quantidade de
ações. Se o preview mostrar palavra truncada, oração incompleta ou continuação
sem contexto, o plano volta para edição e recebe novos limites.
## Como cada ação sua é lida
## Defesa automática
O app quebra a gravação em **frases** (os segmentos do voice timeline) e
projeta suas ações sobre elas.
Ao clicar em **Aplicar plano na timeline**, a engine revalida os cortes antes
de se conectar ao Premiere e antes de criar o backup. A aplicação é bloqueada
quando encontra:
### `cut`
- uma borda dentro de palavra;
- menos de 0,30s entre a borda e a palavra mantida;
- fala mantida interrompida antes do corte;
- retomada no meio de frase depois do corte.
| O corte cobre… | Vira | Na tela |
|---|---|---|
| **≥ 60%** da frase | frase **desativada** | apagada, riscada, reativável num clique |
| só o **começo** ou só o **fim** | **trim** da frase | a frase fica, aparada nas pontas |
| um pedaço no **meio** | nada em si | só conta para a regra dos 60% |
Essa validação é uma rede de segurança, não substitui o preview. Depois que o
plano passa, a engine cria uma única cópia de segurança, aplica as ações e
registra o resultado. O painel não cria uma segunda cópia.
O trim é **encaixado na fronteira de palavra** mais próxima. Você não precisa
acertar o frame: mire na palavra onde a frase deve começar ou terminar.
## `reason` continua obrigatório
**O que isso pede de você:** decida se está removendo *a linha* ou *aparando*
uma ponta, e escreva o corte de acordo.
- Removendo a linha → corte a frase inteira, de ponta a ponta.
- Aparando um falso começo → corte só da borda até a palavra onde a fala
engata. Um corte que cobre meia frase é ambíguo: passa de 60% e apaga a linha
toda, quando você só queria tirar a hesitação.
### `zoom` e `text`
Qualquer `zoom` ou `text` que toque uma frase marca aquela frase como
**ênfase** — e ênfase, nesta tela, significa **duas coisas**:
> **A frase de ênfase recebe zoom E legenda dinâmica. As demais recebem
> legenda comum.**
O nível vem da sua `scale`:
| `scale` | Nível na tela | |
|---|---|---|
| 1,15 | 1 — Leve | |
| 1,3 | 2 — Média | |
| 1,5 | 3 — Forte | |
| omitida, ou uma ação `text` | 2 — Média | padrão |
Sem nenhuma ação sua, a tela deriva o nível do `peak_emphasis` da frase
(< 0,25 → sem ênfase; < 0,45 → leve; < 0,65 → média; acima → forte). **A sua
decisão sempre ganha da derivação automática.**
**O que isso pede de você:** escolher a escala com intenção. Ela não é só
"quanto amplia" — é o peso que aquela frase terá no vídeo inteiro, incluindo o
tratamento da legenda. Um zoom leve numa frase de apoio não é neutro: promove
aquela frase a destaque tipográfico também.
### `marker`
Não altera a frase. Continua sendo o seu recado para o editor conferir uma
emenda — e é a ferramenta certa quando você está em dúvida (ver
`03-escolha-da-melhor-tomada.md`).
---
## `reason` aparece na tela
Não é campo de log. O texto que você escreve em `reason` é exibido para o
editor ao lado da frase selecionada, e é o que ele lê antes de manter ou
desfazer a sua decisão.
Escreva para quem está com pressa e vai decidir na hora:
- **Bom:** `"fecho, pico em 'devolver' (ênfase 0.34) — escala mais forte por ser o fechamento da peça"`
- **Ruim:** `"zoom"` · `"corte necessário"` · `"melhor tomada"`
A regra prática: se o `reason` não contém **o dado** que embasou (a palavra, o
número, a comparação entre tomadas), você provavelmente não tinha critério —
tinha impressão.
---
## O que a tela NÃO desfaz por você
- **Tempo errado continua errado.** A tela mostra suas ações no eixo da mídia
original; se você compensou para pós-corte, tudo aparece no lugar errado e o
editor não tem como adivinhar o que você quis dizer.
- **Excesso de zoom continua excesso.** A tela não impõe o teto de 2–4 por
minuto (`07-ritmo.md`) — ela mostra o que você mandou. Efeito demais chega
ao editor como trabalho de limpeza.
- **Frase promovida a ênfase sem querer.** Como zoom e legenda dinâmica andam
juntos, espalhar zooms "de segurança" enche o vídeo de legenda dinâmica. Na
dúvida, deixe sem — o editor promove; é mais barato que despromover.
---
## Depois da revisão
O editor pode, na tela: mudar o nível de ênfase (0–3), desativar ou reativar
frases, corrigir o texto, aparar as pontas por palavra, reclassificar entre
roteiro e bastidor e acrescentar zooms manuais em trechos arbitrários.
O resultado vira um `_phrase_review.json` e o `_phrase_actions.json` derivado —
e é esse que a geração usa. **Seu JSON é o ponto de partida da conversa, não a
palavra final.** Trabalhe para ser um bom ponto de partida: decisões
defensáveis, motivos legíveis e nenhuma escolha que o editor precise desfazer
antes de começar.
Escreva um motivo curto e verificável, com a fala ou comparação que sustentou
a decisão. Evite motivos genéricos como `"corte necessário"`: eles não ajudam o
editor a conferir o plano nem permitem melhorar os critérios depois.