Communitygithub.com

Istok-Media-Tool/basic

Corta uma gravação bruta de vídeo ou áudio em plano-sequência (um take contínuo de cabeça falante, cheio de repetições, falsos começos e erros) para uma edição final limpa que segue o roteiro pretendido, usando transcrição Whisper cruzada com detecção de silêncio para pontos de corte precisos por quadro, montada com ffmpeg.

O que é basic?

Esta skill é especializada em editar gravações brutas de plano-sequência (talking head com múltiplas tentativas e pausas) em vídeos editados e limpos que seguem um roteiro. Ela resolve o problema central de que as transcrições do Whisper sozinhas não fornecem timestamps de corte confiáveis em gravações com pausas e repetições. A skill usa detecção de silêncio amostra-preciso para identificar rajadas de fala reais e, em seguida, transcreve cada rajada isoladamente para revelar falsos começos e tomadas alternativas que a transcrição de arquivo completo oculta. O fluxo de trabalho inclui um editor de plano que lida com tomadas duplicadas (modo Dublovi) e produz um plano de edição a ser confirmado pelo usuário antes de cortar. O uso de ffmpeg com pós-processamento de cortes apertados garante ritmo justo e verificação de cada renderização com um verificador de integridade que detecta quadros congelados, desvios de áudio e problemas de sincronia. A skill suporta macOS e Linux e pode ser integrada com outras skills (zoom, legendas e editor de plano) para um fluxo completo de produção de vídeo.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Istok-Media-Tool/video-edit-skills/tree/HEAD/video-edit/skills/basic

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Edição básica de vídeo (cortar takes brutos para corresponder a um roteiro)

Por que esta skill existe

Um único take contínuo cheio de retomadas é enganosamente difícil de cortar corretamente apenas a partir de uma transcrição. Na primeira vez que este fluxo de trabalho foi feito à mão, confiar nos timestamps de segmento do próprio Whisper de uma única passagem de arquivo completo produziu um plano de corte onde uma frase parecia levar 22 segundos para ser dita. Na verdade, levou 5,75 segundos; o Whisper havia mesclado 16 segundos de silêncio e dois falsos começos naquele único segmento. Confiar nessa transcrição para cortes teria enviado um vídeo cheio de silêncio morto.

Guarde essa lição: o objetivo deste fluxo de trabalho são pontos de corte precisos, e o Whisper sozinho não oferece isso de forma confiável em gravações com pausas, retomadas ou linhas repetidas. Tudo abaixo existe para compensar isso.

Fluxo de trabalho

1. Analisar a fonte

Examine o vídeo em busca de informações básicas antes de fazer qualquer outra coisa:

ffprobe -v error -show_entries format=duration,size -show_entries stream=width,height,codec_name,codec_type,avg_frame_rate -of default=noprint_wrappers=0 "<video>"

Extraia áudio mono de 16 kHz para transcrição (o Whisper prefere isso, e é muito menor/mais rápido de processar do que o vídeo completo):

ffmpeg -y -i "<video>" -vn -ac 1 -ar 16000 audio.wav -loglevel error

Se o usuário forneceu um arquivo de roteiro/cenário (txt, rtf, docx, etc.), leia-o agora para ter o conteúdo pretendido para comparar. Um .rtf geralmente precisa de textutil -convert txt -stdout arquivo.rtf (macOS) para obter texto limpo em vez de marcação RTF bruta.

2. Obter uma visão geral aproximada com uma transcrição de primeira passagem

whisper audio.wav --language <lang> --model medium --output_format txt --output_format srt --output_dir .

Use isso APENAS para ter uma noção do conteúdo geral e aproximadamente até onde as diferentes linhas estão na gravação. Não use seus horários de início/fim de segmento como pontos de corte reais ainda - veja "Por que esta skill existe" acima. Se a gravação for longa, isso pode ser executado em paralelo (ou ignorado em favor da) etapa 3.

3. Encontrar limites reais da fala com detecção de silêncio

Este é o passo que realmente importa. Execute:

