Produção de Vídeo Promocional (toolchain local)
Agente-agnóstico: escrito para qualquer agente de IA de codificação que possa ler arquivos e executar comandos de shell — instalado nativamente para Claude Code, GitHub Copilot CLI / Microsoft Scout e Google Antigravity via install.sh; utilizável a partir de Cursor, Codex, Windsurf e outros como um runbook em markdown simples.
Toolchain demonstrada pela primeira vez por Idan Shimon (Microsoft, github.com/idanshimon) — seu vídeo conceito de 58 segundos, produzido por um agente de IA usando esta pilha localmente a custo zero, é a base a partir da qual esta skill foi desenvolvida.
Não presuma um local de instalação: defina PROMO_VIDEO_SKILL_DIR como o diretório que contém este SKILL.md. Seu diretório scripts/ contém o script TTS, seu auxiliar de pronúncia de acrônimos e as dependências TTS fixadas; assets/ contém o esqueleto da composição, modelos de componentes espaciais e o esquema de registro de decisões; references/ contém a inicialização da toolchain (install.md) a ser executada uma vez por máquina.
Princípio central: o roteiro é travado primeiro; cada número downstream é DERIVADO do áudio de narração medido, nunca adivinhado.
Promocional, não explicativo: esta skill vende o PORQUÊ — público, dor, transformação, prova, um único CTA. O mecanismo ('como funciona') tem no máximo um momento de destaque, e somente quando o usuário o direciona; solicitações para ensinar como algo funciona pertencem à skill complementar explainer-video.
Perfis das ferramentas
| Ferramenta | Localização | Versão testada | Licença | Função | Invocação |
|---|---|---|---|---|---|
| OpenMontage | ~/OpenMontage | checkout de julho/2026 | AGPLv3 (apenas as ferramentas; nada é distribuído no vídeo) | Governança do pipeline: ordem das etapas, registro de decisões, gates, disciplina de auto-revisão pós-render | Leia seu AGENT_GUIDE.md + pipeline_defs/animation.yaml; você (o agente) é o orquestrador — nenhum daemon é executado |
| Kokoro | PyPI | Kokoro-82M (hexgrad), kokoro==0.9.4 | Código Apache-2.0; pesos do modelo Apache-2.0 | TTS de narração, WAV mono 24 kHz por cena | Ambiente virtual Python 3.12; KPipeline(lang_code="a"), voz af_heart, parâmetro speed |
| HyperFrames | ~/hyperframes | 0.7.57, construído a partir do código-fonte | Apache 2.0 | Composição HTML/CSS/GSAP → MP4 determinístico via Chrome headless | node ~/hyperframes/packages/cli/dist/cli.js <cmd> |
| FFmpeg | ~/FFmpeg fonte; binário em um diretório de build | git master (era N-125608) | Build GPL (libx264 habilitado) | Síntese de música (aevalsrc), codificação, QA com ffprobe, loudness, empacotamento para plataformas | Coloque o diretório de build no PATH antes dos comandos do hyperframes |
| bun | npm-global | 1.3.14 | — | Constrói o monorepo hyperframes | npm i -g bun (brew cask falhou silenciosamente uma vez) |
| Node.js | nvm | 24.x (≥22 necessário) | — | Executa a CLI do hyperframes | — |
| Python | gerenciado por uv | 3.12 | — | Inferência do Kokoro | SEMPRE um ambiente virtual (uv venv --python 3.12 .venv); limpo em black + pylint |
| espeak-ng | Homebrew ou apt | qualquer | GPL | Fallback G2P do Kokoro (obrigatório) | macOS: brew install espeak-ng; Debian/Ubuntu: sudo apt install espeak-ng |
| gsap | arquivo vendido | 3.x gsap.min.js | Licença padrão GSAP | Animação de timeline dentro da composição | Copie para video/assets/ — NUNCA um <script src> de CDN (determinismo offline) |
Verificação do ambiente (uma vez por máquina)
export FFMPEG_BUILD_DIR="${FFMPEG_BUILD_DIR:-$HOME/ffbuild}"
export HYPERFRAMES_DIR="${HYPERFRAMES_DIR:-$HOME/hyperframes}"
export PATH="$FFMPEG_BUILD_DIR:$PATH"
node "$HYPERFRAMES_DIR/packages/cli/dist/cli.js" doctor
FFmpeg, FFprobe, Node e Chrome devem todos passar antes da produção (as outras linhas do doctor são componentes opcionais que esta skill não usa). Se algum dos quatro falhar, siga $PROMO_VIDEO_SKILL_DIR/references/install.md — a inicialização completa a ser executada uma vez por máquina: pacotes do sistema (macOS/Linux), clonagem das ferramentas fonte (incluindo o checkout de referência do OpenMontage), construção do FFmpeg a partir do código-fonte com libx264, construção da CLI do HyperFrames e dicas de permissão do agente. Não carregue esse arquivo quando o doctor já passar.
Chrome: a CLI usa automaticamente um chrome-headless-shell em cache em ~/.cache/puppeteer/ (faz o download uma vez se estiver ausente). A primeira execução do TTS também baixa os pacotes Python e os pesos do Kokoro-82M. Após esses downloads únicos, síntese, validação, renderização e empacotamento têm um caminho offline.
Estrutura do projeto (criar por vídeo, repositório git próprio, commits atômicos)
<projeto>/
.venv/ # uv venv, python 3.12
.gitignore # .venv/ __pycache__/ video/snapshots/ .DS_Store
README.md # como foi feito + reprodução (escreva POR ÚLTIMO)
production/
research/research-brief.md # fatos com fontes + tabela honesta de concorrentes
proposal.md # conceito, lista de cenas, promessa de entrega, decisão de duração
script/script.md # NARRAÇÃO TRAVADA
scene_plan/scene-plan.md # recursos visuais por cena + notas de movimento
checkpoints/decision-log.json # somente anexação; (categoria, assunto) é a chave
checkpoints/self-review.md # evidências pós-render
assets/audio/ # WAVs do kokoro + durations.json
renders/ # versão master + derivadas para plataformas
youtube/ # metadata.md + thumbnail.jpg
tools/tts_generate.py # copie desta skill, edite SCENE_NARRATIONS
tools/tts_pronounce.py # de scripts/, pronúncia de acrônimos (não edite)
tools/pronunciation.local.json # opcional, léxico do projeto
video/
index.html # A composição
assets/gsap.min.js # vendido
assets/audio/ # cópias dos WAVs (caminhos relativos à composição)
Etapa 1 — Pesquisa (offline-first)
Explore o próprio repositório/README/docs do assunto — buscando resultados e evidências (números, adoção, resultados antes/depois), não arquitetura. Produza research-brief.md com:
- Bloco de posicionamento (obrigatório — o roteiro não pode ser travado sem ele). Uma frase cada: público (para quem é), dor (o que eles sentem hoje), transformação (o que muda para eles), diferenciador (o único motivo para escolher isto). Esta é a mensagem; cada etapa subsequente a serve.
- Fatos do produto, cada um verificado em relação à fonte — mantidos apenas se apoiarem um benefício (o teste do 'e daí?': o que o espectador ganha?).
- Uma tabela de concorrentes (fraqueza → como o assunto a aborda). Regra de honestidade: conceda pontos reais aos concorrentes (um ✓ na coluna deles compra credibilidade para a sua).
- Uma lista de 'afirmações liberadas para o roteiro'.
Etapa 2 — Proposta + decisões
Escreva proposal.md: reafirme o bloco de posicionamento, conceito de 5–7 cenas, alvo ≈60s, promessa de entrega ('liderado por movimento, não um slideshow'), tabela da toolchain. Registre em decision-log.json — comece a partir do esquema vazio em $PROMO_VIDEO_SKILL_DIR/assets/decision-log.json (campos: category, subject, selected, options_considered, rejected_because, confidence): pipeline_selection, render_runtime_selection (registre Remotion como a alternativa considerada — regra rígida do OpenMontage), composition_mode (atelier para trabalho principal), voice_selection, music_selection, output_profile, approval_gates e cta_selection — exatamente UMA chamada para ação (dar estrela no repositório / instalar / assistir à demonstração / visitar o site); promos morrem ao terminar com uma lista de funcionalidades em vez de um pedido. Se o briefing do usuário fixou uma escolha, marque approved_by: "user brief". Mudou uma decisão depois? ANEXE uma nova entrada com o mesmo (category, subject) — nunca edite a antiga.
Etapa 3 — Roteiro (o ponto de travamento)
~150–155 palavras ≈ 60s na velocidade 1.1 do kokoro af_heart. Arco promocional (venda o porquê): gancho de importância/dor → revelação (nome + a transformação que promete) → benefícios no mundo do espectador → prova (comparação honesta, resultados reais) → aspiração → o único CTA do registro de decisões. Funcionalidades aparecem apenas como evidência de um benefício: toda frase deve passar no teste do 'e daí?' (o que o espectador ganha?) ou ser reescrita. No máximo UM momento de destaque de como funciona, e somente quando o usuário o direciona — ensinar mecanismo é trabalho da skill explainer-video. Voz: segunda pessoa, presente do indicativo, frases curtas e incisivas ('você entrega mais rápido', não 'isso permite entregas mais rápidas'). Escreva acrônimos naturalmente ('OID', 'MCP', 'IA') tanto no roteiro quanto no texto na tela — no momento da síntese, tools/tts_pronounce.py deriva a forma falada (palavra vs letra por letra) a partir de seu léxico, e acrônimos desconhecidos são soletrados letra por letra e relatados. Classifique termos de domínio em um tools/pronunciation.local.json opcional ({"NASA": "word", "ECG": "letters", "NGINX": "engine X"}). Evite ênfase EM CAIXA ALTA: uma palavra em caixa alta é tratada como um acrônimo. Orçamento de palavras por cena ≈ sua parcela de 60s × 2,55 palavras/s.
Etapa 4 — Narração + derivação de tempo (o truque fundamental)
Crie o ambiente Python 3.12 do projeto a partir das dependências diretas fixadas fornecidas, copie o modelo genérico e edite apenas SCENE_NARRATIONS:
uv venv --clear --python 3.12 .venv
uv pip install --python .venv/bin/python \
-r "$PROMO_VIDEO_SKILL_DIR/scripts/tts-requirements.txt"
mkdir -p tools
cp "$PROMO_VIDEO_SKILL_DIR/scripts/tts_generate.py" tools/tts_generate.py
cp "$PROMO_VIDEO_SKILL_DIR/scripts/tts_pronounce.py" tools/tts_pronounce.py
PYTORCH_ENABLE_MPS_FALLBACK=1 .venv/bin/python tools/tts_generate.py
mkdir -p video/assets
curl -fL https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js \
-o video/assets/gsap.min.js
A primeira síntese baixa os pesos do Kokoro-82M (~330 MB); execuções posteriores usam o cache e podem ser offline. Ela escreve WAVs por cena + durations.json. Se a execução imprimir um AVISO de acrônimo desconhecido, classifique esses tokens em tools/pronunciation.local.json e execute novamente antes de derivar o tempo.
Trate 150–155 palavras na velocidade 1.1 como uma estimativa inicial, não uma calibração. Total >62s? Aumente a velocidade ou corte palavras, depois meça novamente.
Derive a linha do tempo (padding ≈0,3–0,5s entre cenas):
n_start[0] = 0.5 # lead-in
scene_start[i] = n_start[i] - 0.15..0.4 # visuais aparecem logo antes da locução
n_start[i+1] = n_start[i] + dur[i] + 0.3..0.35
TOTAL = último n_end + 0.6..0.9 cauda (fade para preto)
Esses números vão em TRÊS lugares que devem concordar: data-start/ data-duration da cena, valores de <audio data-start> e as constantes JS const S1..Sn da linha do tempo. Copie os WAVs para video/assets/audio/.
Etapa 5 — Trilha sonora (sem downloads)
Gere um pad sintetizado determinístico com o FFmpeg construído a partir do código-fonte. Defina TOTAL_SECONDS como o total da composição derivado; nenhuma amostra externa ou referência privada é necessária:
TOTAL_SECONDS=60.5
FADE_SECONDS=3.5
FFMPEG_BIN="${FFMPEG_BUILD_DIR:-$HOME/ffbuild}/ffmpeg"
FADE_OUT_START="$(awk -v total="$TOTAL_SECONDS" -v fade="$FADE_SECONDS" \
'BEGIN { printf "%.3f", total - fade }')"
mkdir -p production/assets/audio
"$FFMPEG_BIN" -hide_banner -y \
-f lavfi \
-i "aevalsrc=0.07*(sin(2*PI*110*t)+0.65*sin(2*PI*110.7*t)+0.45*sin(2*PI*164.81*t)+0.30*sin(2*PI*220.9*t)):s=48000:d=${TOTAL_SECONDS}" \
-af "highpass=f=55,lowpass=f=1200,afade=t=in:st=0:d=2,afade=t=out:st=${FADE_OUT_START}:d=${FADE_SECONDS},pan=stereo|c0=c0|c1=c0" \
-c:a pcm_s16le production/assets/audio/music-bed.wav
Mantenha-o esparso; mixado em data-volume="0.12..0.16" soa como atmosfera — para energia promocional, fique no topo dessa faixa, nunca acima da narração. Copie o WAV para video/assets/audio/ e use uma faixa de áudio distinta.
Etapa 6 — Criação da composição (contrato HyperFrames)
Comece a partir de $PROMO_VIDEO_SKILL_DIR/assets/composition-skeleton.html (ele incorpora todas as regras abaixo); antes de escrever HTML, leia superficialmente $HYPERFRAMES_DIR/skills/hyperframes-core/SKILL.md.
Estrutura: index.html autônomo, raiz <div data-composition-id="X" data-start="0" data-duration="TOTAL" data-width="1920" data-height="1080">, cenas como <section class="clip" data-start data-duration data-track-index="1">, UMA gsap.timeline({paused:true}) registrada sincronamente em window.__timelines["X"], posições do tween = S<i> + local_offset.
Regras que custam tempo real de depuração — obedeça de antemão:
- Cada
<video>/<audio>precisa de umidexplícito e não vazio. No HyperFrames, áudio sem um pode funcionar em uma prévia do navegador, mas renderiza SILENCIOSO (ferramentas de lint reclamam; não ignore). - Nunca coloque um
transform:inicial no CSS em um elemento animado por GSAP — usetl.fromTo(el, {y:24, opacity:0}, {y:0, opacity:1, ...}); o GSAP é dono do transform completo. (opacity: 0sozinho no CSS é aceitável para revelações apenas com.to().) - O preenchimento de fundo vai em um FILHO de sangria total (
position:absolute; inset:0), nunca na raiz (o fundo da raiz pode renderizar como preto). - Elementos de mídia são filhos diretos da raiz,
data-track-indexem faixas de áudio distintas (ex.: 7=VO, 8=música). - Sem repetições infinitas (
repeat: -1proibido — use finito, ex.: piscar de cursorrepeat: 9, yoyo: true), semDate.now/Math.random, sem buscas de rede, sem scripts de CDN. idúnico em todos os lugares; anime apenas transforms/opacidade (+strokeDashoffsetpara desenhos de linha SVG).- Elementos que aparecem no meio da cena: estado inicial oculto + tween em
S<i>+offset.fromTotemimmediateRenderpara que buscas de estado pré-tween mostrem o estado de origem — é isso que o torna seguro para busca. - Tokens de design em variáveis CSS
:root= o tema; troque ground/ink/accent + pilha de tipos para re-estilizar. Use uma fonte que o renderizador fornece (JetBrains Mono) — fontes do sistema são substituídas de qualquer forma (lint info informa o mapeamento). - Mantenha o texto secundário ≥ WCAG AA na cor de fundo —
checkFALHA na construção por contraste e imprime a cor compatível a ser usada. - Sobreposições/rolagens intencionais: marque com
data-layout-allow-overflow/-overlap/-occlusionem vez de deixar descobertas inexplicadas.
Conselho de motivos de cena: mostre o estado RESULTADO — o mundo do espectador melhorado (contraste antes/depois, a dor desaparecida, o resultado na tela). Extraia vislumbres de apoio da UI/domínio real do assunto (paleta de busca falsa, terminal, árvore, tabela) como evidência de um benefício, não um tour de mecanismo. Os modelos de arco narrativo; os motivos não devem. $PROMO_VIDEO_SKILL_DIR/assets/composition-moodboard.pdf contém exemplos de motivos de cena que funcionam; estude-os antes de escrever a composição. Cada motivo deve responder 'como isso ajuda o espectador a sentir a transformação?'.