Communitygithub.com

GetOccasion/demo-video-skill

Produz um vídeo de demonstração narrado, legendado e com gravação de tela de um recurso de aplicativo web – interações de navegador roteirizadas gravadas com agent-browser, narração local com Kokoro e legendas incorporadas com ffmpeg. Use sempre que o usuário quiser um vídeo de demonstração, passo a passo, screencast, tour de funcionalidades, explicador ou demonstração gravada de algo em seu aplicativo, quiser "mostrar" um recurso para a equipe, clientes ou partes interessadas como vídeo, ou pedir narração ou legendas em uma gravação – mesmo que não diga "skill" ou nomeie nenhuma dessas ferramentas.

O que é demo-video-skill?

Esta skill transforma qualquer recurso de aplicativo web em um vídeo de demonstração curto e profissional, combinando gravação de tela real (com agent-browser), narração gerada localmente (com Kokoro) e legendas sincronizadas (compostas com ffmpeg e Pillow). O princípio central é que a narração é o relógio: o áudio é gerado primeiro, medindo a duração de cada cena, e as ações do navegador são sincronizadas com a palavra que as descreve – evitando o problema de deriva temporal que ocorre ao sincronizar narração com uma gravação pré-existente. A skill suporta múltiplos takes (desktop, mobile, encerramento), legendas queimadas ou em trilha separada, e revisão visual com grade de quadros. É ideal para desenvolvedores que precisam criar demonstrações para clientes, equipes ou stakeholders sem depender de serviços externos de TTS ou gravação.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add GetOccasion/demo-video-skill

Perguntar na sua IA favorita

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

Documentação

Vídeo de demonstração

Transforma um recurso em um vídeo curto: interações reais em um navegador real, narração que explica o que está na tela e legendas. Tudo roda localmente: agent-browser grava, Kokoro fala, ffmpeg e Pillow montam.

A ideia que faz funcionar: a narração é o relógio. Renderize a narração primeiro, meça cada cena e sincronize cada ação do navegador com a palavra que a descreve. Nunca cronometre a narração com base em uma gravação; o ritmo de uma gravação varia, o de um WAV não. A gravação em si não espera a narração: as ações são executadas uma após a outra, e a montagem congela o quadro onde a narração precisar de mais tempo.

Ferramentas

Os scripts desta skill ficam em scripts/ no diretório base dela, o mostrado como "Base directory for this skill" quando carregada; o cenário de exemplo fica em references/ lá. Cada comando shell começa do zero, então uma variável exportada em um não existe no próximo: comece cada comando que precisar delas com export SCRIPTS=<base directory>/scripts DEMO=<demo directory>; (abaixo), como aqui para a verificação:

export SCRIPTS=<base directory>/scripts; bash $SCRIPTS/preflight.sh

Verifica se agent-browser e um Chrome para ele, ffmpeg/ffprobe com libx264, e o ambiente Python e modelo do Kokoro estão disponíveis: ou pré-instalados (DEMO_VIDEO_VENV está definida), ou uv (que executa Kokoro e Pillow em Python 3.12 independente do Python do sistema) mais acesso ao Hugging Face. Se sair com código não-zero, pare: informe ao usuário o que está faltando e como instalar, e não inicie o script, os dados ou a gravação. Não substitua outro mecanismo de TTS, gravador ou vídeo sem áudio sem perguntar – o resultado não seria o que foi solicitado. Seus avisos (sem espeak-ng, sem fonte do sistema) não bloqueiam; mencione-os.

$SCRIPTS/python executa os scripts Python abaixo no ambiente que o uv.lock da skill fixa; os cenários também leem $SCRIPTS. Mantenha todos os arquivos de trabalho em um diretório de demonstração (DEMO, um caminho absoluto) no scratchpad, fora do repositório. Se você puder escrever apenas dentro do repositório, ou o vídeo for para ser anexado a um pull request a partir dele, use uma pasta nele que o git ignore: tmp/demo-video/<nome> quando git check-ignore -q tmp/demo-video tiver sucesso, caso contrário outra pasta que confirme ser ignorada, para que nada dentro dela possa ser commitado. Não edite .gitignore para isso sem perguntar.

