Corte de Vídeo
Transforme um vídeo longo não editado em um primeiro corte compacto e assistível — decisivo, consciente do conteúdo e autoverificado. O humano (você, o modelo) toma a decisão editorial do QUE manter; os scripts incluídos cuidam da precisão mecânica (alinhamento de bordas, remoção de silêncio, renderização segura de memória, autoverificação).
Quando usar
- Um vídeo bruto/não editado (URL do YouTube ou arquivo local) precisa se tornar um corte mais enxuto.
- O pedido é "reduzir pela metade", "cortar as partes chatas", "fazer um primeiro corte/rough cut".
- NÃO serve para: microcirurgia de vícios de linguagem palavra por palavra, montagem de múltiplas câmeras (B-roll), ajuste de cor/áudio — esses são passos posteriores de polimento (P1/P2). Esta skill é P0: validar o diagnóstico + produzir um corte sólido.
Dependências
/video-understand é um pré-requisito. Execute-o primeiro e use sua mídia, transcrição, análise e linha do tempo validados, em vez de recriá-los nesta skill. Antes de começar, verifique se está instalado. Se não estiver, avise o usuário que este pré-requisito está faltando e pare antes de processar a mídia.
Ative qualquer ambiente que tenha estes itens (NÃO assuma um nome de conda env específico):
yt-dlp(download),ffmpeg/ffprobe(corte/renderização/inspeção) no PATH- Python com
faster-whisper(CPU funciona:device=cpu, compute_type=int8) Verifique primeiro:yt-dlp --version,ffmpeg -version,python -c "import faster_whisper".
Fluxo de trabalho do protocolo de projeto
Use estes arquivos de projeto duráveis:
work/cut/edit-plan.json # decisões de manter/descartar escritas à mão
work/timeline.json # intervalos de precisão gerados + mapeamento fonte/programa
review/01-cut/cut-summary.md
review/01-cut/timeline-map.png
review/01-cut/boundary-review.mp4
review/01-cut/full-proxy.mp4 # opcional: revisão de ritmo do programa completo
Cada decisão canônica tem um id estável, action (keep ou drop), start_s, end_s, reason e evidence_refs semânticos/de transcrição. Mantenha as decisões de keep em ordem cronológica; a V1 não suporta clipes de origem reordenados ou duplicados.
Reutilize a camada de evidência compartilhada em vez de transcrever ou diagnosticar novamente:
python skills/video-understand/scripts/transcribe.py work/cache/audio16k.wav work/understand/transcript
python skills/video-understand/scripts/analyze.py work/understand/transcript.json work/understand/analysis.json
Os scripts de precisão existentes aceitam o plano canônico. Expanda os limites seguros para palavras, atribua opcionalmente velocidade variável linear e, em seguida, gere a linha do tempo compartilhada:
python skills/video-cut/scripts/build_edit.py work/cut/edit-plan.json work/understand/transcript.json work/cache/edit-final.json
python skills/video-cut/scripts/assign_speed.py work/cache/edit-final.json work/understand/transcript.json
python skills/video-understand/scripts/build_timeline.py work/cache/edit-final.json work/timeline.json --fps-num SOURCE_FPS_NUM --fps-den SOURCE_FPS_DEN
Use o FPS racional exato de work/understand/media.json; nunca arredonde 30000/1001 para 30/1. Revise timeline-map.png e o curto boundary-review.mp4. Gere full-proxy.mp4 apenas quando o ritmo do programa completo precisar ser revisado. Escreva o resultado em cut-summary.md e aponte o relatório de verificação da operação para esse arquivo.
Registre a contribuição da linha do tempo com sua entrada de origem necessária, atualize o inteiro da operação revision e defina based_on para a revisão exata de entendimento consumida:
"render": {
"kind": "timeline-transform",
"input": "../input/original-video.mp4",
"plan": "timeline.json"
}
O renderizador de entrega compartilhado codifica a sequência ativa após todas as verificações de revisão passarem.
Fluxo de trabalho de compatibilidade independente
Os arquivos e comandos legados abaixo permanecem suportados durante a migração. build_edit.py aceita tanto edit_coarse.json quanto decisions[] canônicos; cut_render.py aceita tanto edit_final.json quanto timeline.json; o transcribe.py desta skill é um wrapper de compatibilidade em torno de ../video-understand/scripts/transcribe.py.
Mantenha todos os intermediários em work/, entregue na raiz do projeto:
work/source.mp4 # original baixado
work/audio16k.wav # áudio mono 16k para whisper
work/transcript.json/.srt # timestamps no nível da palavra
work/analysis.json # diagnóstico (silêncios/pausas preenchidas/sequências longas)
work/edit_coarse.json # *** SUA decisão editorial (escrita à mão) ***
work/edit_final.json # segmentos de renderização expandidos e precisos (gerados; carrega velocidade por segmento)
work/selfcheck/ # re-transcrição do corte + quadros de junção
first_cut.mp4 # O ENTREGÁVEL
first_cut_report.md # relatório de edição + autoverificação
O pipeline (6 passos)
1. Download
Maior qualidade acessível + áudio. O player cliente padrão frequentemente falha (não disponível / DRM / login necessário). A alternativa confiável é o cliente android:
yt-dlp --extractor-args "youtube:player_client=android" \
-o work/source.mp4 "<URL>"
Se apenas um 360p muxado (formato 18) estiver acessível sem login, aceite-o e documente a restrição — HD DASH é frequentemente bloqueado por DRM/login. Em seguida, extraia o áudio:
ffmpeg -y -i work/source.mp4 -ac 1 -ar 16000 work/audio16k.wav
2. Transcrição (nível da palavra)
python scripts/transcribe.py work/audio16k.wav work/transcript [base.en]
Produz work/transcript.json (segmentos + início/fim por palavra) e .srt.
faster-whisper, CPU/int8, VAD ativado, word_timestamps=True.
Não inglês: passe um modelo multilíngue + --lang, por exemplo,
python scripts/transcribe.py work/audio16k.wav work/transcript medium --lang zh
(os modelos .en são apenas para inglês). Verifique o idioma em uma amostra curta primeiro.
Nota: Whisper remove a maioria dos "hum/ah" vocalizados — eles NÃO são mensuráveis na transcrição, portanto a detecção de pausas preenchidas deve combinar a pesquisa lexical de palavras abaixo com a análise de lacunas de silêncio, não confiar apenas no texto da transcrição.
3. Diagnosticar ("ver" + "ler")
python scripts/analyze.py work/transcript.json work/analysis.json
Levanta: silêncio inicial/final, silêncios entre palavras ≥0,8s (com a palavra que seguem), contagem de pausas preenchidas, palavras por minuto, taxa de fala, repetições quase duplicadas, monólogos >40s (candidatos a divagação).
Para ver a imagem, amostre um quadro a cada ~2s em uma folha de contato e leia você mesmo; confirme a estrutura da cena / procure quadros pretos ou defeituosos:
ffmpeg -y -i work/source.mp4 -vf "fps=1/2,scale=240:-1,tile=10x10" -frames:v 1 work/contact.png
ffmpeg -i work/source.mp4 -vf "blackdetect=d=0.1:pic_th=0.98" -an -f null -
Para um único take de um entrevistado estático, não há cortes de cena — espere isso e observe que os cortes abruptos posteriores são normais, não falhas.
4. Decidir o corte — escreva edit_coarse.json À MÃO
Este é o núcleo editorial. Leia a transcrição + análise, depois escreva um plano bruto de blocos manter e intervalos descartar que cubram toda a linha do tempo sem lacunas/sobreposições, cada um com in, out e um reason de uma linha. Comprima o preâmbulo lento em algumas pepitas de alto valor; mantenha o conteúdo substancial como blocos grandes; descarte tangentes/divagações claras. Preserve a ordem cronológica; a linha do tempo canônica V1 rejeita reordenações. Mire em aproximadamente metade do tempo de execução.
- Especificação do formato + um exemplo real trabalhado:
reference/edit_coarse.schema.md. - Alinhe cada limite de bloco a um final de frase/cláusula — nunca inicie/para
no meio de uma frase ou em uma gagueira. Use:
Aceita tanto planospython scripts/inspect_bounds.py work/edit_coarse.json work/transcript.jsonkeep[]legados quantodecisions[]canônicos e imprime as palavras que cruzam cada limite para que você possa ajustá-los para bordas limpas.
5. Expandir → velocidade automática → renderizar
Transforme blocos brutos em segmentos de renderização justos e sem silêncio morto, normalize o ritmo da fala e renderize:
python scripts/build_edit.py work/edit_coarse.json work/transcript.json work/edit_final.json 1.5
python scripts/assign_speed.py work/edit_final.json work/transcript.json # adiciona velocidade por segmento
python scripts/cut_render.py work/edit_final.json work/source.mp4 first_cut.mp4
build_edit.pydivide cada bloco em pausas internas > SPLIT_GAP (4º argumento, ex. 1,5s) e corta cada limite DENTRO do silêncio (LEAD 0,10s / TRAIL 0,30s) para que nenhuma palavra seja cortada e o silêncio morto seja recuperado. SPLIT_GAP menor = mais apertado, mas mais entrecortado; para um locutor lento e deliberado, prefira ~1,5, não 0,6 (evita microcortes bruscos).assign_speed.py(velocidade variável automática) — agrupa segmentos de renderização pordecision_ref, mede o ritmo de fala de cada decisão de manutenção escrita à mão e escreve um fatorspeedcompartilhado mais oout_durresultante de cada segmento de volta emedit_final.json. O ritmo já dentro da faixa confortável permanece em 1,0; o ritmo fora da faixa move-se apenas para a borda mais próxima da faixa morta. Blocos de decisão adjacentes diferem em no máximo 0,08x para evitar junções "instáveis". Altera apenas o ritmo — nunca qual conteúdo é mantido. Leia a tabela impressa antes de renderizar:- Métrica: palavras/min (inglês) — muda automaticamente para caracteres/min para CJK (zh/ja/ko).
- Os padrões são pontos de partida da primeira execução, não constantes validadas: faixa morta 145–185 WPM e velocidade limitada a 1.0–1.3. Isso é apenas aumento de velocidade: blocos de decisão lentos podem ser apertados, enquanto a entrega naturalmente rápida permanece em 1.0. Blocos de decisão curtos (<3,5s / <6 palavras) são muito ruidosos para medir e herdam o fator global.
- A taxa é medida após a recuperação do silêncio morto, portanto lê mais alto que o WPM bruto e está
mais próxima da taxa de articulação. A revisão da tabela impressa é crucial: examine-a
e ajuste
deadbandpara o locutor antes de renderizar, não confie cegamente nos padrões. - Controles (k=v):
mode=segment|global|off(padrãosegment;global=um fator para o vídeo inteiro,off=desabilitado),deadband=lo,hi,min_speed=/max_speed=,min_seg=/min_words=,lang=auto|en|cjk. Para pular o recurso completamente, simplesmente não execute esta etapa —cut_render.pytrata umaspeedausente como 1.0.
cut_render.pyusa N entradas separadas com busca + filtro concat, única re-codificação. Isso é obrigatório: cortar N vezes de UMA entrada decodificada faz o ffmpeg armazenar em buffer todo o fluxo decodificado (dezenas de GB para um vídeo longo). Vejareference/pitfalls.md. Quando os segmentos carregam umaspeed, ele re-temporiza cada entrada (setptsvídeo /atempoáudio) e normaliza fps/timebase/taxa de amostragem por entrada antes do concat para que A/V permaneça sincronizado; sem velocidade, ele recai no gráfico original exato.- Comparação final:
video-edit-comparelê a linha do tempo canônica mais a entrega final real. Ele projeta os pixels finais de volta ao tempo de origem, estica clipes com velocidade variável para sua duração de origem e mostra intervalos descartados como preto.
6. Autoverificação — não declare concluído até que isso passe
# verificação de regressão de precisão do protocolo (execute a partir da raiz do repositório)
python skills/video-cut/scripts/check_project_protocol.py
# re-transcreva o corte final
ffmpeg -y -i first_cut.mp4 -ac 1 -ar 16000 work/selfcheck/cut_audio16k.wav
python scripts/transcribe.py work/selfcheck/cut_audio16k.wav work/selfcheck/cut_transcript
# verificação de conteúdo + pausas preenchidas + limites
python scripts/verify_cut.py work/edit_final.json work/transcript.json work/selfcheck/cut_transcript.json
# verificação de quadros / preto / junção (os tempos de junção usam out_dur pós-velocidade, portanto permanecem precisos)
python scripts/selfcheck_frames.py work/edit_final.json first_cut.mp4 work/selfcheck
# sincronia A/V + sem silêncio morto residual
ffprobe -v error -select_streams v:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffprobe -v error -select_streams a:0 -show_entries stream=duration -of csv=p=0 first_cut.mp4
ffmpeg -i first_cut.mp4 -af silencedetect=noise=-32dB:d=1.5 -f null -
Limites de aprovação:
| Verificação | Condição de aprovação |
|---|---|
| Quadros pretos | nenhum |
| Quadros de junção | limpos — apenas saltos naturais de cabeça, sem preto/congelado/rasgado |
| Sincronia A/V | duração do vídeo vs áudio dentro de ~1 quadro (≈30ms); total ≈ final_duration_s_after_speed quando a velocidade variável está ativa |
| Silêncio morto ≥1,5s | 0 (a taxa de fala deve aumentar marcadamente) |
| Segmento descartado/duplicado | nenhum — todo o conteúdo principal presente (difflib ~0.90 é OK; índice |
| menor geralmente é ruído de transcrição de segunda passagem em fala murmurante, não perda — verifique | |
| pela presença de palavras-chave antes de culpar o corte) | |
| Meias-frases cortadas | nenhuma — limites caem em silêncio / no final de frases |
Ressalva honesta sobre pausas preenchidas: o corte no nível do parágrafo NÃO alcançará "quase nenhuma pausa preenchida". A densidade por minuto pode até aumentar porque o conteúdo mantido é coloquial. Relate isso honestamente; a remoção de pausas preenchidas no nível da palavra pertence ao P1/P2 (e tornaria um único take de um entrevistado visivelmente entrecortado).
Finalmente
Escreva first_cut_report.md: resumo da fonte, o que foi cortado e por quê, a tabela de autoverificação,
e quaisquer ressalvas honestas (limite de qualidade devido à fonte 360p, densidade de pausas preenchidas, etc.).
Se a velocidade variável foi aplicada, divulgue-a: quantos segmentos foram re-temporizados e a faixa de velocidade
(da tabela de assign_speed.py / speed_params), para que o leitor saiba que o corte não é
1:1 em tempo real. Observe também que o valor de pausas preenchidas por minuto do verify_cut.py lê ligeiramente
mais alto após o aumento de velocidade (mesmas palavras, minutos mais curtos) — um artefato de relatório, não pausas preenchidas adicionadas.
Princípios-chave (os conquistados com esforço)
- Você decide o conteúdo; os scripts decidem os quadros. Escreva
edit_coarse.jsonà mão; deixebuild_edit.pyfazer a precisão eassign_speed.pyfazer o ritmo. - A velocidade toca o ritmo, nunca o conteúdo. A velocidade variável automática dá a todos os segmentos de renderização de
um
decision_refa mesma velocidade, move o ritmo fora da faixa apenas para a borda mais próxima da faixa morta, limita decisões adjacentes a um delta de 0,08x e limita a saída a 1.0–1.3.atempopreserva o tom. Revise a tabela;mode=offou pule a etapa para desabilitar. - Nunca corte uma palavra. Os limites sempre caem dentro do silêncio.
- Renderização segura de memória apenas (N entradas com busca + concat). Nunca faça múltiplos cortes em uma entrada.
- Seja decisivo. Faça chamadas razoáveis e prossiga; não pergunte demais.
- A autoverificação é inegociável. Re-transcreva + verifique quadros antes de declarar concluído.
- Veja
reference/pitfalls.mdpara a lista completa de armadilhas e correções.