Communitygithub.com

Istok-Media-Tool/subtitle

Queima legendas/legendas estilizadas e legíveis em um vídeo usando um arquivo ASS real e o renderizador libass do ffmpeg (não gambiarras drawtext). Use esta skill sempre que o usuário quiser adicionar ou queimar legendas/titlovi/captions em um vídeo, quiser sobreposição de texto sincronizada com a fala, ou pedir para mudar fonte/tamanho/posição/quebra de linha de uma legenda existente em um vídeo legendado – mesmo que ele não diga "ASS" ou "libass" explicitamente.

O que é subtitle?

Esta skill fornece um fluxo de trabalho profissional e repetível para queimar legendas estilizadas em vídeos usando o renderizador libass do ffmpeg e arquivos ASS reais, evitando as limitações de filtros drawtext ou legendas quebradas automaticamente. Ela resolve problemas comuns como a falta de suporte a libass no ffmpeg padrão do Homebrew, quebras de linha que respeitam pausas naturais da fala (em vez de contagem de caracteres), margens adequadas para evitar cortes em plataformas como Instagram, e um sistema de ancoragem no topo que impede que as legendas "pulem" verticalmente quando o número de linhas muda. A skill é ideal para desenvolvedores e criadores de conteúdo que precisam de controle preciso sobre a aparência e o comportamento das legendas, e é compatível com qualquer ambiente que tenha ffmpeg com libass (usando `ffmpeg-full` no macOS). O processo inclui verificar a instalação do ffmpeg, gerar o arquivo ASS com um template de estilo testado, quebrar linhas manualmente nos pontos de pausa da fala, verificar a largura contra as margens reais, renderizar frames de teste rápidos antes da queima final, e aplicar correções iterativas editando diretamente o arquivo ASS.

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

Perguntar na sua IA favorita

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

Documentação

Queima de legendas

Por que esta skill existe

Conseguir que as legendas realmente fiquem corretas exigiu várias rodadas de correção em um projeto real, cada uma valendo a pena para não ter que reaprender do zero:

  1. O ffmpeg padrão do Homebrew do sistema não tem nenhum filtro drawtext/subtitles (não foi compilado com libass/freetype) – queimar legendas estilizadas reais precisa de uma compilação diferente do ffmpeg, não de uma gambiarra.
  2. Quebrar linhas de legenda para equilibrar a contagem de caracteres produz nonsense – uma quebra de linha deve cair onde uma pessoa naturalmente pausaria ao falar a frase, nunca no meio de uma expressão.
  3. Linhas muito próximas das bordas esquerda/direita do quadro podem ser cortadas pelo próprio corte do Instagram após a postagem – é necessária uma margem real, não apenas "cabe no quadro".
  4. O tamanho da fonte e a posição vertical são subjetivos por projeto e vale a pena confirmar, mas verifique-os visualmente antes de uma renderização completa de qualquer forma – uma verificação de quadro congelado detecta estouro/corte em segundos, uma re-renderização completa não.
  5. As legendas não devem se mover entre as telas. A primeira versão as ancorava na PARTE INFERIOR (Alinhamento 2, MarginV 500), então uma legenda de 3 linhas começava mais alto do que uma de 2 ou 1 linha, e as legendas que sobrepunham a anterior por alguns centésimos de segundo eram empurradas para cima pela prevenção de colisão do libass durante toda a sua duração – o usuário via os títulos "subindo e descendo". Regra fixa: ancorar no TOPO (Alinhamento 8, MarginV = y da primeira linha; 1215 em 1080x1920 mantém a linha mais baixa de uma legenda de 3 linhas onde a âncora inferior antiga a colocava) para que a primeira linha de cada legenda fique no mesmo lugar e as linhas extras cresçam para baixo, e nunca permita que dois eventos de Diálogo se sobreponham no tempo. Além disso, deixe um breve momento VAZIO entre legendas consecutivas: termine cada evento 0,12s (3 quadros a 25fps) antes do próximo começar. O usuário pediu isso explicitamente – o breve branco diz ao cérebro que a legenda mudou; legendas consecutivas são lidas como o mesmo texto piscando. Encurte o Fim da legenda anterior, nunca atrase o próximo Início (este está sincronizado com a fala).
  6. O texto da legenda não precisa ser uma transcrição literal – corrija problemas de gramática/palavras (uma palavra ouvida errada, uma construção estranha) e remova ou adicione palavras que o usuário pedir para alterar, da mesma forma que faria ao copiar-editar texto que qualquer um lerá.

Passo 1: Certifique-se de que o ffmpeg pode realmente queimar legendas

ffmpeg -hide_banner -filters 2>&1 | grep -E "subtitles|^\s*\S*\s+ass\b"

