video-sync
Combine shots.json + vídeo original em um único vídeo: de um lado a imagem, do outro as informações do plano atual, que mudam quando o plano muda, com a tabela rolando e destacando automaticamente.
O layout depende apenas da proporção do vídeo original:
| Original | Layout | Composição |
|---|---|---|
| Paisagem (largura > altura), Quadrado | Imagem em cima, informações em baixo | vstack |
| Retrato (altura > largura) | Imagem à esquerda, informações à direita | hstack |
Ambas as direções garantem que a imagem seja redimensionada sem cortes ou distorções, o painel preenche o espaço restante.
Apenas três capturas de tela são feitas; o movimento é deixado para o ffmpeg. Apenas duas coisas mudam no painel: a rolagem e a linha destacada. Então, medimos a posição das linhas uma vez, capturamos a base e duas imagens longas com a tabela de planos (uma escura, uma totalmente clara). A rolagem = recortar uma janela da imagem longa escura; o destaque = sobrepor uma linha recortada da imagem longa clara. A posição é controlada por pequenas expressões sendcmd em cada ponto de corte. Para um vídeo de 53 planos, as capturas de tela caem de 53 para 3 (12 segundos).
O plano atual é fixado na segunda linha: os planos 1 e 2 não rolam (o destaque desce uma linha); a partir do plano 3, a cada corte, sobe uma linha. Movimento apenas no corte, estático dentro do plano – no corte, a rolagem leva --ease (padrão 0,45) segundos para parar na posição. Em um plano longo de 16 segundos, a lista subiria continuamente, forçando o espectador a acompanhá-la por 16 segundos.
A altura da linha é determinada pelo conteúdo: descrições longas resultam em linhas mais altas; a barra de destaque é estratificada por altura (crop não pode ser alterado em tempo de execução).
{baseDir} = o diretório deste arquivo. O script {baseDir}/scripts/video-sync.mjs não tem dependências, execute diretamente com node.
Limites (o que não faz): Não faz decupagem (é trabalho do video-shots; esta skill consome seu shots.json), não edita, não faz transições, não adiciona música, não queima legendas na imagem, não faz renderização quadro a quadro (animações são feitas pelo ffmpeg cortando janelas por tempo).
Passo 0 — Tenha os dados de decupagem primeiro
Você precisa de um shots.json (saída do video-shots) e do vídeo original correspondente. Ter frames/ (um quadro-chave por plano) é melhor – a tabela de planos usará o quadro inicial como miniatura; caso contrário, ficará em branco.
O shots.json deve corresponder a este vídeo: se meta.durationSeconds não corresponder ao vídeo original, não combine – corrija a decupagem primeiro.
Passo 1 — Veja a geometria primeiro, depois o vídeo
node {baseDir}/scripts/video-sync.mjs plan shots.json --video <video>
Apenas calcula, não compõe – resultado em segundos: layout, área de vídeo, área do painel, dimensões de saída. Confirme esses três números antes de continuar – compor um vídeo de 3 minutos leva 1-2 minutos; recalcular se estiver errado não vale a pena.
Ajuste se necessário: --panel <proporção> (para paisagem: altura do painel / altura do vídeo, padrão 0,8; para retrato: largura do painel / largura do vídeo, padrão 1,6), --width / --height (limite superior da área de vídeo, padrão 1920 / 1080).
Se o vídeo original for muito pequeno, aumente a área de vídeo: o painel é calculado a partir da área de vídeo. Para um vídeo 640×360, o painel teria apenas 640×288, com quatro colunas amontoadas e ilegíveis. --scale 2 aumenta a área de vídeo para 1280×720 e o painel para 1280×576, mantendo a proporção. O padrão é 1 (apenas reduz, não amplia). plan / panels / compose / export todos precisam do mesmo --scale.
Passo 2 — Renderize o painel
node {baseDir}/scripts/video-sync.mjs panels shots.json --video <video> \
--frames frames --lang zh|en [--out panels] [--chrome <caminho>]
Gera static.png (base), list-dim.png / list-lit.png (imagens longas com a tabela de planos completa, uma escura e uma clara), layout.json (posição de cada linha, usada na composição para calcular a rolagem), além de panels/panel.html – abra-a no navegador para visualizar o painel. Altere #S07 para ver o efeito em diferentes planos (a animação de rolagem na prévia corresponde à exportação).
Para alterar o layout, edite {baseDir}/scripts/panel.css e execute panels novamente para obter o novo layout – sem precisar modificar o script ou repensar a lógica de captura de tela. Convenções de estilo em {baseDir}/references/panel-style.md.
Passo 3 — Componha
node {baseDir}/scripts/video-sync.mjs compose shots.json --video <video> \
[--panels panels] [-o out.mp4] [--crf 20] [--ease 0.45]
A faixa de áudio é copiada do original (vídeos sem áudio automaticamente não recebem áudio). O stderr exibirá as dimensões de saída, duração e layout – verifique-os.
As duas etapas podem ser feitas em um único comando:
node {baseDir}/scripts/video-sync.mjs export shots.json --video <video> -o out.mp4 --frames frames
Passo 4 — Extraia quadros para verificação ⛔ Não pule
Não entregue apenas porque a composição não deu erro. Extraia pelo menos três quadros para verificar se as informações correspondem à imagem:
for t in 5 30 60; do ffmpeg -v error -y -ss $t -i out.mp4 -frames:v 1 -q:v 3 /tmp/check-$t.jpg; done
Verifique três coisas: se o quadro atual pertence ao plano correto, se o número do plano no painel é o mesmo e se a linha de destaque o acompanha. Se não corresponder, geralmente é porque o shots.json não é do mesmo vídeo ou --video aponta para o arquivo errado.
Relate em uma frase: caminho de saída, dimensões, duração, layout, número de planos e quais imagens do painel não foram renderizadas (se houver).
Limites
- A rolagem só acontece nos cortes: rola por
--easesegundos e para; o painel fica estático dentro do plano. Se quiser que suba continuamente, é possível (altere os segmentos domotionPlan), mas em planos longos cansará os olhos - Requer um navegador headless (Chrome / Chromium / Edge). Esse é o preço de poder alterar o layout facilmente: usar
drawtextdo ffmpeg para o painel exigiria implementar manualmente quebra de linha em chinês, miniaturas e destaque - O vocabulário (enquadramento / categoria / movimento / transição) está embutido nesta skill – não importa de outro diretório para que a skill seja portátil. Se
video-shotsadicionar novos termos, eles devem ser adicionados aqui também, caso contrário aparecerão como chaves de enumeração - A renderização do painel sempre tira três capturas de tela, independentemente do número de planos: 53 planos ≈ 12 segundos. A composição depende da duração do vídeo: um vídeo de 203 segundos leva cerca de 75 segundos
Autoteste
node {baseDir}/scripts/selftest.mjs
122 asserções, sem usar ffmpeg ou navegador: geometria (paisagem/quadrado/retrato, arestas pares, limites, botões), contrato de dados da página do painel (envio de vocabulário, papéis rítmicos, miniaturas presentes ou não, escape), comandos de animação (cada um deve ser curto – expressões longas fazem o ffmpeg falhar na configuração), parâmetros do ffmpeg. Execute este teste após modificar os scripts.