Communitygithub.com

rocketmandrey/legendas-conversacionais-para-video

Insere legendas fixas em vídeos curtos de diálogo (Reels, Shorts, TikTok, 1:1, 16:9) a partir de marcações de tempo por palavra: cada palavra falada, uma única linha de base inferior centralizada (sem elevações ou deslocamentos laterais; sobreposições com rostos/mãos/fogo são registradas como aceitas, uma elevação opcional por colisão é registrada com motivo e px), locutores diferenciados por cor ou micro-marcador, sem efeito de letra de música, contraste WCAG verificado quadro a quadro. Gera um manifesto canônico de legendas (JSON), uma prévia com as legendas gravadas, SRT simples, ASS e WebVTT estilizados, além de um validador que mede os pixels renderizados em relação ao perfil.

O que é legendas-conversacionais-para-video?

Esta skill oferece um pipeline completo e profissional para adicionar legendas a vídeos de diálogo, como entrevistas, esquetes, Reels, Shorts e TikToks. Seu principal diferencial é a abordagem fixa e controlada: todas as legendas são ancoradas em uma única linha de base na parte inferior central da tela, eliminando oscilações e movimentos laterais que causam desconforto visual. O algoritmo segmenta o texto em blocos legíveis com base em pausas, pontuação e limites de largura, garante que nenhuma palavra seja omitida e aplica um esquema de cores para diferenciar locutores. O sistema oferece modos opcionais de elevação para evitar cobrir rostos ou objetos importantes, mas por padrão prioriza a estabilidade visual, registrando sobreposições aceitas para revisão de qualidade. A skill suporta múltiplos formatos de saída (SRT, ASS, WebVTT, vídeo com legendas gravadas) e inclui um validador rigoroso que verifica contraste, posicionamento, consistência de fonte e ausência de sobreposições com a interface da plataforma. É ideal para cenários onde a precisão textual, a legibilidade e a consistência visual são mais importantes do que efeitos dinâmicos. A skill é compatível com plataformas como Claude Code, Cursor e Codex, sendo invocada por comandos como 'adicionar legendas a um vídeo de diálogo', 'legendas como no Shorts' ou 'exportar SRT/ASS/VTT'.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add rocketmandrey/conversational-video-subtitles-skill

Perguntar na sua IA favorita

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

Documentação

Legendas conversacionais para vídeo

Visão geral

As legendas são uma camada própria: elas carregam cada palavra falada e nunca dependem do que está desenhado no quadro. Rótulos de objetos no quadro (nomes de peças, prós/contas, setas) são uma camada diferente com uma linguagem visual diferente (caixas de texto ao lado do objeto); nunca corte uma palavra das legendas porque "ela já está na tela".

Pipeline: words.json + forbid.json (caixas que as legendas não devem cobrir) + config.json + perfil → plan → manifest.json (dados canônicos) → burn (prévia com legendas gravadas = verdade visual) / export srt|ass|vtt → validate.

Funções dos formatos (não os misture):

formatocarregaNÃO carrega
manifest.jsonpalavras, tempos, layout, placement, sobreposições aceitas, elevações com motivos, verificações de render: fonte de verdade para dadospixels
.srttexto + tempopeso, cor, contorno, palavra ativa, posição. Nunca prometa estilo em SRT
.assfamília + Negrito/\b700, tamanho convertido para métricas libass, contorno, caixa de fundo, palavra ativa por intervalo de tempo, posiçãofades; precisa da fonte: subtitles=x.ass:fontsdir=fonts
.vttCSS ::cue (família, peso, contorno via text-shadow, fundo) + classes .active_<spk> / .em_<spk> / .dim / .u por intervalo de tempoposição exata; a página deve carregar a fonte com @font-face
preview.mp4 com legendas gravadastudo: a única fonte de verdade visual—

Quando usar

  • Esquetes de diálogo, entrevistas, explicadores com narrador: verticais ou quadrados, no máximo uma ou duas linhas.
  • Você tem marcações de tempo por palavra (ou áudio: STT em nuvem opcional via chaves de ambiente) e sabe onde estão rostos/mãos/objetos-chave (caixas; para cenas 3D, renderize um matte de cor sólida e crie caixas).
  • Não para vídeos de letra de música (palavras saltitantes e com escala não são suportadas propositalmente).