Se nada corresponder, o ffmpeg regular não pode fazer isso. Instale a compilação completa:

brew install ffmpeg-full

ffmpeg-full é keg-only (não vinculado simbolicamente ao PATH) – use-o explicitamente para a etapa de queima de legendas: /opt/homebrew/opt/ffmpeg-full/bin/ffmpeg.

Pegadinha que realmente aconteceu: instalar o ffmpeg-full puxou um x265 compartilhado mais novo e quebrou o ffmpeg/ffprobe regular no PATH (erro dyld "Library not loaded: libx265.*.dylib"), porque eles estavam vinculados à versão mais antiga. Imediatamente após instalar o ffmpeg-full, execute:

ffmpeg -version && ffprobe -version

Se algum deles der erro, corrija com brew reinstall ffmpeg (a fórmula regular, não -full) e verifique novamente se ambos os comandos funcionam antes de continuar – não presuma que o ffmpeg regular sobreviveu à instalação intacto.

Passo 2: Obtenha o texto exato da legenda e o timing

Se o vídeo veio do fluxo de corte da skill básica, você já sabe o texto exato mantido por segmento e seus carimbos de data/hora originais – mas uma vez que os segmentos são cortados e concatenados, o tempo é reiniciado para a própria linha do tempo do novo vídeo. Calcule a posição de cada segmento no vídeo FINAL somando as durações reais dos clipes renderizados em ordem:

ffprobe -v error -show_entries format=duration -of csv=p=0 clip_01.mp4

... e acumule. Dentro da janela de cada segmento, recue ligeiramente do início/fim do próprio clipe para corresponder a onde a fala realmente começa/termina (se os clipes carregam pré/pós-preenchimento da etapa de corte, subtraia-o novamente) para que as legendas não apareçam/desapareçam em silêncio morto.

Passo 3: Escreva o arquivo ASS

Use este modelo inicial – é o estilo exato confirmado em um projeto real (Inter Bold, texto branco, caixa preta opaca, posição inferior-média, margens laterais generosas):

[Script Info]
Title: <nome do vídeo> - titlovi
ScriptType: v4.00+
WrapStyle: 2
ScaledBorderAndShadow: yes
PlayResX: 1080
PlayResY: 1920

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Inter,70,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,3,16,0,8,120,120,1215,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.15,0:00:05.93,Default,,0,0,0,,Zamisli univerzum koji postoji\Ntoliko dugo da su sve zvezde\Nodavno ugašene

Antes de queimar, faça cada evento terminar 0,12s antes do próximo começar (defina o Fim do evento anterior para o próximo Início - 0,12; nunca mova um Início) – veja a lição 5.

Ajuste PlayResX/PlayResY para corresponder à resolução real do vídeo (este exemplo é um vertical 1080x1920). WrapStyle: 2 significa que o libass NÃO quebrará o texto automaticamente – toda quebra de linha deve ser um \N explícito no campo Texto. Isso é intencional: força você a escolher cada quebra deliberadamente (veja o Passo 4) em vez de deixar o renderizador adivinhar, e também é o que detecta uma linha transbordando como uma borda cortada, em vez de uma linha extra adicionada silenciosamente.

Fonte/tamanho/cor/posição são todos padrões para COMEÇAR, não regras fixas – confirme com o usuário se este é um novo projeto, ou reutilize seus últimos valores confirmados se você já os conhece para este usuário.

Convenção de pontuação confirmada em um projeto real

Pontos finais E vírgulas finais no final da ÚLTIMA linha de uma tela de legenda são removidos (legendas não precisam de ponto final como a prosa – lê-se como poluição visual naquele tamanho). Isso se aplica mesmo quando a frase continua na próxima tela de legenda: a própria mudança de tela já é lida como a pausa, então "svoje znanje kroz praksu," é mostrado como "svoje znanje kroz praksu". O usuário pediu isso explicitamente (2026-11-05, "isto kao za tačku"). Pontos de interrogação e exclamação permanecem. Vírgulas DENTRO de uma tela (no meio da linha, ou no final de uma linha que não é a última da tela) permanecem. Aplique por padrão em todos os vídeos, incluindo a linha do segundo idioma em legendas bilíngues, e ao texto da legenda que o usuário editar no editor de plano.

Passo 4: Quebre cada linha onde uma pessoa realmente pausaria ao falar

Esta é a parte que mais vale a pena acertar e a mais fácil de errar por preguiça. Leia a frase como se estivesse dizendo em voz alta e quebre nos limites naturais de respiração/frase – limites de oração, após um pensamento completo, em vírgulas que representam uma pausa real na fala. Nunca quebre porque uma linha "precisa" ser mais curta se o ponto de quebra natural estiver em outro lugar – encontre uma quebra natural MAIS CURTA (quase sempre há mais de um ponto de pausa válido em uma frase mais longa), não force uma não natural apenas para atingir uma meta de largura.

