Sobreposições de vídeo
Transforma um corte bruto em um conjunto de sobreposições animadas que aparecem no frame exato em que o locutor diz cada coisa, além de um arquivo de importação para Resolve.
Defina sua marca aqui
Edite estas configurações uma vez, em scripts/scene.html (bloco :root no topo). Todo o resto as lê daí.
| configuração | variável CSS | padrão |
|---|---|---|
| cor de destaque | --brand-accent (+ --brand-accent-rgb como r,g,b) | #4a90e2 |
| cor de texto clara | --brand-light (+ --brand-light-rgb) | #f2f2f2 |
| cor de tinta escura | --brand-ink | #0d131a |
| fonte de destaque (títulos, números) | --font-display | 'Anton','Bebas Neue',Impact,sans-serif |
| fonte do corpo | --font-body | 'Inter',-apple-system,sans-serif |
A fonte de destaque deve estar instalada localmente (por exemplo, em ~/Library/Fonts), já que os clipes são renderizados offline em headless Chrome. Anton e Bebas Neue são gratuitas no Google Fonts. O nome BLUE no código significa apenas "a cor de destaque".
Estilo da casa: texto em cena. Sem cartões. O texto vive no ambiente ao lado da cabeça do locutor, títulos grandes flanqueiam a cabeça, perguntas aparecem enquanto são ditas. Sempre suas cores de marca e fonte de destaque.
A regra mais importante: mantenha os gráficos atrás da cabeça do locutor ou ao lado. Nada na frente do rosto e sem cor de fundo. Os diagramas também ficam na parede ao lado do locutor. Tudo tem matte, para que as mãos passem na frente dos gráficos.
Uma execução produz: N clipes .mov transparentes, PLACEMENT.txt, dois arquivos .fcpxml e um breve PREVIEW.mp4.
Sem som por padrão. Não adicione música ou efeitos sonoros a menos que seja solicitado.
Padrões de estilo (ajuste a gosto)
- Nada aparece antes de ser dito. Fixe cada clipe ao timestamp no nível da palavra da frase, mais ~0,12s para que apareça logo após as palavras. O tempo no nível da seção é lido como antecipado.
- Curto. De 2,5 a 6,5 segundos. A maioria em torno de 3. Deve cortar para frente e para trás como em mídias sociais, sem ficar parado.
- Atrás da cabeça ou ao lado, nunca sobre o locutor. Sem cartões, sem fundos, sem escurecimento. Isso aposenta os chips de canto, todos os cartões
full-*edefinepara novos vídeos. - Ainda mostre o mecanismo. Recorra a um diagrama antes de uma frase. Use os tipos de diagrama em cena (
dots,flow,bars,compare,math), que ficam na parede ao lado do locutor. - Divida qualquer coisa com duas metades. Modo antigo e modo novo são dois clipes. 10% e 20% são dois clipes. Três opções são três clipes, cada um aparecendo quando nomeado.
- Cada linha aparece na sua palavra. Listas, linhas laterais e palavras digitadas carregam deslocamentos por linha (
at,t) a partir dos timestamps das palavras, não um espaçamento uniforme.scripts/plan.example.pymostra como: cite a frase, nomeie as palavras-chave, e ele faz a busca. - Uma boa mistura (cerca de 48 clipes em 11 minutos): aproximadamente um quarto de diagramas em cena (principalmente
flow), depoisside,pair,type,list2,num. Guardepairpara os momentos de impacto. - Títulos grandes vão ao redor da cabeça, não atrás dela.
pairdivide cada linha em um limite de palavra em duas metades que flanqueiam a cabeça, com um intervalo de 1,9x a largura da caixa facial (cabelo e orelhas incluídos), dimensionado para que ambas as metades fiquem na tela. Portanto, cada linha depairprecisa de 2+ palavras ("UMA HORA", não "HORA"): uma palavra única não pode ser dividida e acaba escondida."around": falseemdatatraz de volta o visual centralizado. A linha com contorno tem 150px com traço de 5px e preenchimento claro de destaque (35%) para permanecer legível.
Antes de começar: o corte está travado?
As sobreposições são ancoradas a palavras exatas, portanto qualquer novo corte depois desloca todos os clipes. Se o usuário fornecer material bruto ou o corte não estiver travado, execute a skill paper-edit primeiro e coloque as sobreposições na linha do tempo que ela produz.
Pipeline
1. Obter o corte e analisá-lo
Peça uma exportação do Resolve (por exemplo, para ~/Movies/), ou material bruto em <YOUR_FOOTAGE_DRIVE> para um paper edit primeiro. Um arquivo de vídeo é suficiente; não é necessário exportar áudio separadamente. Quando o Resolve estiver aberto, leia a linha do tempo através do MCP em vez de pedir uma exportação.
ffprobe -v error -show_entries format=duration -show_entries stream=codec_type,r_frame_rate -of default=nw=1 "<file>"
Anote a taxa de quadros (o renderizador assume 23.976 = 24000/1001; altere FPSN/FPSD em render.mjs para outras taxas) e pergunte se a linha do tempo começa em 00:00:00:00 ou 01:00:00:00 (o padrão do Resolve é 01).
2. Transcrever com timestamps no nível da palavra
ffmpeg -y -i "<file>" -ac 1 -ar 16000 -c:a pcm_s16le cut.wav
whisper-cli -m ~/.cache/whisper-models/ggml-small.en.bin -f cut.wav -oj -ml 1 -sow -of cut
-ml 1 -sow dá um token por segmento, que é o que permite a ancoragem exata. Em seguida, encontre o tempo de início de cada frase combinando execuções de token em cut.json (transcription[].offsets.from é em ms).
3. Escolher os momentos
Leia a transcrição completa primeiro. Busque aproximadamente um clipe por 15 segundos de vídeo. Para cada um: a frase âncora, um tipo e a duração mais curta que permita que a animação apareça e respire.
Fundamente o conteúdo no que foi realmente dito, não em anotações escritas. Se as anotações dizem "30 minutos" e na câmera é "cerca de meia hora", a gravação vence, e sinalize a diferença para o usuário.
4. Escrever clips.json
Copie scripts/clips.example.json como modelo. Um objeto por clipe:
{"id":"06-math-10","start":52.96,"dur":3.6,"type":"dots","cue":"\"if 10 out of 100 meals are home cooked\"",
"data":{"lit":10,"pct":"10%","count":"10 meals"}}
Mantenha-o fora da pasta da skill (um caminho temporário é suficiente) e passe-o com CLIPS=. start é o tempo bruto da frase em segundos; o renderizador adiciona o atraso de 0,12s sozinho. A ordem de id define a ordem dos arquivos, então numere-os.
5. Renderizar um corte limpo (necessário para os tipos em cena)
Os tipos em cena leem a posição da cabeça e isolam o locutor de uma cópia do corte sem sobreposições. Renderize a linha do tempo travada do Resolve com todos os clipes de sobreposição desabilitados, H.264 1080p, no diretório temporário. Na API, SetTrackEnable em V2 não funcionou no Resolve 19; item.SetClipEnabled(False) em cada clipe da faixa funcionou. O material bruto precisa estar online, portanto a unidade de material deve estar montada; se não estiver, peça ao usuário para conectá-la e aguarde a montagem em vez de renderizar um master finalizado (sobreposições antigas seriam incorporadas a ele).
6. Renderizar
cd <skill>/scripts && npm i # primeira vez apenas, instala puppeteer-core
swiftc -O person.swift -o person # primeira vez apenas, o localizador de matte + cabeça
TITLE="<video name>" CLIPS=/path/to/clips.json FOOTAGE=/path/to/clean.mp4 OUT_DIR="$HOME/Movies/<video-name>-overlays" node render.mjs
# um ou alguns clipes apenas:
ONLY=06-math-10,07-math-30 OUT_DIR=... node render.mjs
Aproximadamente 10 a 15 segundos de tempo real por segundo de sobreposição (pair é mais lento, pois aplica matte em todos os quadros). Colocar em segundo plano e verificar depois é a abordagem correta. Ele escreve PLACEMENT.txt e ambos os arquivos .fcpxml no final.
Sempre verifique sobre o vídeo real antes de uma renderização completa. node proof.mjs <clean.mp4> <clips.json> out.jpg [email protected] [email protected] ... renderiza um quadro por clipe (prefixo@segundos após o início do clipe) sobre a gravação real, com matte incluído, organizado em uma imagem. Olhe para ela. Texto saindo do quadro, um título cruzando os olhos, texto sobre um fundo ocupado: tudo invisível no código e óbvio na imagem. Para diagramas, a composição sobre cinza ainda é suficiente.
Quadros de prova: extraia para PNG primeiro. Sobrepor um .mov qtrle diretamente em uma fonte de cor lavfi com -ss resulta em um quadro em branco. Extraia o quadro (ffmpeg -ss 2.8 -i clip.mov -frames:v 1 f.png), depois componha o PNG.
Colocação no Resolve: duplique a linha do tempo travada como YouTube Final, importe os movs para um compartimento Overlays e AppendToTimeline cada um com trackIndex: 2 e recordFrame do timecode IN em PLACEMENT.txt (((h*3600+m*60+s)*24+ff), então 01:00:00:00 = 86400). Analise PLACEMENT.txt uma linha de cada vez (line.split()[0] é o id, [1] o timecode). Uma regex de arquivo inteiro pode ler texto de cue como "60-second" como um id de clipe e roubar o timecode da próxima linha, fazendo com que um clipe não seja colocado silenciosamente. Verifique se a contagem colocada é igual à contagem de clipes.
7. Pré-visualizar e depois entregar
Construa uma pré-visualização de 10 a 15 segundos sobre o vídeo real para que o usuário possa avaliar antes de aplicar:
ffmpeg -y -ss <t> -t 11 -i "<cut>" -itsoffset <a> -i "<clip1>.mov" -itsoffset <b> -i "<clip2>.mov" \
-filter_complex "[0:v][1:v]overlay=0:0:eof_action=pass[v1];[v1][2:v]overlay=0:0:eof_action=pass[vo];[vo]scale=1280:-2[v]" \
-map "[v]" -map 0:a -c:v libx264 -crf 20 -preset veryfast -c:a aac PREVIEW.mp4
O áudio original vem com o vídeo, que é tudo que a prévia precisa.
Em seguida, forneça as etapas de importação (File > Import > Timeline > Import AAF, EDL, XML, escolha o arquivo starts-01, copie os clipes para uma faixa acima do vídeo em 01:00:00:00).
Trabalhando diretamente no Resolve (preferível quando o Resolve está aberto)
Se um servidor MCP resolve estiver instalado (por exemplo, o de terceiros barckley75/resolve-claude-mcp), ele se comunica com a API de script do Resolve Studio. Se a skill paper-edit fornecer um ensure_resolve.sh, execute-o primeiro para que o Resolve seja iniciado se estiver fechado. Com Preferences > System > General > External scripting using: Local, prefira-o em vez do processo de exportação e importação.
O que muda:
- Nenhum corte exportado necessário. Leia a linha do tempo aberta para seus timecodes reais, taxa de quadros, timecode inicial e layout de clipe, depois ancore as sobreposições a isso. Isso corrige "os timecodes mudam toda vez que eu refaço o corte."
- Nenhuma importação manual. Coloque os clipes
.movrenderizados diretamente em uma faixa de vídeo. - Melhor seleção de momentos. Ver onde os cortes já caem permite que as sobreposições se alinhem com a edição em vez de lutar contra ela.
- Também pode iniciar a renderização quando as sobreposições estiverem no lugar.
Regras para usá-lo:
- Leia antes de escrever. Confirme primeiro o nome do projeto, nome da linha do tempo, timecode inicial e taxa de quadros, e repita-os para o usuário.
- Nunca escreva na linha do tempo principal. Duplique-a primeiro e trabalhe na cópia, para que um erro não custe nada.
- Evite
execute_resolve_codea menos que uma ferramenta normal não possa fazer o trabalho. Ele executa Python arbitrário nos projetos do usuário. Se for a única maneira, mostre o código primeiro. - Sessões locais apenas. O Resolve precisa estar rodando neste Mac, portanto isso nunca funciona em execução em nuvem ou agendada. Recorra ao FCPXML nesse caso.
- O caminho do FCPXML ainda funciona e é o fallback. Continue gerando ambos os arquivos; eles custam segundos.
Transcrição de áudio: alguns servidores MCP do Resolve fornecem sua própria transcrição mlx-whisper, mas a etapa whisper.cpp acima é comprovada com este pipeline.
Tipos de clipe
Tudo abaixo está aposentado para novos vídeos (mantido para que vídeos antigos possam ser reeditados). Tipos de tela cheia pintam um fundo de tinta sólida com um brilho suave de destaque, então escondem completamente o locutor. Os chips ficam no canto inferior esquerdo.
| tipo | use para | data |
|---|---|---|
full-dots | aposentado, use dots | lit, pct, count |
full-flow | aposentado, use flow | label, steps[], good |
full-compare | aposentado, use compare | title, left{label,value,sub}, right{...,hi}, vs |
full-bars | aposentado, use bars | title, bars[{label,value,value_label,hi}] |
full-math | aposentado, use math | parts[] (operadores x = + → renderizam na cor de destaque), sub |
full-pack | aposentado, use bars ou side | name, note, size 0-1, hi |
full-stat | aposentado, use pair | label, value, sub, hi |
full-title | aposentado, use pair ou type | title (<br> permitido), size, sub |
full-list | aposentado, use list2 | title, items[], step segundos |
chip | aposentado, use side ou num | num, title, sub, hi |
chip-stack | aposentado, use list2 | items[] |
Os tipos aposentados ainda renderizam, para reedição de vídeos antigos.
Tipos em cena (o padrão)
Sem cartões, o texto vive no ambiente ao lado da cabeça do locutor, e títulos grandes vão ao redor da cabeça.
| tipo | use para | data |
|---|---|---|
pair | um título de impacto: linha com contorno acima da cabeça, linha sólida na altura da testa | top, bottom, outline (top/bottom) |
side | 2-3 linhas curtas ao lado da cabeça, cada uma aparecendo na sua palavra | lines[], accent (índice, grande + cor de destaque), at[] |
type | uma citação ou pergunta digitada palavra por palavra enquanto é dita | words[{w,t,hi,br}] |
define | aposentado: escurece o plano. Use side ou pair | word, sub |
num | número com contorno aparece, palavras ao lado ("01 planeje a semana") | num, lines[] |
list2 | itens construindo à esquerda e/ou direita da cabeça | left[], right[], at[] (em ordem, esquerda primeiro) |
Diagramas em cena (mesma colocação na parede e matte):
| tipo | use para | data |
|---|---|---|
dots | uma taxa em 100, grade de preenchimento ao lado do locutor | lit, pct, count |
flow | um processo, etapas encadeadas de cima para baixo, a última na cor de destaque | label, steps[], at[] opcional |
bars | dois ou três números por tamanho | title, bars[{label,value,value_label,hi}] |
compare | duas opções, uma de cada lado da cabeça | left{label,value,sub}, right{...,hi} |
math | uma equação lida na parede, operadores lideram cada linha | parts[], sub |
at e t são segundos a partir do início do clipe; construa-os a partir dos timestamps das palavras para que cada linha apareça na sua palavra.
Estes precisam de FOOTAGE=, o corte sem sobreposições (renderize a linha do tempo do Resolve com os clipes de sobreposição desabilitados). O renderizador lê a caixa facial e uma grade de brilho de fundo para cada clipe, para que o texto encontre a parede vazia, vire de lado ou encolha se sair do quadro, e mude para tinta em fundo claro e texto claro em fundo escuro. Ele isola o locutor de cada quadro de cada clipe em cena (segmentação de pessoa Apple Vision, person.swift), para que os títulos fiquem atrás da cabeça e as mãos passem na frente dos gráficos. Compile uma vez: swiftc -O person.swift -o person. Sem FOOTAGE=, os tipos em cena caem em camadas de cor sólida e não cavam o locutor.