Algoritmo

Segmentação (chunk no perfil): quebra forçada em pausa > pause_s (0,35 s), fim de frase, mudança de locutor; quebra suave em uma vírgula quando um bloco já tem 2+ palavras; limitada por max_words, largura em pixels (max_width_frac da zona segura) e max_dur_s (~1,2 s de fala é bem legível). Um bloco nunca termina em uma palavra funcional (não, em, a, and, the…): ela passa para o próximo bloco. Uma palavra isolada junta-se ao seu vizinho da mesma frase se couber. As durações das palavras são limitadas a 2,5× a mediana para que uma palavra não se prolongue em uma pausa. Tempo: uma unidade aparece ≤ 50 ms antes de sua primeira palavra, nunca corta a palavra anterior, permanece hold_s após sua última palavra, no mínimo min_show_s.

Posicionamento — placement: {mode: fixed_bottom_center, collision_lift: false} (padrão para diálogo).

  1. Uma única linha de base para todo o vídeo: baseline = safe.bottom - baseline_margin_px (ou defina-a explicitamente). Cada unidade é alinhada pela parte inferior nesta linha de base (y + h = baseline) e centralizada no quadro (x + w/2 = L/2). Nada se move: sem elevação, sem deslocamento lateral, sem eixo x por locutor. A largura da linha é limitada para que a linha centralizada permaneça dentro da zona segura.
  2. Uma linha que cruza uma caixa proibida na linha de base permanece no lugar: a unidade recebe overlap_accepted: [classes] e a folha de QA desenha um quadro amarelo "sobreposição aceita (linha de base fixa)". Informação, não uma falha. Cruzar a interface da plataforma ainda é um erro (diminua a fonte ou divida a frase).
  3. Opção collision_lift: true (modo baseline_lift): eixo x sob o locutor (align: speaker) ou centralizado com alternativas esquerda/direita; se todo eixo x colidir, eleve em etapas inteiras de lift_step_px até ficar livre. A unidade recebe lift = {px, reason: [classes atingidas na linha de base], t0, t1}, o manifesto lista todas as elevações em collision_lifts. Sem motivo → sem elevação. Use apenas quando a solicitação pedir para manter os rostos livres a qualquer custo.
  4. Nunca descarte uma palavra.

Aparência. Contorno (outline_px) + sombra suave + um fundo claro cujo alpha é aumentado por unidade apenas o suficiente para manter a cor mais escura acesa ≥ 4,5:1 (WCAG) contra o percentil 90 dos pixels atrás dela. Locutores diferem pela cor da palavra ativa e, no modo híbrido/cartões, uma barra de 4 px na cor do locutor; sem balões de fala, sem placas de nome pesadas. Movimento calmo: apenas fades de alpha, sem pops de escala, sem deslizamentos verticais.

QA (scripts/validate.py, também executado por demo):

  • esquema: o manifesto contra templates/manifest.schema.json;
  • linhas ≤ 2;
  • posicionamento: em fixed_bottom_center a dispersão de y-baseline e de x-centre em todas as unidades é 0 (tolerância de 0,5 px) e não há elevações; em baseline_lift cada elevação está na grade, tem um motivo e a unidade realmente colide na linha de base;
  • zero sobreposições com a interface da plataforma em cada quadro; caixas proibidas: 0 em baseline_lift, relatadas como overlap_accepted (informação) em fixed_bottom_center;
  • cada palavra exatamente uma vez, em ordem, visível enquanto falada;
  • contraste ≥ 4,5 e sem oscilação, ambos medidos por quadro por burn;
  • fontes resolvidas: arquivo presente, família e estilo Pillow, peso OS/2, fc-scan, e o fontselect do libass escolhe o arquivo e peso declarados;
  • pixels: no quadro com legendas gravadas E na renderização libass do ASS, a cor ativa está presente, a parte inferior do glifo está a 3 px do layout, a largura da haste está a 1 px de um raster de referência do peso declarado e o contorno está a 1 px de outline_px.