Exemplo concreto de uma correção real – errado (balanceado por contagem de caracteres, quebra no meio da frase):

Zamisli univerzum koji postoji toliko
dugo da su sve zvezde odavno ugasene

Correto (quebra nas pausas naturais da fala):

Zamisli univerzum koji postoji
toliko dugo da su sve zvezde
odavno ugašene

1, 2 ou 3 linhas são todas aceitáveis – não há máximo fixo. O que importa é que cada quebra seja um lugar onde um falante realmente pausaria, e que cada linha caiba dentro das margens (próximo passo).

Passo 5: Verifique a largura em relação às margens laterais REAIS, não à largura bruta do quadro

MarginL/MarginR no estilo (120px cada em um quadro de 1080 de largura no modelo acima – aproximadamente 11% de cada lado) existem porque a plataforma para a qual o vídeo se destina (Instagram etc.) aplica seu próprio leve corte após o upload, e o texto muito próximo da borda bruta pode ser cortado por isso, não apenas pelo limite do quadro. Trate PlayResX - MarginL - MarginR como a largura utilizável real ao julgar se uma linha é muito longa, não a largura total do quadro.

Como WrapStyle: 2 desativa a quebra automática, uma linha que é realmente muito larga não se quebrará sozinha – ela transbordará além da margem, o que é lido como texto cortado. Detecte isso no Passo 6, antes que acabe em uma renderização completa.

Passo 6: Renderize quadros de teste antes de queimar o vídeo inteiro

Escolha um timestamp dentro de cada uma das 2 a 3 legendas mais longas/arriscadas e renderize apenas aquele quadro:

/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg -nostdin -y -i video.mp4 -ss <t> -update 1 \
  -vf "subtitles=captions.ass" -frames:v 1 check.png -loglevel error

Veja cada um para: texto transbordando além das margens, mais linhas do que o pretendido (um sinal de que uma linha é mais larga do que o esperado e algo upstream precisa de reescrita/re-quebra), e a posição vertical realmente lida como "inferior-média" em vez de muito próximo da borda inferior ou alto o suficiente para cobrir o rosto. Corrija e verifique novamente antes de prosseguir – este loop é rápido (alguns segundos por quadro) e detecta exatamente os erros que uma renderização completa perderia minutos reproduzindo.

Passo 7: Queime o vídeo final

/opt/homebrew/opt/ffmpeg-full/bin/ffmpeg -nostdin -y -i video.mp4 \
  -vf "subtitles=captions.ass" -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \
  -c:a copy output_titlovi.mp4 -loglevel error

O áudio não é tocado (-c:a copy) pois as legendas são apenas vídeo. Se a saída precisar passar por uma ferramenta com limite de tamanho de arquivo, faça uma cópia compactada secundária em vez de compactar o mestre – mesmo padrão das skills básica e zoom:

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

Iterando sobre feedback

O trabalho com legendas tende a passar por várias rodadas de pequenas correções específicas (tamanho da fonte, posição, uma palavra errada, pontuação). Cada rodada deve tocar apenas no que mudou – edite o arquivo .ass diretamente (é texto simples), execute novamente a verificação rápida de quadro do Passo 6 no que foi afetado, e então queime novamente com o Passo 7. Não há necessidade de refazer os Passos 1-2 a menos que o vídeo subjacente tenha mudado.

Corrigindo palavras erradas através da ferramenta interativa

O usuário prefere corrigir o texto da legenda ele mesmo na mesma ferramenta de editor de plano (skill plan-editor – o editor padrão do usuário): com a alternância "Izmeni tekst" toda frase se torna editável no local. Sempre que as legendas existirem, mostre essa ferramenta com --ass captions.ass --durations ... (veja a skill plan-editor) em vez de pedir correções em prosa – também quando apenas as legendas precisam de verificação e nenhum zoom é planejado (então apenas comece cada clipe amplo e deixe o plano de zoom intocado).

A resposta carrega CAPTIONS_JSON: {"<índice do diálogo>": "linha 1\nlinha 2"} com apenas as telas alteradas. Aplique:

python3 scripts/apply_caption_edits.py captions.ass caption_edits.json captions_fixed.ass

Isso altera apenas o texto (o timing e o estilo permanecem), transformando cada linha em uma quebra \N explícita. Em seguida, execute novamente a verificação de quadro do Passo 6 nas telas editadas (uma palavra mais longa pode transbordar as margens) e queime novamente com o Passo 7.

Individual skills in this repo

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

Habilidades Relacionadas