# Revisão sem retrabalho

Para quando o vídeo já existe e precisa de ajuste, diagnóstico ou conferência antes da entrega. A regra geral:
achar a causa, mexer no mínimo, conferir o que mudou e o que não podia mudar, e dizer a verdade sobre o que foi
feito.

| a pessoa diz | seção |
|---|---|
| "tá poluído", "tá confuso", "o que eu tiro?" | Diagnóstico só de leitura |
| "entre 00:12 e 00:18, arruma tal coisa" | Conserto num trecho |
| "de novo o mesmo erro" | Tentativa repetida |
| "tem chiado", "a voz sobe e desce" | Voz |
| "confere a legenda antes de eu postar" | Legendas checadas no áudio |
| "não pode aparecer nome de cliente", "confere se não vazou nada", vídeo com tela gravada ou borrão | Dado pessoal no arquivo exportado |
| "tenho uma pasta cheia de arquivo, o que uso?" | Triagem dos materiais |
| "faz um antes e depois" | Comparação justa |
| "quanto isso custou?" | Custo, sem inventar |

## Ordem de prioridade

Quando há vários problemas, nesta ordem (a não ser que a pessoa destaque outra coisa):

1. sentido e informação (o que é dito e mostrado está certo e fiel ao material);
2. voz e sincronia;
3. enquadramento e leitura;
4. acabamento.

## Cada problema em quatro partes