python3 scripts/detect_bursts.py audio.wav > bursts.txt

Isso fornece "rajadas" de fala com precisão de amostra (o complemento do silêncio detectado), filtradas para excluir blips menores que 0,5s que são quase sempre ruído de respiração, não palavras. Confie nesses timestamps, não nos do Whisper.

Verificação de sanidade de escala: a fala natural tem cerca de 2-3 palavras por segundo. Se a duração de uma rajada parecer desproporcionalmente grande em relação à quantidade de texto que você esperaria caber nela, algo está errado.

4. Transcrever cada rajada isoladamente

python3 scripts/transcribe_bursts.py audio.wav bursts.txt --language <lang> --model medium

Isso carrega o modelo Whisper uma vez e, em seguida, transcreve cada rajada como seu próprio pequeno clipe com condition_on_previous_text=False e temperature=0. Essa combinação é o que realmente revela retomadas ocultas: uma rajada que uma passagem de arquivo completo rotula como uma linha limpa pode conter, uma vez isolada, um falso começo imediatamente seguido pela entrega real - totalmente invisível na transcrição de primeira passagem, que silenciosamente mesclou ou suprimiu a repetição.

Espere que uma gravação com retomadas notórias produza mais rajadas do que linhas de roteiro - geralmente vários fragmentos de falso começo por linha, especialmente na linha com que o falante mais lutou. Isso é normal; é exatamente para isso que serve esta etapa.

5. Construir o plano de edição - e obter confirmação antes de cortar qualquer coisa

Agrupe as rajadas transcritas por qual linha/ideia do roteiro elas pertencem. Para cada linha, você normalmente verá um de:

  • Uma rajada limpa e completa - fácil, esse é o take.
  • Um falso começo seguido por uma rajada completa - use o completo, descarte o fragmento.
  • Vários takes completos com as mesmas palavras - são duplicatas de desempenho genuínas; você não pode julgar entrega/emoção apenas por uma transcrição, então corte com um padrão razoável (por exemplo, a tentativa posterior, já que as pessoas geralmente se estabelecem em um take) e deixe o usuário escolher no modo Dublovi (abaixo).
  • Vários takes completos com palavras genuinamente diferentes (o falante improvisou de forma diferente a cada vez) - esta é uma escolha de conteúdo real, não apenas uma de desempenho. Apresente as alternativas ao usuário explicitamente em vez de escolher silenciosamente - eles podem preferir sua própria frase improvisada ao roteirizado, ou vice-versa.

Para ambos os tipos de takes duplicados, o usuário decide junto com você, assistindo a eles: os takes vão para o modo Dublovi do editor de plano (dois cortes lado a lado, esquerda / direita, "Ovaj je bolji"), em TODO vídeo. Não descreva takes no chat e não pergunte "dubl 1 ili 2" lá. Corte com sua escolha padrão, liste cada duplicata em takes_src.json (skill editor de plano, "Dublovi") e abra o editor; troque o take no corte se eles escolherem outro.

Em seguida, escreva o plano completo como uma mensagem para o usuário: o texto exato sendo mantido para cada linha, seu intervalo de timestamp exato e o que está sendo cortado e por que (silêncio morto, falsos começos, takes alternativos descartados). Aguarde confirmação explícita antes de executar qualquer comando de corte. Isso não é uma formalidade - editar um vídeo parece destrutivo e é difícil verificar sem assistir à saída, então o usuário precisa ver o plano para detectar problemas (take errado escolhido, uma linha que não soa natural costurada ao lado de outra) antes que o tempo de renderização seja gasto nisso, não depois.

Se o usuário perguntar sobre duração total, apenas some as durações dos segmentos mantidos

  • mas avise que a duração final real será um pouco maior depois que o preenchimento entre os cortes for adicionado (etapa 6).

6. Cortar e montar

Escreva as seleções confirmadas (um par início<TAB>fim por linha, na ordem de saída) em um arquivo, então:

scripts/cut_and_concat.sh "<source_video>" selections.txt final_output.mp4

