Pipeline de vídeo promocional Velxio
Três fases, todas neste repositório (velxio_docs; /home/dave/velxio_docs
na máquina de desenvolvimento):
- Gravação —
scripts/promo-record.mjsdirige o produto real no ambiente de staging de ponta a ponta (projeto em branco -> selecionar ESP32 -> adicionar 220R + LED -> girar resistor -> conectar por cliques nos pinos -> escrever sketch -> Executar -> LED pisca), com um cursor visível injetado + ondulações de clique. Cada evento registra{t, x, y, label}emscripts/promo-events.json; o vídeo bruto vai parascripts/promo-raw.webm. - Grade de batidas —
scripts/detect-beats.py(librosa) transforma a trilha sonora empromo/src/beats.json(BPM, batidas, kicks, snares). - Pós-produção —
promo/é um projeto Remotion.prepare.mjscopia a gravação, corta a inicialização pré-modal, remove a espera de compilação e gerasrc/data.json.src/Promo.tsxrenderiza a câmera (zooms centralizados em cada batida, cada corte ENCAIXADO na batida musical mais próxima dentro de 0,34 s), pílulas de legenda, um cartão de introdução SÓLIDO, o encerramento do velxio.dev e a faixa musical com fades.
Comandos
cd /home/dave/velxio_docs
VELXIO_SHOTS_EMAIL='[email protected]' VELXIO_SHOTS_PASSWORD='<veja memória vstaging-test-user>' \
node scripts/promo-record.mjs # gravação (~2-4 min incluindo compilação)
python3 scripts/detect-beats.py promo/public/soundtrack.mp3 --fps 30 \
--out promo/src/beats.json # apenas quando a faixa mudar
cd promo && node prepare.mjs
CHROME=$(ls -d $HOME/.cache/ms-playwright/chromium-*/chrome-linux*/chrome | sort | tail -1)
npx remotion render src/index.ts promo out/velxio-demo.mp4 --codec h264 --browser-executable "$CHROME"
Cópia publicada para visualização no GitHub: media/velxio-demo.mp4 (versionada;
sobrescrever + push para atualizar a mesma URL). promo/out/ está no .gitignore.
Procedimento de revisão (NÃO pule)
Amostre quadros NOS TEMPOS DAS BATIDAS a partir de promo/src/data.json (campo ct),
não em tempos adivinhados — as batidas mudam entre gravações:
ffmpeg -y -ss <ct> -i out/velxio-demo.mp4 -vframes 1 /tmp/beat.png
Verifique: introdução legível, cada zoom mostra seu assunto sem cortes, legendas correspondem ao que está na tela, cursor não cobre o que importa, LED final aceso.
Vídeos demo da galeria (modo básico)
Um vídeo curto por exemplo da galeria — abra, execute, filme funcionando — com um cartão de introdução dividido (logotipo + título + placa à esquerda, o circuito inteiro à direita), um terço inferior, o encerramento e a trilha sonora.
node scripts/gallery-scan.mjs # atualiza o índice de 406 exemplos
node scripts/gallery-record.mjs [slug ...] # gravações em 1080p (pula os já feitos)
node scripts/gallery-bbox.mjs [slug ...] # mede onde o circuito está
node scripts/gallery-render.mjs [slug ...] # um mp4 por gravação
- O conjunto de demos curadas está em
GALLERYno topo degallery-record.mjs; slugs vêm descripts/gallery-index.json(nome do arquivo de miniatura = slug). - Imagens estáticas são capturadas com
deviceScaleFactor: 2e cortadas para o bbox do circuito: nítidas o suficiente para preencher metade de um quadro 1080p. NÃO amplie o aplicativo para aumentar um circuito pequeno — a âncora de zoom da tela centraliza o zoom e desloca o circuito para fora da tela; em vez disso, redefina a visualização. - A passagem de bbox precisa de um CONTEXTO NOVO por exemplo: um segundo carregamento do editor no lado do cliente deixa a tela vazia e não mede nada.
- Exemplos sem placa (categorias Digital / Analógico) nunca compilam: Executar permanece desabilitado e o mecanismo SPICE os controla desde o início.
- Placas Backend-QEMU (STM32, Raspberry Pi) não emitem linha de console "in-browser". STM32 adicionalmente não executa no staging (libqemu-arm é um ativo opcional que não está implantado lá) — compila em 3 s, nunca inicia.
- Gravações brutas (
promo/public/gallery/*.webm, ~3 MB cada) estão no .gitignore; as imagens estáticas do circuito e o JSON por gravação são versionados para que uma re-renderização precise apenas das gravações de volta.
Música
promo/public/soundtrack.mp3 = Mixkit "House Vibez" por Lily J
(assets.mixkit.co/music/745/745.mp3), Licença Gratuita Mixkit Stock Music —
livre para uso comercial, sem necessidade de atribuição. 123 BPM. Troque o arquivo
e execute detect-beats.py novamente para mudar as faixas; mantenha a nota de licença em
promo/public/ATTRIBUTION.md atualizada.
A faixa abre com ~8 s de introdução: AUDIO_START em Promo.tsx é a
primeira batida detectada, então o áudio começa a partir daí e a composição
no tempo t=0 cai em um downbeat. O volume fica em 0,55 com fade-in de 0,9 s e
fade-out de 2,2 s sobre o encerramento.
Regras de estilo (da revisão de David — mantenha-as)
- O vídeo começa NO editor com o modal de modelo de placa já
aberto e o chat de IA já minimizado. Sem página inicial, sem carregamento, sem
clique de minimizar visível (o gravador faz isso programaticamente antes de iniciar;
prepare.mjs corta até a marca
start). - Título de introdução em um cartão escuro SÓLIDO — nunca translúcido sobre a UI.
- Os zooms devem CENTRALIZAR seu assunto: translate+scale com clamps de borda
(
Promo.tsx), nunca transformOrigin-at-point (deixa o assunto em uma borda e o corta, por exemplo, o cartão ESP32). - Batidas de adicionar peças usam uma SEGURADA curta (~0,5 s) para que o zoom libere a tempo de VER onde a peça caiu.
- A batida de código centraliza o PAINEL DO EDITOR INTEIRO (sobrescrita fx/fy 357.280) — nunca um editor cortado enquanto o código está sendo escrito.
- Após clicar em Executar, ESTACIONE o cursor longe (o gravador move para 700.620) para que o spinner do botão fique visível; a batida de Executar amplia o cluster de compilação/execução em escala moderada (1,8).
- Gire o resistor para horizontal após colocar (vertical padrão sobrepõe seus próprios fios na câmera).
- O ponteiro é uma SETA no estilo macOS, não um ponto. Matemática do hotspot (do skill saas-product-demo): a ponta está em (4,3) em uma viewBox 24x24, então na largura de renderização W o elemento é deslocado por (-4W/24, -3W/24) — sem isso cada clique fica ~6-8 px fora do alvo.
- Os cortes de zoom ocorrem nas batidas musicais (veja Música acima); nunca deixe um corte derivar mais de ~0,3 s de seu clique apenas para acertar uma batida.
- Batidas cujo MODAL FECHA ao clicar (escolher-branco, adicionar-*) precisam de
lead: 0.45: o pico deve chegar ANTES do clique para que o quadro mostre o cartão ainda aberto, e a liberação então revela onde a peça caiu. Pico no próprio clique deixa o zoom estacionado em um diálogo já fechado.
Armadilhas (cada uma custou uma gravação quebrada — todas tratadas nos scripts)
/é a página de marketing mesmo quando logado; o editor é/editor. Faça login via fetch POST/api/auth/loginprimeiro na página.- Em um
/editornovo, a sobreposição "Iniciar um novo projeto" abre automaticamente (.new-project-overlay) e intercepta todos os cliques de ponteiro externos. Interaja com ela primeiro ou dispare cliques programáticos. - O campo de busca do seletor MANTÉM a consulta anterior — limpe com Ctrl+A + Backspace antes de digitar ou você pesquisa "esp32led".
- Cartões do seletor: corresponda ao título exato em
.component-picker-modal .card-name(mecanismos de texto genéricos acertam tokens do Monaco atrás do modal). Os resultados são debounced — aguarde até 5 s. Os nomes dos cartões são exatos ("Resistor 220 Ω" com caractere estilo U+2126, não "Resistor"; verifique/components-metadata.json). - Placas NÃO são arrastadas pelo corpo — deixe a placa onde o seletor a solta (perto da borda direita da tela) e coloque peças pequenas RELATIVAS ao seu boundingBox atual, à ESQUERDA onde há espaço.
- Cliques em pinos: use os próprios
[data-pin-overlay="true"][title=<pino>]divs do aplicativo, escolhendo o candidato mais próximo do elemento hospedeiro (os títulos colidem: pino "2" da placa vs pino "2" do resistor). Calcular a partir de deslocamentospinInfoquebra em peças giradas. Os pinos ESP32 são nomeados '2','4','GND' (não D2). - Verifique se cada fio caiu contando
path[stroke="#1a1a1a"](cada caminho de contorno do fio; fios não têm classe/atributos data). Tente novamente uma vez via Escape + re-clique. - O verificador de circuito BLOQUEIA Executar para um LED sem resistor em série (erro de 3.9e12 A). A demo deve conectar GPIO2 -> 220R -> LED A, GND -> LED C. Nunca envie uma gravação que precisou de "Executar mesmo assim".
- O modelo em branco vem com um sketch padrão — Ctrl+A antes de digitar o
comentário na câmera, então defina o sketch completo via
monaco.editor.getModels()[0].setValue(...)(digitação literal luta contra os colchetes de fechamento automático do Monaco). - O toast "Gostando do Velxio? / Dê uma estrela no GitHub" fotobomba gravações com mais de ~5 min — o limpador injetado o remove a cada segundo.
- Playwright não grava cursor do SO: o
#vx-cursordiv injetado + ondulação de mousedown (context.addInitScript) é o ponteiro visível. - A espera de compilação é real (30 s quente - 5 min frio): o gravador registra
runesim-started; prepare.mjs remove esse intervalo. O início da simulação é detectado por uma linha de console "in-browser"; "guest crashed" = abortar. - Remotion renderiza com o chromium do Playwright via
--browser-executable(nenhum download de navegador agrupado nesta máquina).
Repositórios de referência (examinados em 2026-08-15)
Dois skills Claude-Code para o mesmo gênero, abordagem diferente: eles COMPÕEM filmes a partir de capturas de tela/UIs esquemáticas com um cursor sintético — o nosso GRAVA o produto real. Clone-os para evoluir:
github.com/noamdorr/saas-product-demo-video— filmes de marketing de 20-45 s sincronizados com batidas em Remotion. Roube: SINCRONIA COM BATIDA da trilha sonora (librosadetect-beats.py: cortes de cena em snares, eventos em cena em kicks — nosso vídeo é silencioso, adicionar música + alinhar zooms com batidas é a maior melhoria disponível), matemática do hotspot do cursor (a ponta da seta SVG está em 4,3 de uma caixa 24 — importa se trocarmos nosso cursor de ponto por uma seta), verificação de orçamento de digitação, manual de iteração.github.com/Vincentwei1021/video-shotcraft— 152 cartões de receita de tomada com fontes de demo + galeria (vincentwei1021.github.io/video-shotcraft), um modelo aprovado (Ink Press), movimentos de câmera 2.5D, design de som, e três modos de operação (template / autônomo / co-criado). Roube: o vocabulário de tomadas diretoriais e sua lista de verificação de revisão final; também instala como um plugin se um filme cinematográfico completo for desejado.
Relacionados
- Pipeline de capturas de tela:
scripts/shots.mjs(mesmo repositório) — cenas com o painel de IA minimizado a menos que seja o assunto. - GIFs: grave um segmento (Playwright recordVideo) e converta com
ffmpeg two-pass palette; veja
scripts/weather-media.mjspara o padrão de zoom+corte (região de corte a partir do boundingBox do elemento). - Usuário de teste do staging: memória
vstaging-test-user(s3-tester, pro). - Projeto de showcase quebrado (estação meteorológica) é rastreado em
project/docs-portal-2026-08/STATUS.mdem velxio-prod.