# O corte

O corte é a base de tudo: se ele muda, os tempos da legenda, das animações e dos sons mudam junto. Por isso
ele vem primeiro e é revisto no estúdio antes de qualquer animação.

A regra de pausa é a do André, e vale para todo vídeo falado curto (anúncio, Reels, VSL):
- **Respiro é erro.** Nenhum silêncio de 0,25 s ou mais pode sobrar, nem entre frases nem dentro de um
  trecho. A emenda fica com uns 0,12 s de silêncio.
- **A tomada certa é a inteira e mais limpa**, quase sempre a última. A que para no meio, repete palavra
  ("eu eu"), termina em "..." ou muda de ideia sai.
- **Não corte a pessoa no meio.** Fechar o respiro não é comer o fim da palavra: o S de "demais", o E de
  "YouTube" e a sílaba final ficam.
- **Barulho na pausa sai junto** (cachorro, estalo de boca, respiração forte).
- **Nunca emende criando frase que ninguém disse**, nem tire uma ressalva ou condição ("só se", "não é
  pra todo mundo"). Em conversa, não troque a ordem das falas para fabricar uma afirmação.
- Aula longa e tutorial podem guardar um respiro curto onde ajuda a entender. Pergunte no briefing.

## 1. Transcrever

```
uv run --directory '<SKILL>' python helpers/transcribe_batch.py '<VIDEOS>' --prompt 'Hubla, Kiwify, Cakto'
uv run --directory '<SKILL>' python helpers/pack_transcripts.py --edit-dir '<EDIT>'
```

- `--prompt` leva só os nomes de marca, produto e plataforma do briefing, separados por vírgula. Sem ele o
  Whisper escreve "Cacto" em vez de Cakto e "Kiwi-Fi" em vez de Kiwify.
- Na primeira vez baixa o modelo de voz (cerca de 1,6 GB): avise que leva alguns minutos. Depois fica em
  cache (uns 3 s por minuto de áudio num Mac M; bem mais lento no Windows).
- Se um arquivo falhar, os outros seguem. Conte qual falhou e por quê.
- Dois vídeos com o mesmo nome (`video.mov` e `video.mp4`): nada é transcrito. Peça para renomear um.
- **Arquivo só no iCloud** (Mac com "Otimizar armazenamento"; comum em Mesa e Documentos): o `ffmpeg` fica parado
  minutos sem erro nele. O `transcribe_batch.py` confere a pasta antes de abrir qualquer vídeo, pede o download
  ao macOS (`brctl download`) e espera um pouco; se não chegar, para com `só no iCloud` e nada é transcrito.
  Peça para a pessoa baixar (no Finder, botão direito > Baixar Agora) ou copiar os vídeos para fora do iCloud,
  e rode de novo. Para conferir qualquer pasta antes:
  `uv run --directory '<SKILL>' python helpers/icloud.py '<VIDEOS>'`.
- **`AVISO: só N palavra(s) para X s de voz`**: o Whisper devolveu o prompt ("E aí, olha só, sabe?") ou pulou
  a fala. Acontece com trecho copiado de um vídeo maior que começa no meio de uma frase. Trecho copiado
  começa numa pausa, antes da primeira palavra de uma frase: copie de novo e transcreva. Se não for isso, rode
  `transcribe.py '<VIDEO>' --edit-dir '<EDIT>' --no-prompt --force`. Nunca escolha frases de uma transcrição
  com esse aviso.

**Ruído de fundo.** Logo depois, meça:
`uv run --directory '<SKILL>' python helpers/noise_check.py '<VIDEOS>'`
Se sair `ruído leve` ou `ruído forte` em algum vídeo, conte em uma frase e ofereça a limpeza (Leve ou Forte).
Anote no `project.md`. A limpeza não muda o corte.
Na mesma saída vem uma linha `Voz` por vídeo (`helpers/voz_check.py`): `voz SATURADA`, `possível saturação`,
`eco de sala leve` ou `eco de sala forte`. É medida, não escuta: ouça os tempos citados antes de afirmar, conte
em uma frase e anote no `project.md`. O que fazer com cada um está em `references/revisao.md`, seção Voz.
Saturação não volta: se a frase saturada importa, pergunte se dá para regravar.

## 2. Ler, olhar e escolher as frases

Leia `<EDIT>/takes_packed.md` (nunca os `transcripts/*.json`).

**Olhe cada vídeo** em 3 quadros (10%, 50% e 90% da duração):
`uv run --directory '<SKILL>' python helpers/contact_sheet.py '<VIDEOS>/IMG_5262.mov' --times 3.4 17 31 -o '<EDIT>/verify/fonte_IMG_5262.png'`
Procure legenda ou título já gravado na imagem (pergunte se quer a legenda da skill mesmo assim) e dado
pessoal na tela que precise de borrão (e-mail, CPF, telefone, cartão, conversa de outra pessoa). Anote.

**Roteiro.** Se a pessoa mandou o roteiro, ele serve para conferir palavras e a ordem, nunca para tirar
tempo. Improviso sem erro fica. Roteiro (ou um índice de arquivos) e fala discordam ("no editor" no roteiro,
"num editor" na gravação): **a fala gravada vence**. O `quote`, a legenda e o texto na tela escrevem o que
foi dito; na dúvida entre o Whisper e o ouvido, ouça o trecho e anote em `palavras-duvidosas.md`.

**Tomadas repetidas.** Transcrever o arquivo inteiro junta tomadas ("Foi assim que nós... Foi assim que
nosso"). Na dúvida, veja em pedaços menores:
`uv run --directory '<SKILL>' python helpers/pack_transcripts.py --edit-dir '<EDIT>' --silence 0.2 -o '<EDIT>/takes_fino.md'`
Palavra longa demais (uma palavra de 1,5 s) ou uma rajada de voz com uma palavra só é tomada repetida
escondida: ouça antes de decidir.

**A ordem** segue a história, não a gravação:
- Anúncio direto: GANCHO (0 a 3 s) → PROBLEMA → VIRADA → PROVA → CHAMADA.
- Tutorial: GANCHO → PASSO 1 → PASSO 2 → PASSO 3 → RESULTADO → CHAMADA.
- Depoimento ou UGC: GANCHO → CONTEXTO → EXPERIÊNCIA → RECOMENDAÇÃO → CHAMADA.
- Gancho e corpo gravados em arquivos diferentes (lote de ganchos): um gancho + o corpo inteiro.
Anúncio vertical rende bem entre 20 e 45 s. Sobrou material bom? Proponha versão curta e longa.

**Conte a estratégia** em 3 a 6 linhas: as partes em ordem, com a frase de cada uma entre aspas, o que sai e
por quê, e a duração estimada. Espere o sim só se a pessoa pediu para aprovar antes ou se você vai tirar algo
que muda o sentido. Senão, siga e mostre no estúdio.

## 3. O edl_manual.json: uma frase por trecho

Escreva `<EDIT>/edl_manual.json`, com um trecho por frase (ou por pedaço de frase quando a tomada tem erro
no meio), já na ordem final:

```json
{
  "version": 1,
  "sources": {"H01": "/caminho/absoluto/H01.MP4", "B02": "/caminho/absoluto/B02.MP4"},
  "output": {"format": "vertical", "width": 1080, "height": 1920},
  "grade": "",
  "jcut": {"lead_frames": 4, "tail_trim_frames": 2},
  "ranges": [
    {"id": "m01", "source": "H01", "start": 3.7, "end": 6.0, "beat": "GANCHO",
     "quote": "Esse é o fim dos editores de vídeo.", "reason": "última tomada inteira", "gain_db": 0},
    {"id": "m02", "source": "B02", "start": 1.1, "end": 3.75, "beat": "PROBLEMA",
     "quote": "Sabe a pior parte de ter editor?", "reason": "última tomada", "gain_db": 0}
  ]
}
```

- A chave de `sources` é o nome do arquivo sem extensão. Caminho absoluto.
- `start` um pouco antes da primeira palavra e `end` um pouco depois da última (0,1 a 0,3 s de folga em
  silêncio). Nunca deixe a folga entrar na tomada de antes ou de depois: o corte pela onda aperta o resto.
- Retomada colada ("…profissional… Que") : comece e termine no vale da onda, nunca pegue o pedaço da
  tomada anterior grudado no começo da nova. Na dúvida, veja no `timeline_view.py`:
  `uv run --directory '<SKILL>' python helpers/timeline_view.py '<VIDEOS>/IMG_5262.mov' 7.5 13`
- `"dry": false` num trecho que não pode ser partido nos respiros: vídeo tocando na tela, som saindo da
  caixinha do celular, música que faz parte da cena. As bordas dele ficam exatamente como você escreveu: o
  corte pela onda não mexe e o render não faz J-cut nele. O silêncio de dentro dele aparece na conferência como
  `fica:` e não conta como respiro.
- Tempo de leitura no fim (a última palavra é a conclusão e o texto precisa ficar na tela): use o
  `segura_fim` do `projeto.json` (`modelo-projeto/LEIA.md`), não um trecho `dry: false` sem fala.
- `beat` e `quote` aparecem nos relatórios: capriche. `gain_db` acerta o volume do trecho (passo 5).
- Vídeo deitado num corte em pé (ou o contrário): o render enquadra pelo rosto. Se errar, ponha `"focus_x"`
  (0 = esquerda, 1 = direita) ou `"focus_y"` no trecho.
- `grade` fica "" por padrão. Cor lavada ou escura: compare antes (`helpers/grade.py --candidates`).
- Conversa gravada com duas câmeras, ou com lapela: `"imagem_de": "CAM2"` no trecho mantém a voz da `source` e
  tira a imagem da CAM2 no mesmo instante. Veja "Várias câmeras", no fim.

## 4. Corte pela onda

```
uv run --directory '<SKILL>' python helpers/corte_por_onda.py '<EDIT>/edl_manual.json' -o '<EDIT>/edl.json'
```

Ele mede cada fonte na onda (volume em janelas de 10 ms, guardado em `regions/onda_<nome>.npy`) e, em cada
trecho: começa 0,06 s antes do primeiro som de verdade (estalo de boca antes da frase não conta), recua se
tiver consoante fraca no começo, parte o trecho em todo respiro de 0,22 s ou mais deixando 0,06 s de cada
lado, apara o silêncio do fim (qualquer tamanho) para 0,06 s, estica o fim enquanto uma consoante fraca
ainda soa e joga fora sobra de respiração ou estalo. O fim de um trecho nunca passa do começo do próximo trecho
da mesma fonte (senão o mesmo som toca duas vezes). O limiar de pausa sai do chão de ruído de cada fonte; a
primeira linha da saída mostra quanto ficou. Trecho `dry: false` sai como `inteiro: ... bordas como escritas`.

Leia a saída:
- `aviso: ... está em cima de fala; pode cortar palavra`: a borda que você escreveu no `edl_manual.json` cai
  no meio de uma palavra. Mova para a pausa mais perto (termine a palavra ou tire a palavra inteira) e rode
  de novo.
- Sobrou respiro (a conferência do passo 6 mostra): rode com `--limiar` uns 3 dB mais alto que o da saída
  (ex.: de -38 para `--limiar -35`) ou parta o trecho ali no `edl_manual.json`. Se ainda sobrar, vá até o limiar
  que o `respiros.py` mede (em geral -29): ele é o juiz do portão, então um corte que fecha mais baixo que ele
  deixa respiro que o portão reprova.
- Mic baixo (gravação do OBS, lapela longe) pode comer fim de palavra fraca. Se a conferência mostrar palavra
  mordida, rode com `--limiar` uns 4 dB mais baixo que o da saída (ex.: `--limiar -44`).
- Nunca edite o `edl.json` à mão: ele é refeito a cada rodada. Mudanças vão no `edl_manual.json`.

## 5. Volume da voz

`uv run --directory '<SKILL>' python helpers/voice_levels.py --edl '<EDIT>/edl.json'`
Trecho 5 dB ou mais fora da referência vem com `gain_db final X`: ponha o X no trecho do `edl_manual.json`
e rode o passo 4 de novo.

## 6. Renderizar e conferir

```
uv run --directory '<SKILL>' python helpers/render_cut.py '<EDIT>/edl.json'
uv run --directory '<SKILL>' python helpers/conferir_corte.py '<EDIT>' --prompt 'Hubla, Kiwify, Cakto'
```

O render do corte leva uns 30 s por minuto. O `conferir_corte.py` transcreve o `cut.mp4` e roda ao mesmo
tempo a medida dos respiros (`respiros.py`), o `verify_cut.py` e a folha das junções. No fim ele imprime a
**fala do corte, trecho a trecho**: o `quote` que você escreveu e, embaixo, o que o corte diz, em frases curtas
com o tempo de começo no corte. Trecho com palavras diferentes do quote vem marcado `≠ confira: falta "..."`,
`a mais "..."`. A mesma fala fica em `<EDIT>/fala_do_corte.md` (ou
`uv run --directory '<SKILL>' python helpers/pack_transcripts.py --edit-dir '<EDIT>' --corte`). Leia tudo:
- palavra estranha no fim ou no começo de um trecho ("matrão" no lugar de "material") é borda mordendo a
  palavra: volte ao passo 3 e dê folga naquela borda;
- palavra repetida ou "eco" numa emenda é resto da tomada anterior: mova a borda para o vale da onda;
- `≠` só porque o quote foi escrito de outro jeito (número por extenso, muleta que saiu): tudo bem, siga;
- a transcrição do corte marca começo de palavra até 0,3 s adiantado. Tempo que dispara animação, som ou
  corte na palavra vem da onda:
  `uv run --directory '<SKILL>' python helpers/palavras_na_onda.py '<EDIT>' [--palavra 'éééé']`
  mostra, para cada palavra que mudou, o tempo medido na onda (`pausa` é preciso; `vale` é aproximado, uns
  0,03 s; `whisper` quer dizer que não havia marca na onda) e grava `<EDIT>/palavras_onda.json`. No
  `projeto.json`, ponha esse tempo em segundos (`"em": 3.42`) no lugar do nome da palavra (`"em": "éééé"`,
  que usa o tempo do Whisper). Refaça a cada corte novo.
- Cada render do corte muda um pouco a transcrição ("o tudo" vira "o" e "tudo", "ééé" vira "éééé"). Cena
  própria que monta uma linha por palavra tem que aguentar uma palavra a mais ou a menos.

Avisos do verify que pedem ação: `EDGE-IN-SPEECH` e `EDGE-IN-WORD` (palavra cortada até provar o contrário),
`CLICK` (mova a borda 0,02 a 0,05 s para dentro da pausa), `REPEAT` (dois trechos repetem a fala),
`LOW-LEVEL`/`HIGH-LEVEL` (use o `gain_db` sugerido), `BLACK`, `SILENT`, `SOURCE-MISSING`. O `DEAD-AIR` não
deveria aparecer depois do corte pela onda; se aparecer, o trecho tinha `"dry": false` ou o limiar ficou
baixo demais. Refaça até sair limpo, no máximo 3 voltas; o que ficar, explique para a pessoa. O
`verify_cut.py --aprovar` é do preview antigo: aqui não se usa. A aprovação do corte é a pessoa dizer no chat
(passo 7).

**Respiros:** a parte `Respiros que sobraram` lista todo silêncio de 0,25 s ou mais no corte, com o tempo. É a
primeira coisa que a pessoa nota. Tem que sair vazia; o que está dentro de um trecho `dry: false` sai em
linha própria, `fica: ...`, e não conta. Para medir qualquer vídeo pronto:
`uv run --directory '<SKILL>' python helpers/respiros.py '<VIDEO>'`.

## 7. Mostrar no estúdio e aprovar

Meça onde está o rosto em cada trecho do corte (o projeto enquadra por isso; sem ele, enquadra pelo meio do
quadro):
`uv run --directory '<SKILL>' python helpers/face_layout.py --edit-dir '<EDIT>'`

Crie o projeto do vídeo, se ainda não existe (copia o modelo e anota a skill e o corte), e abra o corte no
estúdio, só com o vídeo e a legenda simples:

```
python3 '<SKILL>/modelo-projeto/build.py' novo '<PROJ>' --edit '<EDIT>'
cd '<PROJ>' && python3 build.py corte && python3 build.py estudio --sem-abrir
```

Como abrir o estúdio em cada app: `references/estudio.md`. Mensagem curta: duração, as partes em ordem, o que
saiu e o que você quer que ela olhe ("confere se não sobrou respiro e se nenhuma palavra ficou mordida").
- **Ajuste por tempo** ("tira de 00:12,3 a 00:12,8", "a palavra 'então' em 00:20"): o tempo é do corte. Ache
  em `cut_timeline.json` o segmento com `out_start <= t < out_end`; na fonte, o tempo é
  `src_start + (t - out_start)`. Mude o `edl_manual.json` (partir um trecho em dois para tirar uma palavra
  do meio, trocar de tomada, encurtar) e refaça os passos 4 a 7. O estúdio recarrega sozinho quando o
  `build.py corte` roda de novo.
- **"Ainda tem respiro"**: meça com `respiros.py` e rode o passo 4 com `--limiar` uns 3 dB mais alto e, se ainda sobrar, no limiar que o `respiros.py` mostra (em geral -29).
- **Aprovação** é ela dizer no chat que aprovou ("aprovado", "pode seguir"). Anote no `project.md` com a
  data e a nota dela. Se ela já tinha autorizado fazer tudo sem parar, este é um teste interno: siga para a
  amostra e mostre o corte junto.

Depois do corte aprovado, qualquer mudança de fala muda os tempos: refaça `transcribe.py` do `cut.mp4`
(o `conferir_corte.py` já faz), o `palavras_na_onda.py` se algum tempo veio dele, e o `build.py` do projeto, e
confira se as animações continuam na palavra.

## Várias câmeras (conversa, entrevista, lapela)

Duas ou mais câmeras (ou uma câmera e o gravador da lapela) gravaram a mesma conversa ao mesmo tempo. Cada trecho
tem uma voz só (`source`) e a imagem pode vir de outra câmera no mesmo instante (`imagem_de`).

**1. Sincronizar.** Com as câmeras (e a lapela) em `sources` do `edl_manual.json`:

```
uv run --directory '<SKILL>' python helpers/sincronizar_cameras.py --edl '<EDIT>/edl_manual.json' --principal CAM1 --prova
```

Sem EDL ainda: `sincronizar_cameras.py '<VIDEOS>/CAM1.MP4' '<VIDEOS>/CAM2.MP4' -o '<EDIT>/sincronia.json' --prova`
(`LAPELA='<VIDEOS>/lapela.wav'` dá o nome da fonte; sem nome, vale o nome do arquivo sem extensão, como em
`sources`). A principal é a de melhor som, a lapela se houver. Sai `<EDIT>/sincronia.json`, com o `deslocamento`
de cada fonte em segundos (positivo: ela começou a gravar antes) e a confiança. Leia a saída:
- `confiança boa`: siga. `confiança media`: confira a prova com cuidado. `confiança fraca` (sai com código 1): não
  use. Ou não é a mesma conversa, ou o som de uma é quase só ruído (câmera longe, vento). Ache uma palma no vídeo
  e escreva o `deslocamento` à mão no JSON.
- `deriva de relógio`: as câmeras contam o tempo um pouco diferente (numa conversa longa, dezenas de ms no fim).
  O render corrige pela `escala`; confira a prova no começo e no fim (`--em <segundo da principal>`).
- `não é uma reta`: a câmera parou, perdeu quadros ou o arquivo foi dividido. Sincronize cada pedaço como uma
  fonte à parte.
- `outro deslocamento quase tão bom`: música ou som repetitivo por baixo. Confira no olho.
- Fonte sem áudio: pela onda não dá. Sincronize no olho e escreva o deslocamento.

**2. Conferir no olho.** Abra `<EDIT>/verify/sincronia_CAM2.png`: 5 quadros seguidos de cada câmera no instante da
batida mais forte, a principal em cima. Numa palma, batida na mesa ou claquete, a mão fecha no mesmo quadro nas
duas linhas. O `sincronia_CAM2.mp4` põe as duas lado a lado com o som da principal (boca e som juntos). Sem batida
no vídeo, use `--em` num "p" ou "b" bem marcado. Se a linha de baixo fecha um quadro depois, ponha
`"ajuste_s": 0.033` nessa fonte do `sincronia.json` (um quadro a 30 fps; um quadro antes, `-0.033`). O ajuste fica
guardado quando a sincronia é refeita. A lapela não tem imagem: confira de ouvido, no corte.

**3. Escrever os trechos.** `source` é de onde vem a voz; `imagem_de` é a câmera que aparece:

```json
"sources": {"CAM1": "/v/CAM1.MP4", "CAM2": "/v/CAM2.MP4", "LAPELA": "/v/lapela.wav"},
"ranges": [
  {"id": "m01", "source": "LAPELA", "start": 12.40, "end": 16.10, "imagem_de": "CAM1", "quote": "..."},
  {"id": "m02", "source": "LAPELA", "start": 16.10, "end": 18.30, "imagem_de": "CAM2", "quote": "..."},
  {"id": "m03", "source": "LAPELA", "start": 19.00, "end": 23.50, "imagem_de": "CAM1", "quote": "..."}
]
```

- **Troque de câmera só numa fala ou numa reação de verdade:** quando a outra pessoa começa a falar, quando ela
  reage (ri, se surpreende, discorda) ou quando só a outra câmera mostra o que está sendo dito. Nunca a cada
  palavra, nem a cada frase só para dar ritmo. Cada câmera fica na tela pelo menos uns 2 s, fora a reação curta.
- **Trocar no meio de uma fala** (m01 para m02 acima): dois trechos colados (o `end` de um igual ao `start` do
  outro), mesma `source`, mesmo `gain_db`; só o `imagem_de` muda. O corte pela onda mede os dois como um trecho só
  e parte na troca (sai `troca de câmera: ... a voz segue`); o render toca a voz direto, sem J-cut e sem fade ali,
  e troca a imagem no quadro do tempo escrito (sai `voz continua do anterior`). Com `gain_db` diferente, a voz
  leva um corte de 30 ms na troca (aviso). Troca escrita numa pausa de 0,22 s ou mais vira junção normal: a pausa
  sai, como em qualquer corte. Pedaço de menos de 0,3 s de uma câmera vai para a vizinha.
- Uma voz por vez. Com duas lapelas, `source` é a lapela de quem fala; quando os dois falam juntos, use o som da
  câmera que pega os dois.
- Lapela sem vídeo só entra com `imagem_de` em todos os trechos dela.
- `focus_x`/`focus_y` valem para a imagem que o trecho mostra. O recorte da pessoa (tela verde) sai da câmera
  certa de cada trecho.
- Sincronia com outro nome: `"sincronia": "sincronia_tarde.json"` no topo do EDL (padrão: `<EDIT>/sincronia.json`).

**4. Conferir.** Passos 4 a 7 como sempre, e mais:
- `a CAM2 não tem imagem de ... a ... s`: ela começou depois ou parou antes. Use outra câmera nesse trecho.
- `mediu CAM2 em outro arquivo`: o vídeo foi trocado ou reexportado. Rode a sincronia de novo.
- Na folha das junções (`verify/contact_cut.png`) a troca aparece de um quadro para o outro; na fala do corte, a
  frase segue inteira através da troca (nada repetido, nada faltando).
- Olhe a boca logo depois de cada troca: atrasada ou adiantada é sincronia fora. Corrija o `ajuste_s` daquela
  câmera e renderize de novo.