Leia o bloco de comentários no topo de cut_and_concat.sh antes de executá-lo manualmente ou adaptá-lo - ele documenta pegadinhas (ffmpeg roubando stdin de um loop bash, fades comendo a última sílaba de uma palavra, por que cada clipe precisa de re-codificação individual antes da concatenação) que são fáceis de reintroduzir silenciosamente se reescritos do zero em vez de usar o script como está.

Padrões: 0,15s de preenchimento antes de cada corte, 0,35s após cada corte, um fade-in de 5 ms apenas (sem fade-out), h264 re-codificado (crf 18) + aac 192k por clipe, cada clipe cortado para uma contagem exata de quadros com seu áudio cortado para o mesmo comprimento, unidos com o filtro de concatenação ffmpeg (nunca o demuxer de concatenação - veja "Verificar cada renderização" abaixo). Substitua via variáveis de ambiente PRE_PAD, POST_PAD, CRF, FADE_IN se um corte específico precisar de tratamento diferente (por exemplo, uma linha com muito ruído de respiração no final pode precisar de mais POST_PAD).

Se a última letra de uma frase ainda soar cortada após uma primeira passagem, isso quase sempre significa que o POST_PAD está muito apertado para aquele corte específico (a consoante final do falante se estende além do limite que o detector de silêncio capturou) - aumente-o para aquele segmento em vez de recorrer a um fade, que apenas torna um corte apertado pior, não melhor.

Ritmo apertado - o padrão do usuário

O preenchimento padrão (0,15s / 0,35s) deixa uma pausa de ~0,5s em cada corte. O usuário descobriu que isso quebrava o ritmo ("mora biti sve zbijenije i tečnije"), então, para seus vídeos, aperte cada corte antes de montar:

python3 scripts/tighten_cuts.py audio.wav selections.txt tight.txt
PRE_PAD=0.04 POST_PAD=0.08 scripts/cut_and_concat.sh "<source_video>" tight.txt final_output.mp4 <abs workdir>

Isso move cada borda para onde a fala realmente começa / termina (-42 dB) e divide qualquer pausa interna maior que 0,30s. Em um rolo real, isso reduziu de 1:48 para 1:41 com cada palavra intacta. Depois, verifique transcrevendo o resultado: cada palavra do texto mantido ainda deve estar lá. Os tempos de qualquer coisa colocada no corte antigo (close-ups, legendas, título) devem ser re-mapeados através do tempo de origem, não deslocados por uma constante. Quando duas partes cortadas separadamente são unidas (por exemplo, principal + encerramento), pegue o ponto de junção da contagem real de quadros da primeira parte (nb_read_frames / fps), não da sua duração de contêiner: em um rolo real, a diferença de 0,04s deixou um quadro solta do plano geral entre um close-up e o encerramento, e a primeira legenda do encerramento apareceu um quadro antes. Uma sobreposição que termina na junção deve ir até aquele quadro.

Verificar cada renderização - antes de enviar qualquer coisa

