O que Visual Guide Video faz?
Analisa o vídeo e escreve um único arquivo de timeline (timeline.py). O motor renderiza toda a direção, música e efeitos sonoros como código. Nenhuma ferramenta de edição externa ou arquivo de áudio é usado.
SKILL_DIR = pasta onde este SKILL.md está localizado. Execute scripts com python "$SKILL_DIR/scripts/<nome>.py".
0. Preparação
pip install -r "$SKILL_DIR/requirements.txt" # opencv-python imageio-ffmpeg numpy scipy pillow edge-tts
O ffmpeg é fornecido pelo pacote imageio-ffmpeg. Fontes coreanas e emoji são detectadas automaticamente no SO (podem ser especificadas com VGUIDE_FONT_BOLD/REGULAR/EMOJI).
A pasta de trabalho fica ao lado do vídeo: project/timeline.py, saída em project/build/, project/output/.
1. Determinação do tipo de vídeo → Seleção de perfil
python "$SKILL_DIR/scripts/analyze.py" VIDEO --out build/analysis
Veja a Tipo estimado no console e as imagens em build/analysis/sheets/*.jpg (use Read) para determinar o tipo. Um PROFILE define os valores padrão, mas os valores escritos em timeline.py sempre têm prioridade.
| PROFILE | Alvo | Padrões | Direção principal |
|---|---|---|---|
screen | Gravação de tela, tutorial, demonstração | Música pop, som original desligado, barra de progresso | SHOTS zoom, CLICKS, LINKS, HIGHLIGHTS, CARDS, HUD, FLOW |
game | Gameplay, luta contra chefe, destaque | Música épica (baixo), som original ligado, correção de cor cinematográfica, 30fps | MAGNIFY, IMPACTS, SPEED/FREEZE, POPS, CALLOUTS(@track), análise de barra |
vlog | Cotidiano, viagem, resenha | Música chill, som original ligado, correção de cor quente | CALLOUTS, STICKERS, SPEED (aceleração de movimento), legendas, GRADES |
anim | Animação, vídeo de personagem | Música cute, correção de cor vívida | POPS (engraçado), STICKERS, CALLOUTS, FREEZE |
promo | Anúncio, promoção, viagem, filme de marca | Música épica (alta), som original desligado, 30fps | TYPE (tipografia de anúncio, carimbo), CLEANUP (remover texto original), transição INK, GLOWS, LETTERBOX, partículas sazonais, música zen arc |
Cada tipo tem o que explicar diferente:
- screen: "O que está sendo clicado e o que muda" → zoom no ponto de clique, seta para o painel que mudou, cartão de estado.
- game: "O que aconteceu (ataque, defesa, dano, cura, padrão do chefe)" + diversão → impacto, pop-up de reação, lupa para alvo distante, congelamento para explicação no momento crucial, aceleração de seções de movimento tediosas.
- vlog: "Onde e o que está sendo feito" + emoção → atmosfera em vez de explicação. Destaque de clímax de eventos (aniversário, viagem, formatura) com câmera lenta e partículas, aceleração de seções de movimento agitado, congelamento + Polaroid no momento da foto, colagem de fotos no final.
- anim: Destaque da reação do personagem → pop-ups fofos, adesivos, congelamento de expressão decisiva.
- promo: "Fazer o espectador querer ir e comprar" → texto de anúncio curto (2 linhas de abertura → título herói → capítulos por cena → cartão final), transições características como tinta espalhada na mudança de grandes seções, curva de energia musical (calmo → crescente → clímax → ressonância → final). Se o original tem texto em outro idioma, remova e substitua pelo idioma alvo (versão internacional).
Princípio comum de edição (o que os usuários mais gostam): Seções desnecessárias (movimento, carregamento, câmera tremida) são aceleradas, momentos importantes são congelados para colocar explicação ou decoração. Na fase de análise, cada seção é classificada como "essencial / conexão / supérfluo" e o trabalho começa.
2. Análise — Quando, onde e o que acontece
Tipos de eventos em events.json:
- Comum:
cut(mudança de cena),flash(clarão),loud(pico de som = impacto, explosão),motion(pico de movimento) - screen:
switch(candidato a clique/toque),big-change,scroll - Barra (
--bar):bar-drop/bar-big-drop(dano),bar-low(perigo),bar-empty(morte),bar-rise(cura)
Jogos devem medir a barra de HP. Os momentos de dano, cura e morte são precisos em nível de quadro.
python "$SKILL_DIR/scripts/crops.py" VIDEO --t 10,30 --box 200,940,500,100 --step 25 --out build/hp.jpg # medir coordenadas da barra
python "$SKILL_DIR/scripts/analyze.py" VIDEO --out build/analysis --bar player:337,982,626,988 --bar boss:1015,911,1590,915
- A caixa da barra deve ser uma única linha dentro da barra (altura 4~8px). Usar
lightpara barra branca,:anypara barra colorida. - Verifique
bar_<nome>.png(caiimógrafo: vertical = tempo) com Read para confirmar se os eventos estão corretos. Se no momento do dano a barra piscar em vermelho ou o HUD desaparecer por efeito, pode haver falso positivo; verifique cruzadamente comgraph.pnge as imagens da planilha. - A planilha tem intervalos de 2 segundos. Para seções de luta, extraia quadros a cada 0,3~0,6 segundos para interpretar qual ação é (ataque/rolagem/dano/transformação) diretamente.
Coordenadas precisas: Meça sempre com crop quadriculado.
python "$SKILL_DIR/scripts/crops.py" VIDEO --t 7.4,13.5 --box 0,760,420,240 --out build/crops.jpg # área, grade de 50px
python "$SKILL_DIR/scripts/crops.py" VIDEO --t 12 --full --out build/grid.jpg # tela inteira, grade de 100px
3. Escrever timeline.py
Comece de uma receita. Copie a receita adequada ao tipo como timeline.py e preencha apenas [1. valores a preencher] (tempo, coordenadas, texto dos momentos-chave) para criar automaticamente uma estrutura de direção validada. Depois, ajuste ou adicione itens conforme o vídeo durante a visualização da prévia.
| Receita | Alvo | Valores a preencher |
|---|---|---|
recipes/screen_walkthrough.py | Tutorial de gravação de tela, demonstração | Cliques (tempo, coordenadas, texto, o que mudou, cartão), área a destacar, legendas |
recipes/game_boss_fight.py | Destaque de luta em jogo | Posição da barra de HP, dano (tempo, HP restante), meu ataque, chefe derrubado, cura, morte/abatido, seções de movimento, legenda explicativa |
recipes/event_vlog.py | Vlog de aniversário, comemoração, festa | Tempo e alvo do clímax, seção tremida, contagem regressiva/quadro congelado de foto, aparição de convidados, cena final |
recipes/promo_shorts.py | Curta vertical de 30 segundos (montagem de anúncio) | Lista de segmentos (segmento original, duração de saída), panning por cena, palavra sazonal/texto do capítulo, tipo de transição |
recipes/promo_film.py | Filme de anúncio, promoção, viagem | Cópia de abertura, cartão herói/final, cenas (segmento, velocidade, texto do capítulo, carimbo, câmera, partículas, energia, som de fundo), tempo da transição de tinta, objeto brilhante, texto a remover |
Os valores padrão das receitas foram validados com vídeos reais; mantenha-os para ter um comportamento de exemplo (mas meça novamente para outros vídeos). Para itens não presentes na receita, consulte o esquema completo em templates/timeline_template.py. Se não houver receita adequada, comece do modelo.
Coloque o timeline.py do vídeo editado na pasta desse vídeo. Não coloque timelines de vídeos específicos (nomes de pessoas, cenas pessoais, conteúdo do trabalho) no repositório da skill — padrões que podem ser generalizados são movidos para receitas e este documento.
Regra: tempo é sempre 'tempo do vídeo original', coordenadas são 'pixels originais'. SPEED/FREEZE alteram a duração de saída, mas o motor converte automaticamente.
Para tempos dentro de uma seção congelada, use (tempo original, segundos de deslocamento da saída): (49.45, 1.6) = o congelamento começa em 49.45s e 1.6s depois. Use quando exibir pop-up, Polaroid e final em sequência durante o congelamento.
Elementos universais (todos os perfis)
| Item | Uso |
|---|---|
TRIM, SPEED, FREEZE | Cortar seção, velocidade/câmera lenta (etiqueta de badge, sem badge se label=None), tela congelada (look desat=para explicação / photo=somente flash do obturador). Aceleração tem motion blur automático (SPEED_BLEND) |
TRACKS | Keyframes de alvo em movimento. auto=True (rastreamento OpenCV: pessoas, animais) / auto="match" (correspondência de template: objetos de forma fixa como bolo, placa, rastreia com uma única key até until) / follow="pai" (alvo preso ao track pai, ex: vela no bolo). Referenciado em outros itens como "@nome", "@nome:top" |
PARTICLES | Partículas confetti/hearts/sparkle/bokeh/notes/stars/snow/petals/leaves/fireflies (especificar cor com palette=[6]). Em track/coordenada: burst (explosão única) ou rate (contínuo), tela inteira (at="screen") |
PHOTOS | Exibir um quadro original como Polaroid (legenda, ângulo, queda na entrada). Para telas congeladas e colagem de final |
SHOTS | Câmera: zoom em coordenada / "full" / "fill" (preenche sem letterbox) / "@track" zoom de rastreamento |
MAGNIFY | Lupa: amplia alvo distante em círculo + linha de conexão + rótulo |
CALLOUTS | Balão de fala apontando para alvo (segue o track) |
POPS | Pop-up de texto estilos impact/danger/cool/info/funny/small/party/sweet/count (número de contagem regressiva) |
STICKERS | Adesivos emoji (bob/pulse) |
IMPACTS | Impacto hit/heavy/crit/damage/block/parry: tremor+flash+zoom punch+aberração cromática (+efeito sonoro automático) |
SHAKE, FLASH | Tremor e clarão manuais |
GRADE, GRADES | Correção de cor cinematic/warm/cool/vivid/mono/sepia/danger/dream/night (transição por seção) |
SFX, SFX_VOLUME | Biblioteca de efeitos sonoros: hit boom clang pop ding scratch sad_trombone heartbeat shutter slowmo crash whoosh riser party_horn twinkle confetti / stamp temple_bell ink gliss gliss_down / sons de fundo com duração "waves:8" "wind:6" "water:6" "birds:5" |
SOURCE_AUDIO | Som original (seção de câmera lenta: pitch desce, seção congelada: silêncio, seção acelerada: reduz com fast_gain) |
MUSIC | style pop/epic/chill/cute/zen, arc (zen: energia por cena), start (iniciar música a partir deste tempo), drop, end (+end_fx scratch/fade/cut), volume |
MASTER_GAIN_DB | Ganho final da mixagem (antes do limitador). Vídeos de celular têm áudio baixo, use +3~5 dB |
CUSTOM_LAYERS, CUSTOM_FRAME_FX, CUSTOM_SFX | Efeitos novos solicitados adicionados como funções dentro do timeline.py (veja abaixo) |
OUTPUT_SIZE, FPS, QUALITY | Resolução de saída (ex: 720p (1280, 720)), fps, CRF |
CANVAS, BACKDROP | Tela de design. Para vertical curto: CANVAS = (1080, 1920) + OUTPUT_SIZE = (1080, 1920). Original horizontal entra como faixa central e o espaço vazio é BACKDROP ("black", "blur", dict(kind="blur", tint=(r,g,b), amount=0.5)). Para preencher a tela, zoom com escala SHOTS ≈ 3.16 (= 1920 / 607.5) e panning em x |
SPEED_RAMP | Ao unir seções de velocidade sem gaps (montagem curta): não retorna a 1x entre as seções, transita suavemente por este tempo |
Elementos de anúncio/promoção (scripts/promo.py, disponível em todos os perfis)
| Item | Uso |
|---|---|
CLEANUP | Remover texto, carimbo, logotipo original (t0, t1, (x0,y0,x1,y1), dict(plate=(ta, tb), ref=t)). plate = quadro sem texto na mesma cena → restaura o fundo diretamente (se a cena estiver em panning/zoom, corrige automaticamente com correlação de fase). mask="box" substitui a caixa inteira (fundo parado), sem plate usa inpainting (NS + textura de papel), live=True para texto em movimento (ex: rótulo deslizando, ink="light") |
TYPE | Tipografia de anúncio. "line" (cópia que surge palavra por palavra), "hero" (título grande que aparece com espaçamento + subtítulo + carimbo), "chapter" (carimbo aparece, seguido por kicker, linha, título, com veil para véu de névoa atrás). Coordenadas são para canvas 1920x1080, cor color, legibilidade halo |
GLOWS | Brilho de sol/lua/luz + lens flare (streak, ghost) — segue o track |
INK | Transição de tinta espalhada (t, dict(dur, at)): cobre a tela ao máximo em t, depois se recolhe do centro (efeito sonoro automático) |
TYPE "slam" | Para curtas: palavra grande 'explode' no ritmo — desenhada sobre a tinta, visível mesmo quando a tela está coberta (over_ink). Carimbo seal |
LETTERBOX | Letterbox cinematográfico dict(ratio=2.39, range=(t0, t1)) — fade suave na entrada e saída. Partículas também ficam ocultas sob as faixas |
Quando solicitado um novo efeito — Hook personalizado
Não modifique o motor; escreva funções no timeline.py. ctx contém: t (tempo de saída), ts (tempo original), raw (quadro original), frame (canvas BGR atual), view, cv (canvas de overlay), tl, gfx, fx, W, H, screen("@boss"), window(t0, t1) (intervalo de tempo original → (alpha, progresso)).
def speed_lines(ctx): # CUSTOM_LAYERS: desenhar overlay (sem retorno ou novo frame)
w = ctx.window(57.2, 58.4)
if w:
x, y = ctx.screen("@boss")
for k in range(12):
a = k / 12 * 6.283
ctx.fx.draw_line(ctx.cv, (x + 300 * math.cos(a), y + 300 * math.sin(a)),
(x + 700 * math.cos(a), y + 700 * math.sin(a)), (255, 255, 255), 3, w[0] * 0.6)
CUSTOM_LAYERS = [speed_lines]
def red_pulse(ctx): # CUSTOM_FRAME_FX: transformar frame → retornar novo frame
return ctx.fx._tint(ctx.frame, (255, 0, 0), 0.15) if ctx.window(76, 78) else None
CUSTOM_FRAME_FX = [red_pulse]
def boss_roar(a): # CUSTOM_SFX: a = dict(buf, add, lib, tl, SR)
a["add"](a["buf"], a["lib"]["boom"]()], a["tl"].out(14.4), 0.8)
CUSTOM_SFX = [boss_roar]
Efeitos usados com frequência são movidos para fx.py e se tornam itens oficiais.
Regras de Layout (baseadas em problemas reais)
Comum
- Emojis são renderizados em cores. Mas símbolos como
▸ ✓ ✕podem quebrar como □; use emojis (✅ ✖️ 👉 ⏩ ⏸) ou• → ·. - Legendas com cerca de 40 caracteres por linha. Destaque palavras-chave com
[colchetes]. Legendas em tela congelada: escreva o tempo de início e fim iguais(tf, tf, "...")para aparecer apenas durante o congelamento. - Não sobreponha mais de 3 pop-ups, balões de fala ou adesivos no mesmo momento. Pop-ups de 0,8~1,2s são suficientes.
screen
- Divida o tempo para cartão e destaque não se sobreporem (cartão → destaque). Termine o cartão antes do próximo zoom de clique.
- Se o destaque sair da tela durante o zoom, ele será cortado → use escala 1,15~1,3.
- Posicionamento de rótulo
"right"(seta),"above-right"(linha de tabela),"below"(elemento superior). HUD em modo mini (100px à esquerda) com zoom de 1,12x ou mais.
game
- Ultrawide (21:9) entra como letterbox em canvas 16:9. As faixas superior e inferior exibem badge de velocidade e legendas sem obstruir a ação. Para preencher, use
"fill"emSHOTS. - Destaque de barra: use
"above|nodim"— escurecer a tela esconde a ação. - Zoom máximo de 1,3~1,5x. Se a ação for muito rápida, o alvo sai da tela com zoom excessivo. Use
MAGNIFYpara ampliação. - Congelamento (FREEZE) de 1
2s, 34 vezes por vídeo é suficiente. Durante o congelamento, use CALLOUTS para apontar "quem fez o quê". - Seções de movimento/carregamento: reduza com SPEED 2
3x. Câmera lenta 0,30,4x apenas em dano/golpe final por 0,5~1s. - Keyframes de track: registre sempre que o alvo se mover significativamente; em mudanças de cena (corte), escreva o mesmo valor duas vezes (antes e depois) para evitar interpolação abrupta. Verifique com
python "$SKILL_DIR/scripts/tracks.py" timeline.pypara criar uma planilha. - Use eventos de HP para capturar momento do dano e alinhe
IMPACTScom precisão de quadro. Dano =damage/crit, acerto do meu ataque =hit/heavy. - Mantenha o som original do jogo e BGM baixo (volume 0,3~0,45). Na morte, use
MUSIC["end"]+end_fx="scratch"para cortar a música e um efeito sonoro comosad_trombonepara criar um ponto de risada.
promo (anúncio, promoção, versão internacional)
- Remover texto original: escolha quadros como plate 0,4s antes do texto aparecer ou depois de desaparecer. Quadros em transição de fade ainda têm texto fantasma → verifique com crop de contraste aumentado (remova desfoque de fundo ×4). Após remover, use
--previewem resolução original para ver resquícios e emendas. - Fundos que mudam constantemente (névoa, respingos) não combinam com plate (mancha cinza) → inpaint sem plate e coloque tipografia de capítulo com
veil. - Detecção de carimbo vermelho: captura apenas vermelho escuro (exclui pôr do sol, céu alaranjado). Se o céu estiver alaranjado, verifique se a caixa inteira não muda criando faixas de cor.
- Cópia curta: abertura 2
4 palavras × 2 linhas, título do capítulo 25 palavras, cartão final marca + uma linha. Não use slogans oficiais de instituições/marcas reais. - Tipografia dentro do letterbox (excluindo 140px superior e inferior). O local onde estava o texto original geralmente é 'céu vazio', bom para colocar capítulo.
- Música zen: defina energia por cena com
arc. Ajuste a velocidade da cena para que o tempo da transição de tinta caia na fronteira do compasso (80 BPM = 3s) para que transição, tambor e carimbo fiquem no ritmo (usetl.loadpara ver o tempo de saída → ajustar velocidade). O volume por estágio deve ter grande diferença (calmo −20 / pico −12 dB) para que o clímax se destaque. - Cenas decisivas (biombo se desdobrando, nascer do sol) em 0,75
1x, paisagens lentas em 1,52,4x. Use tinta apenas na mudança de grandes seções (estação, região); dentro da mesma seção, preserve as transições originais. - Curta vertical (30s) (
recipes/promo_shorts.py): 96 BPM (um compasso = 2,5s) com uma cena por compasso. UseSEGMENTS = [(início original, fim original, duração de saída)]para unir eRemappara calcular a velocidade inversa, garantindo que a transição caia exatamente no compasso. Transições originais (crossfade) são comprimidas em 0,2s e escondidas sob tinta/flash.- Primeiro 1s é o hook: uma linha de cópia → compasso 1 com título da marca + impacto musical. Abertura e final em layout de faixa (original central + tipografia acima/abaixo), parte principal em crop vertical completo + panning para o lado do objeto (sol, cachoeira, lua no centro, paisagem ampla da esquerda para a direita).
- Em seções aceleradas, o tempo original fica mais curto; escreva o tempo da tipografia baseado na saída como
(TRIM[0], segundos de saída). Capítulos aparecem rápido compace=0.55. - Partículas com
clip=Truepara cortar junto com o corte (pétalas não passam para a próxima estação). Evite a UI do curta; tipografia dentro de 260px superior e 420px inferior.
vlog (vídeo de família/evento)
- Vídeos de celular têm muita trepidação. Seções de movimento tremido: acelere 3~4x (o motion blur automático suaviza), o som original é reduzido automaticamente.
- Clímax (apagar vela, brinde, gol) em câmera lenta 0,4x + flash + confetti burst + party_horn. Música começa nesse momento (
MUSIC["start"]) — antes, preserve o som original (cantar parabéns, conversa). - Cena de foto: congele no quadro sem tremor logo após o obturador ser pressionado (
look="photo") e exiba uma Polaroid. Verifique na prévia se o quadro congelado está nítido (quadros com câmera em movimento ficam borrados). - Não coloque adesivos, pop-ups ou corações sobre rostos. Meça as coordenadas do rosto e coloque ao lado ou em espaço vazio acima. Para apontar expressões (careta, piscada), use CALLOUTS (
ring=False) apontando de lado. - Se uma contagem regressiva aparecer na tela (temporizador de celular), alinhe pop-ups
count3·2·1 no mesmo momento e adicione efeito sonoro pop. - Final: colagem de 2~3 Polaroids sobre tela congelada + mensagem curta de parabéns (use
OUTROcomtoppara subir estats=[]). - A história deve ser contada mesmo sem legendas ou narrador: título de introdução + uma linha curta de pop-up por cena é suficiente.
4. Repetir pré-visualização
python "$SKILL_DIR/scripts/build.py" timeline.py --preview 3.5,7.4,26 # tempo original
python "$SKILL_DIR/scripts/build.py" timeline.py --preview-out 16.6,36.2 # tempo de saída (verificar meio de congelamento/câmera lenta)
Veja build/preview/sheet*.jpg com Read e corrija sobreposição, corte, oclusão, erro de coordenada, texto quebrado. A conversão de tempo também é verificada com o rótulo src ↔ out. Seções congeladas não podem ser verificadas com tempo original; use --preview-out.
5. Áudio
- Música de fundo
MUSIC["style"]: pop (126BPM electro pop), epic (96BPM taiko, cordas staccato, metais, coro), chill (82BPM lo-fi), cute (132BPM marimba, pizzicato), zen (80BPM gayageum, daegeum, janggu, tambor, coro,MUSIC["arc"] = [(tempo original, "calm|flow|pulse|build|peak|hush|finale")]). Exceto zen, todas têm estrutura intro → A → B → build → drop → outro, com destaque explodindo nodrop. - Som original: game/vlog/anim ligado por padrão. Se houver narrador, ele duck junto (
SOURCE_AUDIO["duck"]). - Verificação de nível: Em jogos, o som original é o protagonista. Meça o RMS por seção para que a música fique 3~5dB mais baixa que o original e os efeitos sonoros apareçam apenas no momento do impacto.
import tl, music, build; t = tl.load("timeline.py"); m = music.build_music(t)[0] * t.music["volume"]; s = build.source_audio(t, m.shape[1]) - Narrador (opcional)
NARRATION = [(t, "frase")](se vazio, lê as legendas).--voice edge(rede neural Microsoft, online — informa que a frase é enviada à Microsoft) /--voice sapi(offline Windows). Embuild/voice_report.json, se rate ultrapassar +15% ou overflow, reduza a frase (coreano ≈ 7 sílabas/s).
6. Renderização final e verificação
python "$SKILL_DIR/scripts/build.py" timeline.py [--voice edge] [--workers 8]
Divide os quadros e renderiza em paralelo com múltiplos processos, depois une (1 min 1080p ≈ 2~3 min com 8 workers). Execute em segundo plano.
Verificações antes de concluir
- Na última linha
[done] ... Ns (esperado Ns), N MB, veja se a duração corresponde e se não há Traceback no log. - Extraia alguns quadros da saída para verificar:
ffmpeg -ss T -i out.mp4 -frames:v 1 f.png - Como não é possível ouvir a música e efeitos sonoros diretamente, verifique com RMS por seção e peça ao usuário para ouvir.
Estrutura de arquivos
| Arquivo | Função |
|---|---|
scripts/analyze.py | Metadados, estimativa de tipo, transições de cena, picos de som, eventos de barra, gráfico, planilha |
scripts/crops.py | Crop quadriculado de coordenadas originais |
scripts/tracks.py | Rastreamento de alvo (interpolação de keyframes, OpenCV), planilha de verificação |
scripts/remap.py | Remapeamento de tempo (TRIM, SPEED, FREEZE) |
scripts/build.py | Pré-visualização / mixagem de áudio (remapeamento de som original, limitador) / renderização paralela / SRT |
scripts/tl.py | Carregador de timeline.py, valores padrão de perfil, conversão de coordenada/tempo |
scripts/render.py | Câmera (rastreio, tremor), camadas de gravação de tela, hooks personalizados |
scripts/fx.py | Efeitos universais: correção de cor, impacto, lupa, balão de fala, pop-up, adesivo, badge, ... |
[truncado para tradução]