Fluxo de trabalho

1. Entenda o recurso e escolha a história

Leia o diff do branch, o plano ou descrição do PR e o texto voltado ao usuário do recurso (arquivos de internacionalização, dicas de ferramentas, texto de ajuda). As dicas de ferramentas geralmente definem as colunas e opções melhor do que qualquer outra coisa – a narração deve concordar com elas palavra por palavra sobre o que um número significa.

Escolha um protagonista (um negócio fictício que realmente usaria o recurso) e uma pergunta que o recurso responde para eles. Uma demonstração que percorre um cenário concreto da pergunta à resposta é melhor que um tour de todos os controles.

Pergunte ao usuário apenas o que altera o vídeo: público (clientes ou equipe), duração alvo, voz e se eles querem ler o roteiro antes de qualquer gravação. Padrões: clientes, ~3 minutos, af_heart. Pule as perguntas que eles já responderam ou tornaram irrelevantes – alguém que pediu tudo de ponta a ponta sem verificações, ou que claramente não está presente para responder, recebe os padrões e nenhuma revisão de roteiro.

2. Crie dados de demonstração que contam a história

Dados vazios ou aleatórios fazem um recurso parecer quebrado. Construa dados cujos números tornem cada ponto na tela óbvio: casos extremos visíveis na primeira tela, totais que mudam visivelmente quando uma opção é alterada.

  • Isole. Crie um banco de dados separado e execute o aplicativo em sua própria porta; nunca reutilize os dados de desenvolvimento do usuário. Procure pelas próprias convenções do projeto primeiro (CLAUDE.md, guias sobre worktrees e servidores de desenvolvimento, diretórios de seed). Se o projeto tiver uma estrutura de seed, escreva uma seed de demonstração reutilizável e por indústria (ex.: db/seeds/demos/bike_shop.rb) em vez de um script único – a próxima demonstração então não custa nada. Pergunte antes de commitar.
  • Datas relativas a hoje, para que a demonstração nunca fique desatualizada.
  • História precisa de viagem no tempo. Aplicativos se recusam a criar pedidos ou reservas no passado; crie cada registro no momento em que realmente teria sido criado (ex.: travel_to do Rails). Cuidado com estados que ignoram o relógio viajado: valores memoizados em uma instância carregada antes da viagem e colunas de status computadas na criação. Recarregue e atualize-os dentro do bloco de viagem.
  • Tire um snapshot do esquema vazio (ex.: createdb -T) para que o re-semeio após uma correção seja uma cópia, não uma migração.
  • Em seguida, abra a página e verifique cada número que você planeja mencionar.

3. Escreva o roteiro

Escreva DEMO/scenes.json: [{"id": "01_intro", "text": "..."}, ...]. Uma cena por ideia, 10–15 cenas. Os IDs classificam em ordem de reprodução.

  • Duração: Kokoro fala a ~150 palavras por minuto, e cada cena adiciona ~1–3 s de ações e pausas. Para 3 minutos, escreva cerca de 450 palavras; 420 palavras renderam 2:40.
  • Diga o que o espectador está vendo, na ordem em que vê. Nomeie os controles pelo rótulo na tela.
  • Escreva para o ouvido: frases curtas, números escritos por extenso onde lerem estranho ("vinte e cinco porcento").
  • Siga as regras de vocabulário do projeto (glossários, palavras reservadas) na narração como no código.
  • Nomeie o que está na tela, quando está na tela. Não nomeie um valor de filtro, opção de menu ou registro antes que o espectador possa vê-lo ("tudo que Sam vendeu" enquanto a lista de funcionários ainda está fechada). Coloque as palavras de abertura do menu antes do nome, para que o cenário possa abrir o menu primeiro e posicionar o ponteiro na opção enquanto ela é falada.
  • Comece com a própria pergunta do recurso. A primeira frase deve ser uma que apenas este recurso responda. "Quanto vendemos este mês?" também descreve um relatório vizinho; "Qual loja vendeu mais kits iniciais?" não. Verifique os recursos irmãos do aplicativo (outros relatórios, outras páginas de configurações) antes de decidir.
  • Escreva para o público declarado. Uma demonstração para cliente mostra o que eles podem fazer; uma demonstração interna para marketing ou gerência de contas também cobre como decide as coisas, como ativá-lo, seus limites e o que dizer a um cliente que perguntar.