Perfis (profiles/*.json)

perfilo que émelhor para
karaoke2–4 palavras por vez, palavra falada na cor do locutor, restante esmaecidomais estável; faixas fixas, explicadores
revealpalavras aparecem gradualmente na fala; layout fixo antecipadamente; ≤ 2 linhasnarração calma
cardsum pensamento por cartão (≤ 8 palavras, 2 linhas), painel denso, palavra-chave coloridafrases lentas e citáveis; lê antes da fala
kineticum token por vez, tipo maiorapenas momentos de destaque; pouco contexto
hybrid (padrão)≤ 3 palavras, palavras aparecem gradualmente, sublinhado sob a palavra falada, cor de ênfase, barra do locutordiálogo

Opções dos sistemas de P&D como parâmetros: tempo = chunk.max_dur_s: 1.2 + timing.min_show_s: 0.83 (ativado no híbrido); ênfase = emphasis: "energy" (z-score de sonoridade da palavra por locutor a partir de --audio), ou "keyword" com uma lista de keywords.

Comandos

python -m subkit demo examples/fixture                     # 5 perfis + hybrid_lift -> prévias, srt/ass/vtt, validation_report.{md,json}
python -m subkit plan --words W --forbid F --config C [--profile hybrid] [--audio A] -o manifest.json
python -m subkit burn manifest.json --forbid F --video in.mp4 [--audio A] -o preview.mp4 --qa qa.jpg
python -m subkit export manifest.json --format ass -o captions.ass     # srt | ass | vtt
python scripts/check_clean.py                                          # antes de um commit: sem mídia/chaves/caminhos absolutos
ELEVENLABS_API_KEY=... python -m subkit stt audio.wav -o words.json     # opcional; ou --provider groq com GROQ_API_KEY

Armadilhas

  • libass ≠ tamanho de fonte Pillow. libass dimensiona por winAscent+winDescent do OS/2 e coloca a linha de base winAscent abaixo do topo da linha; a exportação converte ambos, ou o texto ASS sai ~35% menor, mais fino e mais baixo.
  • Fontes variáveis no ASS voltam para a instância padrão (geralmente Regular). Envie uma instância estática do peso que você usa (aqui Onest-Bold.ttf, 700) e passe fontsdir.
  • ffmpeg -ss antes de -i redefine os timestamps: o filtro de legendas então renderiza t=0. Busque após -i ao gravar ASS.
  • O karaokê \k/\kf está em centissegundos e colore cada palavra falada permanentemente; para "apenas a palavra atual" emita intervalos de tempo (como a exportação faz).
  • SRT não tem estilo: se uma entrega precisar de peso ou destaque, envie ASS/VTT ou o vídeo com legendas gravadas.
  • Uma linha flutuante (eixo x por locutor, histerese, elevações "mais bonitas") parece instabilidade em diálogo. Mantenha o modo fixo padrão; em baseline_lift o validador verifica novamente se cada elevação realmente colide.
  • O contraste é julgado na cor acesa contra os 10% mais brilhantes dos pixels atrás dela, não a média: fogo, neve e realces falham nas médias.
  • Não descarte palavras já desenhadas na tela. Em vez disso, mova o rótulo desenhado para uma camada de anotação.
  • As zonas seguras em templates/safe_zones.json são aproximadas; as UIs das plataformas mudam, então verifique-as novamente antes de publicar.

Lista de verificação

  • words.json cobre toda a fala (STT em nuvem ou um alinhador forçado); locutores rotulados
  • forbid.json: rostos, mãos, fogo/acessórios por cena (+ caixas speakers para align: speaker)
  • config: tamanho, fps, safe_zone, cores dos locutores (matizes diferentes, ambos ≥ 4,5:1 no fundo)
  • plan → manifesto; leia overlap_accepted (fixo) ou collision_lifts (modo de elevação, cada um com um motivo real)
  • burn → observe a folha de QA (ciano = linha de base, amarelo = sobreposição aceita, laranja = elevação com motivo) e os quadros reais
  • validate todos APROVADOS; envie o vídeo com legendas gravadas (+ ASS/VTT se um reprodutor os renderizar; SRT apenas como texto simples)

Habilidades Relacionadas