- **Onde:** o intervalo ou a frase, com o tempo conferido no vídeo.
- **O que está errado:** separe defeito (texto errado, palavra cortada, rosto coberto) de preferência nova ("quero
  mais rosa").
- **A mudança:** algo que dá para conferir depois ("a legenda sobe 80 px entre 00:12 e 00:15").
- **O que preservar:** o que não pode mudar junto.

"Não ficou bom" sem detalhe: olhe o vídeo e aponte problemas concretos. Só pergunte a intenção se houver saídas
que não combinam entre si. Não invente defeito para justificar mais uma rodada.

Ideia nova de estilo não é correção: é decisão nova, e se decide com a pessoa. Correção pequena e que dá para
desfazer não precisa de confirmação.

Reaproveite o que continua válido: projeto, transcrição, rostos medidos, recorte, sons. Mudar posição ou fonte
não pede recorte novo nem animação redesenhada.

## Diagnóstico só de leitura

Nesta etapa você não mexe no projeto. Só olha e escreve.

1. Toque o vídeo inteiro com som, no tamanho do celular. Depois olhe uma folha com 1 quadro por segundo e tiras
   densas nas trocas:
   `uv run --directory "<SKILL>" python helpers/tira_de_quadros.py '<VIDEO>' 0 <fim> --fps 1 --colunas 8 --saida '<PROJETO>/quadros/folha.jpg'`
2. Confira a fala real e diga, bloco a bloco, qual mensagem manda ali.
3. Marque, com início e fim:
   - onde rosto, textos, legenda, animações e imagens disputam o olho;
   - onde falta contexto para entender;
   - onde algo inserido não acrescenta nada.
4. Separe erro objetivo (texto cobre o rosto, legenda repete o lettering) de gosto.
5. Dê a cada problema uma saída só, escolhida da mais leve para a mais forte: **deixar** (fica como está; era
   gosto, não erro), **mover** (vai para outro canto da tela), **encolher** (menor, mais curto ou mais baixo),
   **atrasar** (entra depois, quando o que disputa com ele já saiu) ou **remover**.
6. Escreva `diagnostico.md` no projeto: até cinco correções, da mais importante para a menos, e para cada uma o
   jeito mais rápido de aplicar sem quebrar o que já está bom.

| # | de | até | problema | tipo | saída | como aplicar |
|---|---|---|---|---|---|---|
| 1 | 00:08,2 | 00:11,0 | lista e legenda brigando no mesmo lugar | erro | atrasar | a legenda volta só depois que a lista sai |
| 2 | 00:14,0 | 00:15,5 | carimbo que não corresponde a nenhuma frase | erro | remover | apagar a peça do `projeto.json` |

Não sugira fala nova, trilha nova, oferta nova nem material comprado. Arquivo que não abriu vira limite declarado,
não suposição.

## Conserto num trecho

Entrada: a versão atual, o problema, o início e o fim, e como saber que ficou certo.

1. **Ache a peça que causa.** Camada, arquivo ou valor. Texto em cima do rosto pode ser a posição, a ordem das
   camadas ou uma máscara: corrija o mecanismo certo, não o sintoma.
2. **Mude o mínimo.** Fora do intervalo nada se mexe: enquadramento, trilha, cores, fonte, emendas, duração, fala.
3. **Se precisar sair do trecho,** pare, explique a dependência e mostre o menor alcance possível antes de fazer.
4. **Mostre o trecho corrigido** no estúdio com 1 a 2 s de margem antes e depois.
5. **Depois do render, compare com a aprovada.** Fora do trecho, imagem e som têm que ser os mesmos. Grave este
   script no projeto e rode:

   ```python
   # comparar_versoes.py  uso: python3 comparar_versoes.py aprovada.mp4 nova.mp4 <de> <ate>
   import subprocess, sys, tempfile
   from pathlib import Path
   aprovada, nova, de, ate = sys.argv[1], sys.argv[2], float(sys.argv[3]), float(sys.argv[4])
   tmp = Path(tempfile.mkdtemp())

   def duracao(v):
       return float(subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", v],
                                   capture_output=True, text=True).stdout)

   def fora(t0, t1):                        # não encosta no trecho consertado (com 0,1 s de folga)
       return t1 <= de - 0.1 or t0 >= ate + 0.1

   avisos = []
   if abs(duracao(aprovada) - duracao(nova)) > 0.05:
       avisos.append(f"duração mudou: {duracao(aprovada):.2f} s para {duracao(nova):.2f} s")
   subprocess.run(["ffmpeg", "-v", "error", "-i", aprovada, "-i", nova, "-lavfi",
                   f"[0:v][1:v]ssim=stats_file={tmp / 'ssim.log'}", "-f", "null", "-"], check=True)
   num, den = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=r_frame_rate",
                             "-of", "csv=p=0", aprovada], capture_output=True, text=True).stdout.strip().split("/")
   fps = float(num) / float(den)
   ruins = []
   for linha in open(tmp / "ssim.log"):
       c = dict(p.split(":") for p in linha.split() if ":" in p)
       t = (int(c["n"]) - 1) / fps
       if fora(t, t + 1 / fps) and float(c["All"]) < 0.98:
           ruins.append(t)
   if ruins:
       avisos.append(f"imagem mudou fora do trecho em {len(ruins)} quadros, de {ruins[0]:.2f} a {ruins[-1]:.2f} s")

   def rms(v, nome):                        # volume a cada 0,5 s
       log = tmp / f"{nome}.log"
       subprocess.run(["ffmpeg", "-v", "error", "-i", v, "-map", "0:a:0", "-af",
                       f"aresample=48000,asetnsamples=n=24000,astats=metadata=1:reset=1,"
                       f"ametadata=print:key=lavfi.astats.Overall.RMS_level:file={log}", "-f", "null", "-"], check=True)
       out, t = [], None
       for l in open(log):
           if "pts_time:" in l:
               t = float(l.split("pts_time:")[1])
           elif "RMS_level=" in l:
               out.append((t, float(l.split("=")[1])))
       return out

   for (t, x), (_, y) in zip(rms(aprovada, "a"), rms(nova, "b")):
       if fora(t, t + 0.5) and abs(x - y) > 1 and max(x, y) > -60:
           avisos.append(f"som mudou fora do trecho em {t:.1f} s ({x:.1f} dB para {y:.1f} dB)")
   print("\n".join(avisos) or "fora do trecho, nada mudou")
   ```

   Qualquer aviso fora do trecho é mudança sem querer: ache a causa antes de entregar. Olhe também a emenda do
   trecho com a tira de quadros a 30 qps, para ver se continua lisa.
6. **Versão nova, nunca por cima.** O nome diz o que mudou (`final_v2_legenda-00m12.mp4`). Na entrega, uma linha
   do que mudou e o intervalo conferido.

Correção não é pretexto para redesenhar o vídeo.

## Tentativa repetida

O mesmo erro voltou depois de uma correção:

1. Antes da próxima tentativa, compare o que devia sair com o que saiu. Escreva a diferença em uma frase.
2. Teste uma hipótese por vez, num trecho curto: o menor em que o defeito ainda aparece (uma amostra de 2 s, não o
   vídeo inteiro).
3. Se a correção repete a falha sem nada novo aprendido, pare esse ciclo. Conte o que testou, o que descobriu e o
   que falta (uma informação da pessoa ou uma capacidade que o computador não tem). Siga com as correções que não
   dependem disso.
4. Refazer o vídeo todo não é jeito automático de consertar.

## Voz

Chiado, picos, voz que estourou, eco de sala ou volume subindo e descendo que atrapalham entender.

- **Relate só o que achou de verdade.** `helpers/noise_check.py` mede o ruído de fundo, a saturação e o eco de sala
  de cada vídeo (só a voz: `helpers/voz_check.py VIDEO`); `helpers/voice_levels.py` mede o volume desigual entre
  frases. São medidas: ouça os tempos que a saída cita antes de afirmar, e diga "medi", não "ouvi".
- **Correção moderada** com o que é local: limpeza de ruído leve ou forte (`helpers/denoise.py`), ganho por
  trecho. Mantenha o timbre, as pausas com intenção e o alinhamento com a imagem.
- **Saturação** (`voz SATURADA` ou `possível saturação`): a voz bateu no teto e a parte de cima da onda se perdeu.
  **Não se recupera por inteiro.** Diga isso e não prometa recuperar: dá para suavizar o áspero dos picos, não
  para desfazer. Baixar o volume não conserta (a saturação fica, só mais baixa). Se a frase saturada importa,
  pergunte se dá para regravar com o celular mais longe da boca ou o ganho mais baixo. Saturação leve que foi
  abaixada depois pode sair como `possível` ou nem aparecer: na dúvida, ouça os picos.
- **Eco de sala** (`eco de sala leve` ou `forte`): a voz continua soando depois que a pessoa para (a saída diz
  quanto tempo leva para sumir) ou volta repetida alguns ms depois. **Trate leve:** tire um pouco do grave
  embolado e abaixe só a cauda nas pausas, sem apagar:

  ```bash
  ffmpeg -i original.mp4 -c:v copy -af "highpass=f=90,equalizer=f=300:t=q:w=1.2:g=-3,agate=threshold=LIMIAR:ratio=2:range=0.3:attack=5:release=150" tratado.mp4
  ```

  `LIMIAR` fica 25 dB abaixo da voz medida no `noise_check` (voz -16 dB: 10^(-41/20), uns 0.009). Numa sala
  sintética de 0,70 s essa receita levou a medida para 0,64 s: melhora pouco, e é isso mesmo, porque a sala não sai
  do arquivo. Porta de ruído forte ou redução pesada apaga a cauda e picota a voz (o fim das palavras some e o
  fundo liga e desliga): não use. Eco forte que atrapalha entender: diga e sugira regravar num cômodo com cortina,
  sofá ou tapete.
- **Amostra com três situações:** um trecho de voz mais fraca, um de voz mais forte e um silêncio. Ponha o
  original e o tratado um depois do outro, os dois igualados no mesmo volume percebido, para o ouvido não ser
  enganado pelo mais alto (a receita de eco acima deixou o tratado quase 1 LU mais baixo; sem igualar, ele
  pareceria pior só por isso):

  ```bash
  ffmpeg -ss 12 -t 6 -i original.mp4 -vn -ac 1 -ar 48000 -af loudnorm=I=-20:TP=-2 voz_original.wav
  ffmpeg -ss 12 -t 6 -i tratado.mp4 -vn -ac 1 -ar 48000 -af loudnorm=I=-20:TP=-2 voz_tratado.wav
  ffmpeg -i voz_original.wav -f lavfi -t 0.7 -i anullsrc=r=48000:cl=mono -i voz_tratado.wav \
    -filter_complex "[0:a][1:a][2:a]concat=n=3:v=0:a=1" comparar_voz.wav
  ```

  Depois meça de novo o tratado com `helpers/voz_check.py` e registre as duas medidas.
- **Saída sem clipping:** pico verdadeiro de -1 dBTP ou menos (`-af ebur128=peak=true`).
- Se a pessoa pediu para ouvir antes, trate o vídeo inteiro só depois do ok. Se já autorizou, a comparação é
  conferência sua.
- Nunca sintetize palavra nem troque a voz. Registre as medidas que fez e o que continuou com problema.

## Legendas checadas no áudio

Antes de entregar, principalmente com nomes, números e termos técnicos.

1. Confronte cada legenda com o áudio. Ouça de verdade os trechos com nome, número, marca e termo técnico.
2. Glossário: a lista de nomes e termos da pessoa. Use na transcrição (`--prompt` do `transcribe.py`) e nas
   correções do projeto (`"correcoes"` no `projeto.json`). O roteiro serve para grafia; não substitui o que foi
   dito.
3. Corrija erro de reconhecimento e pontuação. Negação, condição, ressalva e número nunca somem.
4. Palavra que continua duvidosa não se completa como certa: vai para `palavras-duvidosas.md`, com o tempo, o que
   você ouviu e as opções. Entregue essa lista junto.
5. Legenda em pedaços de até 3 palavras, numa linha, que se leem naturalmente (o projeto corta assim:
   `legenda.max_palavras` 3 e `max_letras` por linha). Cada uma aparece e some com a voz; nenhuma fica pendurada
   no silêncio, e nenhuma pisca (uma palavra solta de 0,1 s): junte com a vizinha.
6. Confira no formato de entrega: lugar, quebra de linha, tamanho e contraste. A legenda não pode ficar em cima
   do rosto, das mãos nem do que a pessoa está mostrando. Lettering aprovado fica; sobreposição que não se lê se desfaz.
7. Prévia focada nas legendas mais compridas ou mais difíceis.

Nunca edite o áudio da fala para ele concordar com o texto.

## Dado pessoal no arquivo exportado

Obrigatório antes de entregar quando o vídeo tem tela gravada, conversa, print, painel, e-mail ou qualquer
borrão. É a última conferência, no arquivo exportado (o estúdio não serve: é o arquivo que vai para o mundo).

1. Monte a lista do que não pode aparecer, olhando a gravação bruta: nomes de pessoas, de conversas, de
   projetos e de clientes, loja, endereço, o que a pessoa pediu para esconder. Nome dela e da marca dela,
   se podem aparecer, vão em `--ignorar`.
2. Rode no arquivo exportado:
   `uv run --directory '<SKILL>' python helpers/conferir_dados.py '<PROJ>/renders/final.mp4' --termos 'Maria Souza, Projeto X' --ignorar 'Vitória'`
   Ele lê pelo OCR todos os quadros até 3 min de vídeo (nos mais longos, 4 por segundo; `--todos` lê todos) e
   procura os termos, e-mail, CPF, CNPJ, telefone, cartão, @perfil e nome comum com sobrenome. No Mac usa o
   leitor da Apple; fora dele, o Tesseract, se estiver instalado. Sem leitor, ele diz: aí a conferência é no olho,
   com a tira de quadros a 30 qps em cada troca de tela.
3. Cada achado vem com o texto lido, o tempo, os quadros (de e até) e a caixa em pixels. Alargue o borrão
   (caixa e janela de tempo) ou tire o trecho, renderize e rode de novo até sair `0 achado(s)`. Os vazamentos
   que já aconteceram: o primeiro quadro (borrão com fade de entrada), um quadro antes de o borrão entrar, uma
   linha que mudou de lugar no menu, um menu que abre nos últimos 0,15 s.
4. `possível nome` é palpite (um nome comum seguido de outra palavra com maiúscula): confira se pode aparecer.
5. Zero achado não prova que não tem dado: o leitor erra letra pequena, borrada, girada ou em movimento. Olhe
   também o primeiro e o último quadro.
6. **O que ainda tem o dado fora do vídeo final.** O `borrar` do projeto queima o borrão na cópia do corte do
   projeto (`assets/cut.mp4`), mas o `<EDIT>/cut.mp4`, as gravações brutas e o original de uma prova com
   `arquivo` continuam com o dado legível. Se a pasta do projeto vai para alguém (zip, estúdio compartilhado),
   mande sem a `edit/` e sem esses originais, e rode o `conferir_dados.py` também no `assets/cut.mp4`. Para
   borrar um arquivo fora do projeto (o corte da `edit`, uma gravação), com o ffmpeg:

   ```bash
   ffmpeg -i cut.mp4 -filter_complex "[0:v]split[a][t];[t]crop=LARG:ALT:X:Y,boxblur=RL:2:RC:2[b];[a][b]overlay=X:Y:enable='between(t,DE,ATE)'" -c:a copy cut_borrado.mp4
   ```

   O `split` é obrigatório: usando `[0:v]` duas vezes o ffmpeg não reclama e o borrão não sai (visto no teste).
   `RL` é no máximo metade do menor lado da caixa e `RC` no máximo um quarto (o `boxblur` recusa mais: numa
   caixa de 26 px de altura, `boxblur=13:2:6:2`). Várias caixas: repita o trio em cadeia, cada um com o seu
   `split` (`...overlay=...[v1];[v1]split[a2][t2];[t2]crop=...,boxblur=...[b2];[a2][b2]overlay=...`). Depois,
   `conferir_dados.py` no `cut_borrado.mp4` também.

Na entrega, diga que rodou a conferência de dados, em quantos quadros, e o que não dá para garantir.

## Triagem dos materiais

Pasta cheia de arquivos e nenhuma certeza do que usar. Nesta etapa não altere nada, não procure, não
compre e não gere nada.

1. Faça o inventário da pasta (grave no projeto e rode com `python3 inventario.py '<PASTA>'`):

   ```python
   import hashlib, json, subprocess, sys
   from pathlib import Path
   pasta, vistos = Path(sys.argv[1]), {}
   for arq in sorted(p for p in pasta.rglob("*") if p.is_file() and not p.name.startswith(".")):
       h = hashlib.sha256()
       with open(arq, "rb") as f:
           for bloco in iter(lambda: f.read(1 << 20), b""):
               h.update(bloco)
       sha = h.hexdigest()[:12]
       r = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries",
                           "stream=width,height:format=duration", "-of", "json", str(arq)], capture_output=True, text=True)
       info = json.loads(r.stdout or "{}")
       s = (info.get("streams") or [{}])[0]
       tam = f"{s['width']}x{s['height']}" if "width" in s else "sem imagem"
       dur = info.get("format", {}).get("duration")
       dup = f"  IGUAL A {vistos[sha]}" if sha in vistos else ""
       vistos.setdefault(sha, arq.name)
       print(f"{arq.relative_to(pasta)} · {tam}" + (f" · {float(dur):.1f} s" if dur and float(dur) > 0.1 else "") + dup)
   ```

2. Confira a fala e olhe cada arquivo. Classifique em quatro tipos, sem misturar:
   - **prova de verdade:** mostra de fato o que a fala afirma, e é da pessoa;
   - **demonstração:** mostra como algo funciona;
   - **inspiração visual:** referência de estilo, não entra no vídeo;
   - **ilustração:** desenho, imagem de banco ou gerada. Ilustra, nunca prova.
3. Aponte duplicatas (o inventário acha as iguais; as quase iguais, olhando), baixa resolução (menor que a área que
   vai ocupar na tela), citação na fala a material que não está na pasta e origem ou direito desconhecido
   (marcado para conferir; arquivo na pasta não quer dizer direito de uso).
4. Faltou material: antes de buscar ou gerar, sugira deixar a própria gravação limpa na tela, ou um esquema
   simples que seja fiel à fala. Buscar ou gerar imagem só com o ok da pessoa, e o que vier é ilustração.
5. Escreva `materiais.md`: cada arquivo escolhido, a frase que ele sustenta, como vai aparecer e por quê. Depois a
   lista de lacunas, duplicatas e itens a conferir.

## Comparação justa (antes e depois, ou A e B)

1. Os dois lados mostram a mesma situação? Se não, explique e não monte como confronto direto.
2. Iguale velocidade, enquadramento, escala e sincronia. Não piore um lado em cor, som ou nitidez.
3. Rótulos neutros e literais ("Antes", "Depois", "Versão A").
4. Comparando som, iguale o volume percebido (como na seção Voz). Comparando imagem, nada de texto em cima do
   detalhe.
5. Lado a lado ou um depois do outro: o que se lê melhor no formato final.
6. Se há fala gravada sobre aquilo, é ela que acompanha. Nenhum resultado ou número inventado.
7. Entregue a amostra e diga o que ela deixa ver e o que ela não comprova.

## Custo, sem inventar

Quando perguntarem quanto saiu, a resposta vem de conta feita, nunca de impressão. Número que você não viu (token,
crédito, saldo, preço por vídeo) vira "não medi".

Cada gasto tem a sua origem e o seu jeito de conferir. Não some tudo num número só:

| gasto | de onde sai o número |
|---|---|
| serviço cobrado por uso (voz sintética, imagem ou vídeo gerado por IA) | o extrato, ou o saldo antes e depois |
| uso do plano de IA | o painel de uso do plano; sem ele aberto, "não medido" |
| compra avulsa (música, fonte, banco de imagem) | o recibo |
| render no Mac | os minutos com a máquina presa: não é de graça, custa tempo |
| trabalho de gente | as horas anotadas |

Também conta como dado de verdade quanto tempo ficou esperando render e quantas peças passaram. O que for palpite
aparece com a palavra "estimativa" do lado. Promessa do tipo "economiza 80%" ou "a assinatura dá tantos vídeos
por mês" não entra.

Um ajuste pedido não libera, sozinho, nada que cobra (API paga, crédito, compra). Se o conserto não sai sem isso,
explique o motivo, mostre o caminho sem custo que existir e como ela vê o preço antes de gastar. Teto que ela já
liberou vale até acabar, sem perguntar de novo.

## Entrega de uma revisão

Uma nota curta: os pontos corrigidos, o intervalo que você conferiu de verdade, o que ficou pendente e do que
depende, o nome da versão nova e a garantia de que a anterior continua intacta.