Se o usuário pediu para revisar o roteiro, pare aqui e mostre-o antes de renderizar o áudio: primeiro um resumo curto (uma linha por cena: o que é mostrado e a única afirmação que faz), mais quaisquer afirmações sobre as quais você não tem certeza, com o texto completo abaixo para quem quiser. Mudar uma frase depois significa re-renderizar aquela cena, re-sincronizar o cenário e regravar o take, então este é o momento mais barato para mudar a história. Caso contrário, continue; mencione no relatório final que o roteiro está em DEMO/scenes.json.

4. Renderize a narração

export SCRIPTS=… DEMO=…; bash $SCRIPTS/python $SCRIPTS/narrate.py $DEMO

Escreve tts/<id>.wav e tts/<id>.words.json com timestamps por palavra, e imprime a duração de cada cena. Re-renderize cenas individuais com --only <id> .... Vozes: af_heart (mais natural EUA), am_michael, bf_emma (britânico). A primeira letra da voz escolhe o idioma.

5. Execute um teste de cada interação

Antes de gravar, execute cada passo uma vez com agent-browser e tire uma captura de tela. Isso encontra o que vai quebrar na câmera: rotas erradas, elementos cobertos por sobreposições, menus que enviam ao fechar, um layout que muda quando um selo aparece. Referências de snapshot (@e12) mudam a cada carregamento de página, então use seletores CSS estáveis (IDs, dom_ids, name=) no cenário. Prefira-os a demo.click_role: os helpers deslizam o ponteiro para o elemento de um seletor antes de agir, mas agent-browser não dá posição para um role, então um clique por role faz o ponteiro pular.

Oculte sobreposições apenas de desenvolvimento (profiling, barras de depuração) e limpe flashes únicos como "Logado como…" carregando a página duas vezes antes do take começar (demo.prime(url)).

6. Escreva o cenário e grave

Um cenário é um script Python curto em DEMO usando SCRIPTS/recorder.py. Comece a partir de references/example_scenario.py. Sincronize ações a palavras com s.at("word"). Pontuação é seu próprio token, então sincronize "Outubro" e não "Outubro,", e use nth= quando uma palavra aparecer duas vezes.

Mova-se apenas através dos helpers. Comandos raw de ponteiro do agent-browser teletransportam o ponteiro e uma roda de mouse salta a página, o que parece um corte na câmera. demo.move, demo.hover e demo.click deslizam o ponteiro até lá e retornam quando ele chega; demo.scroll anima a rolagem e retorna quando a página para. Assim, cada movimento está na gravação por completo, antes da próxima pausa congelar um quadro.

Deixe o espectador ler cada menu. Uma lista que abre e fecha em um segundo não mostra nada. Use os helpers, que demoram na lista aberta e descansam o ponteiro na opção antes de escolhê-la:

  • demo.pick(open_trigger, option_selector) para menus HTML personalizados.
  • Passe scene=s, cue="<palavra>" para qualquer helper para posicionar o ponteiro na opção exatamente quando o narrador a nomeia; o menu abre cerca de dois segundos antes. Isso precisa de dois segundos de narração antes da palavra de sincronia na mesma cena, sem ação lenta no meio – a montagem imprime warning: ... runs ...s late quando não consegue, e a correção está no roteiro ("A terceira opção naquele menu divide…" em vez de "Ou divide…") ou em mover a ação lenta para a cena anterior.
  • demo.pick_native(select_selector, label) para um <select> nativo. Uma gravação headless nunca desenha o popup de um select nativo, então isso desenha uma lista substituta a partir das opções reais do select, aponta para a opção e a seleciona.

Uma viewport por take. Um take cuja viewport muda no meio da gravação sai distorcido. Grave um segmento de telefone como seu próprio take (demo.viewport(390, 844)); a montagem o encaixa em uma tela. Um take curto de encerramento depois retorna ao desktop.