Em um rolo real, o usuário notou um quadro solto do plano geral em 1:05 ("to ne sme da nam se dešava"). A causa real era maior: o antigo cut_and_concat.sh unia clipes com o demuxer de concatenação, que coloca cada clipe por sua duração de contêiner e preenchimento/priming AAC, não por seus quadros reais. Isso deixou um buraco de 80 ms (2 quadros) em 10 dos cortes - um quadro congelado na tela após a re-codificação - e o áudio lentamente se adiantou em relação à imagem, então cada sobreposição e legenda com base nele atrasou um quadro ou dois. O script corrigido (pegadinha #4 em seu cabeçalho) corta cada clipe para uma contagem exata de quadros, preenche/corta seu áudio para exatamente esse comprimento e une com o filtro de concatenação.

Regras que se seguem disso:

  • Execute o verificador em TODA renderização (corte, zoom, legendado, final) antes de enviá-lo ao usuário:

    python3 scripts/check_video.py final.mp4 --ass captions.ass --edges edges.txt --cuts joins.txt
    

    joins.txt = cada junção da lista de corte na linha do tempo final (somas crescentes das durs.txt de cada parte, mais os limites das partes). Sempre passe isso: um corte brusco dentro do mesmo enquadramento pontua muito abaixo do limite de cena, então sem ele o verificador perdeu uma mudança de zoom chegando 3 quadros antes de uma junção em um rolo real (1:15) - um flash de 3 quadros que o usuário viu.

    Ele relata buracos de timestamp, quadros congelados solitários, comprimento de áudio vs vídeo, tomadas com apenas alguns quadros de duração, legendas mais curtas que 0,4s ou começando / terminando 1-3 quadros de um corte de imagem, e bordas de sobreposição (edges.txt: um início ou fim de close-up / B-roll por linha, segundos) a 2-5 quadros de um corte. Exit 1 = algo foi encontrado: corrija e renderize novamente; apenas "OK - nothing found" é enviado.

  • Tire cada posição de clipe de contagens de quadros (nb_frames / fps, ou o durs.txt que o script escreve), nunca de format=duration.

  • Quando o corte muda, mova close-ups, legendas e o título através do tempo de ORIGEM (linha do tempo antiga -> fonte -> nova linha do tempo), nunca por uma constante.

  • Uma sobreposição que termina em um corte (um close-up antes do encerramento) vai exatamente até o quadro daquele corte; uma legenda termina com seu clipe.

  • Encaixe toda mudança de zoom, fim de close-up e borda de legenda que cair dentro de 6 quadros de uma junção DENTRO DESSA junção (um início de close-up apenas quando a junção está logo antes dele). Duas mudanças de imagem a alguns quadros de distância sempre são lidas como uma falha; em um quadro, são lidas como um corte.

  • Dê ao último segmento um pequeno final (~0,5s) para que a última legenda e palavra não sejam cortadas.

7. Entregar

A saída mestre de cut_and_concat.sh é de qualidade total (re-codificada em crf 18). Se precisar passar por uma ferramenta com limite de tamanho de arquivo, faça uma cópia comprimida secundária para esse fim, em vez de comprimir o mestre:

ffmpeg -nostdin -y -i final_output.mp4 -c:v libx264 -preset slow -crf 24 -pix_fmt yuv420p -c:a aac -b:a 128k final_output_share.mp4 -loglevel error

Se o usuário também quiser zoom dinâmico/cortes com aproximação para dinamismo, veja a skill zoom - ela pode pegar a saída desta skill e re-cortá-la, ou (mais eficientemente) você pode combinar ambas em uma passagem escrevendo as cadeias de filtro por segmento da skill zoom diretamente na terceira coluna de selections.txt antes de executar cut_and_concat.sh uma vez. A rotação para vertical, se necessária, é tratada separadamente (não faz parte de nenhuma skill) - pergunte ao usuário, pois nem sempre é desejada.

Notas de plataforma

  • O macOS não tem timeout GNU por padrão - não envolva chamadas ffmpeg em um; elas são rápidas para clipes curtos, e o próprio tratamento de background/timeout da ferramenta de chamada cobre a passagem longa de arquivo completo na etapa 2.
  • Sempre que você escrever um loop while read ...; done < file você mesmo (em vez de usar os scripts incluídos) e chamar ffmpeg dentro dele, adicione -nostdin à chamada ffmpeg e redirecione seu stdin de /dev/null - caso contrário, o ffmpeg consome linhas destinadas ao read do loop, corrompendo qual timestamp se aplica a qual iteração sem nenhum erro óbvio.

Após o corte: zoom e legendas passam pelo editor de plano

Quando o vídeo cortado também receber zoom/close-ups (skill zoom) e/ou legendas (skill de legenda), o padrão do usuário é a skill plan-editor: uma ferramenta interativa de clicar-em-uma-palavra onde eles decidem o zoom com você e corrigem o texto da legenda nas mesmas frases ("Izmeni tekst"). Mantenha o texto mantido por clipe do plano desta skill - ele se torna o sentences.txt daquela ferramenta (uma linha por clipe cortado, mesma ordem que selections.txt).

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas