Communitygithub.com

captions (legendas)

Esta skill deve ser usada quando o usuário pedir legendas ou texto na tela do que está sendo dito — "brenn die Untertitel ein", "mach Untertitel", "Untertitel drauf", "leg Untertitel drüber", "brenn die Subs ein", "mach die Captions", "Wort für Wort Untertitel", "Karaoke-Untertitel", "korrigier den Untertitel-Text", "burn in the captions", "add subtitles", "burn the subs", "word-by-word captions", "fix a caption typo". Abrange geração de ASS, a regra de agrupamento por largura de pixel para alemão, a tabela de fontes e a queima (burn-in) via libass.

O que é captions (legendas)?

A skill `captions` gera arquivos de legenda ASS (Advanced SubStation Alpha) a partir de transcrições em nível de palavra e as incorpora (burn-in) no vídeo renderizado final. Ela é essencial para usuários que trabalham com vídeos em alemão, pois implementa duas regras críticas que evitam falhas comuns: (1) agrupamento por orçamento de largura de pixel em vez de `max_chars`, para lidar com substantivos compostos longos típicos do alemão (como "Geschwindigkeitsbegrenzung"); (2) uso exclusivo de fontes estáticas de peso único com o nome exato `Fontname` exigido pelo ASS, evitando que fontes variáveis sejam substituídas silenciosamente por Arial durante a renderização via libass. A skill é acionada por comandos de voz ou por argumentos (`project` e `preset`), e segue um fluxo de cinco etapas: escolha do preset, geração do ASS, correção de texto via `overrides`, queima no vídeo, e verificação de qualidade (QC) que compara hashes para confirmar que a fonte correta foi carregada. Ela também valida a estrutura canônica das linhas `Format:` (23 campos para estilo e 10 para eventos) e exige que o diretório `assets/fonts/` contenha apenas fontes, sob pena de erros de carregamento. Suportada em plataformas como YouTube e mídias verticais, com presets específicos para cada caso.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/businessbaum/vedit/tree/HEAD/.agents/skills/captions

Perguntar na sua IA favorita

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

Documentação

captions

Gere um arquivo de legenda ASS a partir da transcrição em nível de palavra e incorpore-o na renderização. Argumentos: slug $project, $preset — um nome de presets/captions/*.json. Requisição completa em $ARGUMENTS. Um espaço reservado vazio $-significa que a skill foi acionada automaticamente, em vez de ser invocada por barra: pegue o projeto de PIPELINE_STATE no contexto da sessão e informe qual você escolheu. Os nomes de verbos seguem o contrato de build; python engine/ve.py --help vence.

Duas regras específicas do alemão que determinam se isto funciona

1. Nunca agrupe por max_chars

Substantivos compostos alemães são tokens únicos e indivisíveis que estouram qualquer limite de caracteres. Geschwindigkeitsbegrenzung tem 26 caracteres e ocupa aproximadamente 60% de um quadro de 1920 a 90 px. Uma regra max_chars: 22 não consegue quebrá-lo, então ou ele transborda o quadro ou o agrupador emite silenciosamente uma linha que prometeu não emitir.

Agrupe por um orçamento de largura de pixel, com uma regra de redução automática para uma única palavra que exceda o orçamento sozinha. estimate_text_width_px() é responsável pela medição; group_words() é responsável pelo orçamento. Os detalhes estão em docs/fonts.md e no docstring do módulo de engine/vedit/captions.py.

Esta é a falha de legenda alemã mais comum e é totalmente evitável.

2. A fonte deve ser um arquivo estático de peso único, nomeado pelo seu nome exato nameID 1

Fontes variáveis não podem ser selecionadas por peso a partir do ASS. InterVariable.ttf renderiza byte-idêntico ao Arial através do libass — mesmo md5 — enquanto registra que carregou o arquivo. Exit 0, sem aviso. Style: D,Inter,...,Bold=-1 resulta em DirectWrite Arial e ninguém nota até que a exportação esteja no YouTube.

Use apenas estas, com a string Fontname exata e o valor Bold:

arquivoASS FontnameBold
Inter-Regular.ttfInter0
Inter-Bold.ttfInter-1
Inter-SemiBold.ttfInter SemiBold0
Inter-ExtraBold.ttfInter ExtraBold0
Montserrat-Bold.ttfMontserrat-1
Montserrat-ExtraBold.ttfMontserrat ExtraBold0

assets/fonts/ contém apenas fontes. O libass carrega todos os arquivos que encontra lá e gera erro em qualquer outra coisa — um README.md perdido produz Error opening memory font 'README.md'. Esse diretório é uma permissão ask nas configurações exatamente por esse motivo; se um documento pertencer às fontes, ele vai em docs/fonts.md.

Etapa 1 — escolha um preset

presets/captions/*.json contém estilo, orçamento de agrupamento, posição e comportamento de destaque. Escolha por output.platform, não por gosto:

  • Plataformas verticais querem legendas grandes, centralizadas, de 1 a 3 palavras, no alto da área segura — o terço inferior fica sob a interface da própria plataforma.
  • YouTube longform quer duas linhas, ancoradas na parte inferior, dimensionadas para que um espectador de celular consiga ler.

Confirme a escolha com o usuário antes de gerar e nomeie o que muda.

Etapa 2 — gere o ASS

python engine/ve.py captions $project --preset $preset

Dois requisitos estruturais, ambos falham silenciosamente quando violados:

  • A linha Style: Format: de 23 campos V4+ (Name .. Encoding, a própria lista de campos do libass — o 24º campo, AlphaLevel, é SSA v4 e não ASS V4+) e a linha Events Format: de 10 campos devem ser canônicas, e a linha abaixo de cada uma deve ter exatamente esse número de colunas. Uma contagem incompatível faz o libass sair com exit 0, prefixar uma vírgula extra no texto renderizado e descartar silenciosamente o contorno e o negrito. Reproduzido com um Format de 5 campos contra um Dialogue de 6 campos. captions.validate_ass() verifica ambas as contagens antes de qualquer queima; não é opcional e burn_in() recusa em caso de falha.
  • O arquivo é UTF-8. Diacríticos alemães passam corretamente pelo libass quando está em UTF-8 — Größe Straße Übermäßig äöüÄÖÜß foi verificado de ponta a ponta.

O sidecar é escrito em projects/<slug>/out/captions.ass e referenciado a partir de captions.sidecar_path.

O timing vem da mesma lista de palavras de onde vieram os cortes, mapeada pelas posições da linha do tempo em _computed. As legendas são, portanto, invalidadas por qualquer alteração de corte — regenere após recortar, em vez de corrigir.

Etapa 3 — corrija o texto, não a transcrição

Números são o problema previsível. --suppress_numerals é obrigatório na transcrição, então a transcrição diz zwanzig onde o locutor disse "20". Corrija em captions.overrides[] como {word_index, text}, nunca editando a transcrição — a transcrição é a âncora para cada intervalo de palavras no projeto e reescrevê-la dessincroniza os cortes.

O mesmo para nomes próprios, nomes de produtos e abreviações do tipo GmbH. Colete-os em uma passada examinando palavras com score baixo: a pontuação de alinhamento é inútil como sinal de preenchimento, mas é um bom sinal de "o alinhador não tinha certeza sobre o que era esta palavra", que é exatamente o que um nome próprio parece.

Etapa 4 — queime (burn-in)

python engine/ve.py render $project

A queima acontece na multiplexação final, uma vez, sobre o vídeo concatenado — nunca por segmento. Uma queima por segmento re-renderiza a mesma legenda em cada fronteira e coloca uma linha parcial em cada lado de um corte.

O escape de subtitles= e fontsdir= é exato e não óbvio. Barras normais em todos os lugares, e o valor entre aspas com dois-pontos de unidade escapado uma vez:

subtitles='C\:/Users/me/.../out/captions.ass':fontsdir='C\:/Users/me/.../assets/fonts'

escape_filter_path() em vedit/ffmpeg.py é responsável por isso e retorna a string incluindo suas aspas — não adicione as suas. Aspas, não profundidade de aninhamento, é o que decide: a forma escapada uma vez sem aspas falha com No option name near '/Users/...', e apenas a forma entre aspas sobrevive a uma vírgula, ponto e vírgula ou colchete no caminho, todos legais no Windows. escape_filter_path_nested() retorna a variante duplicada sem aspas C\\:/... para quem precisar, com essa limitação documentada.

Tudo passa por uma lista de argumentos (argv) do Python. O mesmo caminho construído no Git Bash se torna C\;C:\Program Files\Git\Users\... — reproduzido ao vivo, não teórico.

Defina captions.enabled e captions.burn_in, e defina stage como "captions".

Etapa 5 — prove que a fonte realmente foi carregada

A verificação de qualidade (QC) (e) é obrigatória e esta é a única maneira de detectar fallback silencioso. Renderize um quadro de legenda com o estilo do projeto e um com um estilo de controle Arial, e afirme que os hashes diferem. Hashes idênticos significam que o libass fez fallback e a exportação será enviada com o tipo de letra errado.

Em seguida, confirme também: contagens de campos nas linhas Format:, nenhuma linha excedendo o orçamento de pixel, e nenhuma legenda cruzando um limite de segmento de forma que deixe um fragmento.

Modos de falha

SintomaCausaCorreção
As legendas parecem ArialFonte variável, ou Fontname erradoArquivo estático + string nameID 1 exata; execute QC (e)
Vírgula extra no texto, sem contornoLinha Format: malformadaEmita linhas canônicas de 24/10 campos
Error opening memory font 'README.md'Arquivo não-fonte em assets/fonts/Apenas fontes nesse diretório
Uma palavra longa sai do quadroAgrupado por max_charsOrçamento de largura de pixel + redução automática de palavra única
No option name near '/Users/...'fontsdir escapado uma vez sem aspasDuplique a barra invertida antes dos dois-pontos
As legendas escrevem números por extenso--suppress_numerals, conforme projetadoEntradas em captions.overrides
Legendas dessincronizam após um recorteASS desatualizadoRegenerar; nunca corrigir timings manualmente

Individual skills in this repo

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

businessbaum/vedit

This skill should be used when the user asks for mechanical tightening of material that is already selected - "mach den Rohschnitt", "bau den Rohschnitt", "schneid die Ähms raus", "nimm die Füllwörter raus", "entferne die Pausen", "kürz die Pausen", "schneid die Versprecher raus", "mach den Radioschnitt", "straff das", "build the rough cut", "cut the fillers", "remove the ums", "tighten the pauses", "trim the dead air", "cut the retakes", "radio edit". Turns kept story blocks into enabled segments in project.json. Does not decide what the video is about - that is cut-story.

businessbaum/vedit

This skill should be used when the user asks to decide what the video is actually about before any mechanical cutting - "bau die Story", "was soll ins Video", "was kann raus", "welche Themen bleiben", "kürz das auf 6 Minuten", "mach daraus ein kurzes Video", "wo ist der Hook", "sortier die Themen", "worum geht es hier eigentlich", "build the story", "decide what stays", "structure this", "cut this down to N minutes", "find the hook", "reorder the topics". This is the CONTENT-selection pass that writes the "story" block in project.json; it runs after transcribe and before cut-rough.

businessbaum/vedit

This skill should be used when the user asks for the finished video file - "exportier das", "render das fertig", "mach das final", "renderte das Video", "mach den Endexport", "bau das Video zusammen", "mach es fertig für YouTube", "gib mir die MP4", "export the final", "render the final cut", "make the master", "export for TikTok", "give me the mp4". Covers segment rendering with the cache, guarded concat, the one-pass audio, loudness normalisation and the single mux.

Habilidades Relacionadas