Grave cada take: export SCRIPTS=… DEMO=…; python3 -I $DEMO/scenario.py desktop (e phone, outro, …). A gravação não tem tempo: s.at() e s.until() não dormem, mas registram um ponto de pausa, então o take dura apenas o tempo de suas ações (um vídeo de 2 minutos grava em menos de um minuto) e um seletor quebrado ou uma palavra de sincronia ausente da narração falha em segundos. Isso torna desnecessário um teste separado; itere diretamente nos takes. Cada take escreve <take>.mp4 e <take>.holds.json; a montagem congela o quadro em cada ponto de pausa até que a narração alcance sua palavra de sincronia. Se um passo falhar no meio do take, corrija o cenário e execute o take inteiro novamente; as pausas devem vir de uma execução limpa.

Toda ação deve deixar a página estabilizada antes da próxima sincronia, já que o último quadro é o que é congelado: mantenha o time.sleep após um recarregamento ou um menu que envie, ou um quadro congelado mostrará uma página meio carregada. Qualquer coisa que roda em seu próprio relógio (um toast que desaparece, um spinner, uma contagem regressiva) só avança enquanto o cenário está agindo, então uma pausa o congela no meio. Para mantê-lo fora da câmera, espere até que termine antes da próxima sincronia, como prime faz para avisos de login. Para mostrá-lo, como um toast "Salvo" aparecendo e desaparecendo, coloque um time.sleep após a ação que cubra todo o efeito, para que ele se desenrole antes da próxima pausa. Para uma página que anima continuamente enquanto ociosa, grave esse take com DEMO_TIMED=1, que dorme até cada palavra de sincronia enquanto grava e escreve <take>.timeline.json em vez disso.

7. Monte

Feche o navegador primeiro (agent-browser close): Chrome e o codificador do ffmpeg juntos podem exceder a memória de uma máquina ou contêiner pequeno.

export SCRIPTS=… DEMO=…; bash $SCRIPTS/python $SCRIPTS/assemble.py $DEMO --takes desktop phone outro --out <nome>

Produz DEMO/build/<nome>.mp4 com legendas queimadas, uma trilha de legendas suave e narração, mais <nome>.srt.

8. Revise antes de entregar

Você pode ver o vídeo, mas não pode ouvi-lo. Verifique o que puder:

export DEMO=…; ffmpeg -v error -y -i $DEMO/build/<nome>.mp4 -vf "fps=1/6,scale=480:-1,tile=4x8" -frames:v 1 $DEMO/build/grid.png   # leia
export DEMO=…; ffmpeg -i $DEMO/build/<nome>.mp4 -map 0:a -af volumedetect -f null - 2>&1 | grep volume                              # não silencioso

Na grade, confirme que cada cena mostra o que sua narração descreve, as legendas nunca cobrem o que está sendo apontado, e nenhum quadro captura uma página meio carregada ou um erro. Capture quadros individuais nos tempos de sincronia (-ss <t>) para os momentos importantes.

Em seguida, pare o servidor de desenvolvimento e entregue o vídeo: na máquina do usuário, copie-o para algum lugar que ele possa encontrar e open; em outros lugares, dê seu caminho, ou anexe-o ao pull request se uma de suas ferramentas fizer isso. Ao relatar, diga o que você não pôde verificar (áudio de ouvido) e liste quaisquer atalhos tomados, como um menu substituto.

Armadilhas já pagas

  • agent-browser click falha com "covered by" enquanto uma página ainda estabiliza. recorder.ab tenta novamente; uma falha que sobrevive às tentativas é real, então corrija o seletor.
  • Menus que enviam ao fechar, como listas de verificação que enviam ao pressionar Escape, levam alguns segundos para recarregar. Espere por isso antes do próximo clique, ou esse clique cairá em um botão movido.
  • Overmind se recusa a iniciar sobre um .overmind.sock obsoleto. Remova o socket apenas após confirmar que nada responde nele (overmind status).
  • Caminhos longos de scratchpad excedem limites de socket Unix; não aponte sockets para lá.
  • Kokoro divide texto longo em pedaços; narrate.py desloca os timestamps de palavra de cada pedaço, então sincronize por palavra, nunca por segundos estimados.

Habilidades Relacionadas