Vídeo de resumo de evento
Testado de ponta a ponta em uma biblioteca de 780 itens. Requer ffmpeg/ffprobe no PATH e Python 3.10+ com pip install pillow pillow-heif numpy.
Os scripts ficam ao lado deste arquivo. Copie para o projeto:
scripts/analyze.py,build.py,reel.py,motion.py,fonts.py→ raiz do projetoscripts/work/*.py→<project>/work/examples/*.json→ raiz do projeto, depois edite-os
Estrutura do projeto: raw/ (originais intocados), music/, work/ (cache), output/, edit.json (a edição completa).
As fontes são encontradas automaticamente no Windows/macOS/Linux; sobrescreva com FONT_SERIF, FONT_SERIF_BOLD, FONT_SANS_BOLD, FONT_BLACK, FONT_EMOJI.
1. Obter mídia
- Coloque cada exportação/zip em sua própria pasta em
raw/. Nunca modifique os originais. - O Google Photos não tem API para uma biblioteca existente: conduza o navegador logado do usuário via CDP (veja o skill
brave-cdpneste repositório). Itens da grade sãoa[href*="/photo/"]; cabeçalhos de dia são[role=checkbox][aria-label^="Select all photos from"]. - Selecione cabeçalhos de dia com eventos reais de mouse (
Input.dispatchMouseEvent), verifique o texto"N selected", pressione Shift+D. A aba deve estar em primeiro plano (Page.bringToFront) ou a grade fica cinza. - O Google limpa a seleção após um download e o
aria-checkeddo cabeçalho pode ficar desatualizado; recarregue antes de reselecionar. Browser.setDownloadBehaviorcomallowAndNamesalva o zip sob seu GUID sem extensão; monitoreBrowser.downloadProgressparacompleted.
2. Analisar e selecionar
python analyze.py→work/media.json(hora de captura do EXIF /creation_timedo vídeo no fuso local, desfoque, brilho, duplicatas, loudness) + contatos por dia emwork/sheets/.python work/vsheets.py→ tiras de 3 quadros por vídeo. VEJA cada folha. Pule clipes que giram durante a gravação, gravações de bolso e clipes noturnos escuros.python work/loudest.py ID:seconds ...encontra a janela mais alta (canto, fala, comemoração). Verifique com uma tira de quadros de 1 fps antes de cortar.- Arquivos compartilhados/encaminhados não têm data de captura (recua para mtime do arquivo) — posicione-os visualmente.
3. Edição (o que o público gostou)
- Comece com VÍDEO (~90% dos clipes); fotos apenas para fotos fortes de grupo. Cortes com muitas fotos parecem lentos.
- Hook de abertura fria: 8–10 clipes de 1,2–1,8 s,
hook_xf0,1 cortes quase secos, depois o cartão de título. Em seguida, dias em ordem com um pequeno"label": "Dia N · data"no primeiro clipe de cada dia, cortes de 3–4 s, e alguns momentos de 9–12 s marcados com"speech": true(a música diminui sob eles). - A música faz ou quebra o hook. Encadeie 2 faixas de ritmo semelhante (
"music": [a, b], crossfade de 3 s) em vez de repetir uma.python work/bpm.py arquivo.mp3estima o ritmo. - Chaves do
edit.json:title, subtitle, closing, closing_sub, title_bg / closing_bg(+_bg_atpara ids de vídeo),music/vertical_music,music_volume, duck_volume, ambient_volume, photo_seconds, hook_xf, closing_seconds, horizontal_hook, horizontal, vertical_hook, vertical. Segmento:{"id", "at" (centro 0–1) | "start", "dur", "speech", "label", "xf"}. python work/shortlist.py→output/shortlist.jpg(um quadro em cada corte). Mostre ao usuário antes de renderizar.
4. Renderizar e QC
python build.py [horizontal|vertical|vertical-full]— cache por segmento emwork/seg, cadeia xfade, loudnorm −14 LUFS, x264.EDIT=outro.json OUT_SUFFIX=-v2 python build.pyrenderiza uma edição alternativa sem mexer na principal.- QC de cada renderização:
nb_frames == duration*30(cadeias xfade podem perder quadros), contagemblackdetect0, ebur128 ≈ −14 LUFS, e uma folha de tiles de quadros (fps=1/5,tile=8x6) para verificar rotação e títulos cortados. - ffmpeg 7+ prefere
-/filter_complex fileem vez do obsoleto-filter_complex_script.
5. Reel sincronizado com batida (IG / TikTok / FB) — reel.py + reel.json
python reel.py→output/reel.mp4(~45 s), construído sobre o renderizador de segmentos do build.py.- Os cortes caem exatamente na grade de batidas (dica
bpm, fase automática, tempos de corte arredondados por quadro). Verifique o ritmo: estimativas ingênuas geralmente retornam metade/dobro (85 vs 154). - Primeiro segundo = o momento mais surpreendente, com uma linha "POV: …". Cortes de 4 batidas, 6-8 batidas em momentos heroicos, zoom lento para dentro em cada clipe, flash branco no início das seções.
- Texto: sem serifa pesada, branco com contorno preto grosso, ~24% do topo (livre dos botões do TikTok/IG), fade up com deslizamento. 5–7 linhas curtas no total; a última é o nome do evento.
6. Corte de meme / motion — motion.py + motion.json (apenas quando o usuário pedir memes/stickers)
- Muitas pessoas acham esse estilo exagerado para um evento emocionante. Padrão para seções 3–5.
- Uma faixa longa e constante (~3 min) em vez de encadeamento; grade de batida via busca de filtro comb.
- Por clipe:
beats,speed(0,5 câmera lenta / 2 avanço rápido),freeze(batidas; parada de fita + silêncio + dessaturar), adesivocap(caixa arredondada, inclinação, pop elástico, emoji colorido), títulosdaycom quem e woosh,title,verse,shake,punch,flash,zoom. - Se uma renderização for morta, exclua os arquivos de clipe que falham no ffprobe antes de reexecutar (o cache os reutilizaria).
7. Entrega vertical e social
- Quando alguém aprovar a edição 16:9 e pedir 9:16, entregue a MESMA edição vertical (
python build.py vertical-full). Não reformate uma edição aprovada; "torná-la mais atraente" significa apenas polimento. - Plataformas (especialmente Facebook) silenciam posts com música não licenciada. Ofereça uma versão com som original (uma edição com
"music": null) e/ou uma cópia silenciosa (ffmpeg -i in.mp4 -an -c copy out.mp4) para que a música possa ser adicionada da biblioteca licenciada da plataforma. - Polimento final:
eq=contrast=1.07:saturation=1.2:gamma=0.97,colorbalance=rm=0.03:bm=-0.03:rh=0.02:bh=-0.02,unsharp=5:5:0.45,vignette=angle=PI/5.5+ áudiohighpass=80,dynaudnorm,acompressor,loudnorm=I=-14.