feat: adicionadas métricas opcionais de fala ao Scanner com anális

- adicionadas métricas opcionais de fala ao Scanner com análise acústica na engine
- corrigido o progresso da transcrição do Scanner durante o processamento do Whisper
- refatorado o runner Swift do Apple Vision para executar requests em lote com retry CPU-only, corrigindo falhas de CVPixelBuffer/ANE/OCR
- documentado o fluxo de edição de vídeo por voz integrado ao Scanner, banco e engine
- formalizado o protocolo de edição por chat com perfil editorial e consultas progressivas

Resumo:
- 10 arquivos alterados
- 5 novos
- 5 modificados
- 0 removidos

 5 files changed, 367 insertions(+), 95 deletions(-)

Arquivos:
  - .jhonny/analises.db
  - code/engine/executar_scanner.py
  - code/engine/integracoes/visual/apple_vision_runner.swift
  - code/engine/integracoes/whisper/provider_de_transcricao_local.py
  - code/engine/testes/test_provider_de_transcricao_local.py
  - .jhonny/analises.db-shm
  - .jhonny/analises.db-wal
  - apple_vision_runner
  - code/relatorios/teste-enquadramento-20s.json
  - docs/
This commit is contained in:
João Henrique
2026-09-09 18:50:00 -04:00
parent b03b175973
commit c4c942044f
10 changed files with 15086 additions and 96 deletions
+500
View File
@@ -0,0 +1,500 @@
# Plano de edição de vídeo por voz
Documento vivo para integrar o Scanner, o banco SQLite, a tela **Editar vídeo**, a decisão assistida por IA e a aplicação segura do plano na timeline do Premiere Pro.
Status: desenho aprovado para implementação incremental.
## 1. Objetivo
Permitir que a tela **Editar vídeo** reutilize uma análise já executada pelo Scanner, sem transcrever o mesmo material novamente. A tela buscará no banco somente os dados necessários, montará um contexto compacto para a IA, receberá decisões editoriais em JSON, validará essas decisões pela engine e aplicará o plano na timeline ativa.
O fluxo não deve enviar o banco inteiro, o relatório completo ou o áudio para a IA. A IA recebe somente o contexto editorial solicitado.
## 1.1. Protocolo de edição solicitado pelo chat
Quando o usuário disser **“editar vídeo”**, o chat deve tratar isso como um
pedido de execução do fluxo deste documento.
O pedido deve separar três identificadores:
1. **Vídeo/análise**: qual análise do Scanner será usada. Preferir `video_id`
ou o caso exibido na tela, nunca confundir com o perfil editorial.
2. **Perfil editorial**: número ou identificador da personalidade/estilo da
edição. Esse número representa como editar, não qual arquivo editar.
3. **Tipo e objetivo do vídeo**: depoimento, entrevista, redes sociais,
educativo, institucional ou perfil personalizado.
Exemplo de pedido normalizado:
```json
{
"intencao": "editar_video",
"video_id": "12669c9c-038d-4a3f-99f9-fe6f6827b5f4",
"perfil_editorial_id": 3,
"tipo_de_video": "depoimento",
"objetivo": "preservar autenticidade e remover repetições",
"intervalo": {"inicio": 0.0, "fim": null}
}
```
Se o número informado ainda não estiver cadastrado, o chat deve pedir a
descrição do perfil ou registrar o perfil antes de gerar decisões. O número
não deve ser interpretado como `video_id`, `clipe_id` ou número de faixa.
### Ordem de execução no chat
Quando os dados estiverem disponíveis, o chat deve:
1. identificar o vídeo/análise correta;
2. carregar o perfil editorial pelo número;
3. consultar o resumo do vídeo localmente;
4. consultar falas, clipes, cenas e métricas necessárias;
5. montar um contexto editorial compacto;
6. aplicar as regras do perfil e o tipo do vídeo;
7. gerar diretamente o plano de edição;
8. validar o plano contra a análise e a timeline;
9. entregar o plano JSON e um resumo das decisões;
10. deixar a aplicação no Premiere para a etapa de confirmação do usuário.
O chat não deve devolver apenas uma sugestão textual quando o pedido for
“editar vídeo”. Deve gerar um plano estruturado, salvo em arquivo, pronto para
ser lido pelo `LeitorDePlanoDeEdicao`.
### Leitura dos registros sem desperdício de contexto
O banco pode ser lido integralmente pela engine em lotes, sem enviar todos os
registros para o chat ou para a IA. A leitura deve ser progressiva:
- primeiro, resumo, faixas, clipes e contagens;
- depois, falas do intervalo ou do objetivo escolhido;
- depois, métricas de voz quando o perfil depender de ritmo, intensidade ou
pausas;
- depois, palavras somente para decisões que exigem precisão;
- por fim, cenas e evidências visuais relacionadas aos trechos candidatos.
“Ler na íntegra” significa garantir que a engine tenha acesso aos registros
necessários, não despejar o banco inteiro no contexto do modelo.
### Consultas criadas durante a edição
Quando surgir uma necessidade nova, a consulta deve ser criada como parte da
engine e documentada neste MD, contendo:
- nome da intenção consultada;
- parâmetros aceitos;
- SQL parametrizado;
- formato compacto de retorno;
- teste de regressão;
- perfil editorial que utiliza a consulta.
Consultas temporárias podem ser usadas para investigar um caso, mas não devem
virar parte do fluxo do chat sem serem transformadas em um método reutilizável
de `ConsultasDeAnalises` ou de um módulo de aplicação.
## 2. Vocabulário do domínio
- **Vídeo analisado**: registro de uma timeline no banco, identificado por `video_id`.
- **Faixa**: trilha de áudio ou vídeo pertencente ao vídeo analisado.
- **Clipe**: intervalo de uma faixa na timeline, com referência ao arquivo de origem.
- **Fala**: segmento de transcrição associado a um clipe, com início, fim e texto.
- **Palavra**: unidade sincronizada dentro de uma fala.
- **Métrica de voz**: sinal acústico calculado para uma fala, como energia, pitch, velocidade e pausas. Não é uma emoção classificada.
- **Cena**: intervalo visual identificado pelo Scanner.
- **Evidência**: observação visual ou sonora com intervalo e confiança.
- **Contexto editorial**: pacote compacto de fatos enviado à IA para apoiar uma decisão de edição.
- **Decisão editorial**: ação proposta pela IA, como corte, zoom, texto ou marcador.
- **Plano de edição**: conjunto ordenado e validado de decisões editoriais.
- **Aplicador**: módulo que executa o plano na timeline do Premiere.
Transcrição, análise, decisão e aplicação são etapas diferentes. Uma transcrição não deve ser interpretada automaticamente como uma decisão de corte.
## 3. Fluxo completo
```text
Premiere Pro
│
├─ Scanner: lê a timeline e salva análise
│ ├─ vídeos, faixas e clipes
│ ├─ transcrição e palavras
│ ├─ métricas acústicas opcionais
│ ├─ cenas e evidências visuais
│ └─ versão da análise
│
└─ Editar vídeo
├─ seleciona um vídeo analisado
├─ define objetivo e regras editoriais
├─ pede contexto editorial compacto à engine
├─ envia contexto à IA
├─ recebe plano JSON
├─ valida plano e origem
├─ mostra prévia das decisões
└─ aplica plano pela engine
```
O Scanner produz fatos. A IA decide. A engine valida e aplica. A interface coordena e apresenta.
## 4. Situação atual do código
### Já existe
- `code/cep-plugin/`: painel CEP com **Editar vídeo** e **Scanner**.
- `code/engine/persistencia/esquema.py`: tabelas SQLite de timeline, transcrição, palavras, cenas e evidências.
- `code/engine/persistencia/repositorio_de_analises_sqlite.py`: persistência das análises.
- `code/engine/persistencia/consultas.py`: consultas de leitura compactas.
- `code/engine/scanner/transcricao_da_timeline.py`: transcrição projetada para os clipes da timeline.
- `code/engine/editor/modelos.py`: `PlanoDeEdicao` e `AcaoDeEdicao`.
- `code/engine/editor/leitura/leitor_de_plano.py`: validação do plano externo.
- `code/engine/editor/aplicador_de_plano_de_edicao.py`: aplicação das ações.
- `code/engine/aplicar_plano_de_edicao.py`: entrada da engine para aplicar um plano.
- `code/cep-plugin/main.js`: geração de `dados-para-ia.json` e aplicação do plano recebido.
### Lacunas conhecidas
- `listar_falas_no_intervalo()` não retorna métricas acústicas por padrão.
- Editar vídeo ainda trabalha principalmente com `transcricao.json` e não seleciona diretamente uma análise do Scanner.
- O contexto editorial ainda não é um módulo explícito da engine.
- O plano atual identifica a origem principalmente por nome e tempo; com várias faixas e clipes, precisa também de `video_id` e `clipe_id`.
- Reprocessamentos precisam de uma política explícita para não duplicar segmentos no banco.
## 5. Consultas SQL compactas
As consultas ficam na engine. O painel não abre SQLite nem monta SQL.
### 5.1. Listar vídeos analisados
```sql
SELECT id AS video_id, nome, duracao, criado_em
FROM videos
ORDER BY criado_em DESC
LIMIT :limite;
```
### 5.2. Resumo do vídeo
```sql
SELECT
v.id AS video_id, v.nome, v.duracao, v.taxa_de_quadros,
v.largura, v.altura,
(SELECT COUNT(*) FROM faixas f WHERE f.video_id = v.id) AS total_faixas,
(SELECT COUNT(*) FROM clipes c WHERE c.video_id = v.id) AS total_clipes,
(SELECT COUNT(*) FROM segmentos_de_transcricao s WHERE s.video_id = v.id) AS total_falas,
(SELECT COUNT(*) FROM cenas c WHERE c.video_id = v.id) AS total_cenas,
(SELECT COUNT(*) FROM evidencias_visuais e WHERE e.video_id = v.id) AS total_evidencias
FROM videos v
WHERE v.id = :video_id;
```
### 5.3. Falas para edição
Consulta padrão, sem palavras e sem métricas:
```sql
SELECT s.id AS fala_id, s.clipe_id, s.inicio, s.fim, s.texto,
s.confianca, s.falante, s.emocao, s.confianca_emocao
FROM segmentos_de_transcricao s
WHERE s.video_id = :video_id
AND s.fim >= :inicio
AND (:fim IS NULL OR s.inicio <= :fim)
ORDER BY s.inicio, s.id;
```
Consulta com métricas acústicas:
```sql
SELECT s.id AS fala_id, s.clipe_id, s.inicio, s.fim, s.texto,
s.confianca, s.falante,
s.caracteristicas_acusticas AS metricas_de_voz
FROM segmentos_de_transcricao s
WHERE s.video_id = :video_id
AND s.fim >= :inicio
AND (:fim IS NULL OR s.inicio <= :fim)
AND s.caracteristicas_acusticas IS NOT NULL
AND s.caracteristicas_acusticas <> '{}'
ORDER BY s.inicio, s.id;
```
`metricas_de_voz` deve ser convertido de texto JSON para objeto dentro da engine. O SQL não deve conhecer a estrutura variável das métricas.
### 5.4. Palavras sob demanda
```sql
SELECT p.segmento_id AS fala_id, p.ordem, p.texto, p.inicio, p.fim,
p.confianca, p.falante
FROM palavras_de_transcricao p
WHERE p.segmento_id IN (:fala_ids)
ORDER BY p.segmento_id, p.ordem;
```
Na implementação, `:fala_ids` deve ser expandido com parâmetros SQLite individuais. Nunca interpolar valores da interface no SQL.
### 5.5. Cenas e evidências
```sql
SELECT clipe_id, inicio, fim, confianca
FROM cenas
WHERE video_id = :video_id
AND (:clipe_id IS NULL OR clipe_id = :clipe_id)
ORDER BY inicio;
```
```sql
SELECT clipe_id, tipo, inicio, fim, valor, confianca, provider, modelo
FROM evidencias_visuais
WHERE video_id = :video_id
AND (:clipe_id IS NULL OR clipe_id = :clipe_id)
AND (:tipo IS NULL OR tipo = :tipo)
AND (:confianca_minima IS NULL OR confianca IS NULL OR confianca >= :confianca_minima)
ORDER BY inicio;
```
## 6. Interface profunda da engine
O módulo principal deve esconder SQL, agrupamento, conversão de JSON, ordenação e redução de contexto.
Interface proposta:
```python
contexto = montador.montar(
video_id=video_id,
objetivo=objetivo_editorial,
inicio=inicio,
fim=fim,
incluir_metricas=True,
incluir_palavras=False,
)
```
Responsabilidades de `MontadorDeContextoEditorial`:
- validar `video_id` e intervalos;
- localizar o resumo do vídeo;
- buscar falas no intervalo;
- incluir métricas somente quando solicitado;
- buscar palavras somente quando solicitado;
- agrupar falas por clipe;
- anexar cenas e evidências relevantes;
- remover campos vazios;
- normalizar números e intervalos;
- produzir um contrato estável para a IA;
- informar a versão da análise usada.
O painel chama uma entrada da engine e recebe JSON. Não conhece tabelas nem SQL.
## 7. Contexto enviado à IA
```json
{
"schema": "contexto_editorial_v1",
"analysis": {
"video_id": "12669c9c-038d-4a3f-99f9-fe6f6827b5f4",
"versao": "hash-da-analise",
"fonte": "scanner"
},
"video": { "nome": "Cópia de 0E6A8829", "duracao": 120.0 },
"objetivo": {
"tipo": "depoimento",
"instrucao": "Remover pausas longas e repetições sem perder autenticidade.",
"preservar": ["contexto", "frases completas", "pausas emocionais"],
"remover": ["silêncios longos", "repetições", "erros explícitos"]
},
"falas": [
{
"fala_id": 42,
"clipe_id": "000f476b",
"inicio": 12.45,
"fim": 15.82,
"texto": "Eu comecei esse processo no ano passado.",
"falante": "SPEAKER_00",
"metricas_de_voz": {
"energy_rms": 0.084,
"pitch_hz_median": 187.5,
"pitch_hz_std": 32.8,
"speaking_rate_wps": 2.4,
"longest_internal_pause_s": 0.42,
"gap_before_s": 0.18
}
}
],
"cenas": [],
"evidencias_visuais": []
}
```
Perfis de tamanho:
- `resumo`: metadados, contagens e intervalos principais;
- `fala`: falas, locutores e métricas, sem palavras;
- `precisao`: falas, palavras e evidências do intervalo selecionado;
- `completo`: somente para diagnóstico local, nunca como padrão para IA.
O fluxo normal começa com `resumo` ou `fala` e consulta `precisao` somente quando necessário.
## 8. Decisões e plano de edição
O plano mantém o contrato já validado pela engine e recebe metadados para impedir aplicação em outra análise:
```json
{
"schema": "plano_edicao_v2",
"source": "Cópia de 0E6A8829",
"analysis": {
"video_id": "12669c9c-038d-4a3f-99f9-fe6f6827b5f4",
"versao": "hash-da-analise"
},
"actions": [
{
"id": "acao-001",
"kind": "cut",
"target": {
"clipe_id": "000f476b",
"arquivo_de_origem": "/caminho/video.mp4"
},
"start": 20.4,
"end": 23.8,
"reason": "Pausa longa sem conteúdo entre duas frases.",
"params": { "tipo": "silencio", "confianca": 0.91 }
}
]
}
```
Regras para a IA:
- não inventar tempos fora do material analisado;
- não cortar fala sem `reason`;
- usar segundos na origem;
- não usar tempo relativo ao JSON;
- não alterar a transcrição original;
- indicar confiança baixa quando houver dúvida;
- não aplicar o plano diretamente no Premiere.
## 9. Validação antes da aplicação
### Validação estrutural
Responsável: `LeitorDePlanoDeEdicao`.
- JSON válido;
- `source` preenchido;
- `actions` não vazio;
- tipo conhecido;
- início e fim numéricos;
- fim maior que início;
- motivo obrigatório.
### Validação contextual
Novo comportamento a adicionar:
- `video_id` do plano igual ao vídeo selecionado;
- versão da análise ainda válida;
- `clipe_id` existente;
- arquivo de origem compatível;
- intervalo contido no clipe ou na origem;
- nenhuma ação duplicada;
- nenhuma ação já aplicada;
- plano compatível com a sequência ativa.
## 10. Fluxo da tela Editar vídeo
1. Selecionar origem: `Transcrever este vídeo` ou `Usar análise do Scanner`.
2. Listar análises disponíveis e mostrar modelo, idioma, diarização, métricas e data.
3. Escolher objetivo: depoimento, entrevista, redes sociais ou personalizado.
4. Definir regras: remover silêncio, remover repetição, preservar pausas emocionais, locutores e intensidade.
5. Carregar contexto compacto pela engine.
6. Mostrar falas, tempos, locutores, métricas e status da análise.
7. Exportar contexto para IA ou chamar um Provider de IA futuramente.
8. Importar o plano devolvido.
9. Mostrar cada ação com intervalo, texto, motivo, confiança e validação.
10. Criar backup, validar e aplicar pela engine.
## 11. Classes e módulos planejados
### Domínio
`code/engine/editor/contexto_editorial/modelos.py`
- `ContextoEditorial`;
- `FalaEditorial`;
- `MetricaDeVozEditorial`;
- `ObjetivoEditorial`.
Essas classes não conhecem SQLite, JSON de transporte ou Premiere.
### Aplicação
`code/engine/editor/contexto_editorial/montador.py`
- `MontadorDeContextoEditorial`.
Essa é a interface profunda: recebe filtros editoriais e devolve contexto compacto, escondendo consultas, agrupamento e normalização.
### Persistência
Estender `ConsultasDeAnalises` com:
- `listar_videos_analisados()`;
- `consultar_contexto_editorial()`;
- `listar_falas_editoriais()`;
- `listar_palavras_das_falas()`.
### Transporte
Criar `code/engine/gerar_contexto_editorial.py` para ler pedido JSON, validar entrada, chamar o montador e escrever resposta JSON.
### Plano
Reutilizar `LeitorDePlanoDeEdicao`, `PlanoDeEdicao` e `AplicadorDePlanoDeEdicao`, adicionando a validação contextual antes do aplicador.
## 12. Versionamento e idempotência
Cada execução do Scanner deve ter uma identidade de análise. O editor deve guardar essa identidade no contexto e no plano.
Recomendações:
- usar `analises_versao` para registrar etapa e hash da entrada;
- incluir configuração, modelo, idioma e faixas no hash;
- invalidar o plano quando a timeline ou o vídeo mudar;
- substituir ou versionar segmentos ao reprocessar;
- não acumular silenciosamente duas transcrições iguais.
Antes do editor, corrigir a política de reprocessamento para evitar duplicidade em `segmentos_de_transcricao`.
## 13. Testes obrigatórios
- consultas filtram por vídeo e intervalo;
- métricas aparecem somente quando solicitadas;
- palavras não aparecem por padrão;
- contexto compacto agrupa falas por clipe;
- campos vazios são removidos;
- palavras são carregadas sob demanda;
- plano de outro vídeo é rejeitado;
- plano de análise antiga é rejeitado;
- ação fora do clipe é rejeitada;
- aplicação cria backup e não repete ação;
- interface seleciona análise, importa plano e mostra validação;
- cancelamento interrompe a geração/aplicação.
## 14. Ordem de implementação
- [ ] Consultas de vídeos analisados.
- [ ] Consulta de falas com opção de métricas.
- [ ] Consulta de palavras sob demanda.
- [ ] Modelos de `ContextoEditorial`.
- [ ] `MontadorDeContextoEditorial`.
- [ ] `gerar_contexto_editorial.py`.
- [ ] Versionamento e reprocessamento no banco.
- [ ] Origem “Usar análise do Scanner” na tela Editar vídeo.
- [ ] Renderização do contexto e das falas.
- [ ] Exportação do contexto para IA.
- [ ] Plano com `video_id`, `clipe_id` e versão.
- [ ] Validação contextual.
- [ ] Aplicação pela engine.
- [ ] Teste em cópia da sequência.
- [ ] Revisão final do código.
## 15. Critério de conclusão
O recurso estará pronto quando o usuário conseguir executar o Scanner uma vez, abrir Editar vídeo, escolher o vídeo analisado, buscar somente as falas necessárias, gerar contexto compacto, importar um plano, validá-lo contra a análise correta, revisar as ações e aplicá-las na timeline com backup, sem repetir a transcrição.
Este documento deve ser atualizado a cada etapa implementada, especialmente quando um contrato JSON ou uma consulta SQL mudar.