Video resumen de eventos
Probado de principio a fin con una biblioteca de 780 elementos. Requiere ffmpeg/ffprobe en PATH y Python 3.10+ con pip install pillow pillow-heif numpy.
Los scripts se encuentran junto a este archivo. Cópialos en el proyecto:
scripts/analyze.py,build.py,reel.py,motion.py,fonts.py→ raíz del proyectoscripts/work/*.py→<proyecto>/work/examples/*.json→ raíz del proyecto, luego edítalos
Estructura del proyecto: raw/ (originales sin modificar), music/, work/ (caché), output/, edit.json (la edición completa).
Las fuentes se encuentran automáticamente en Windows/macOS/Linux; se pueden sobrescribir con FONT_SERIF, FONT_SERIF_BOLD, FONT_SANS_BOLD, FONT_BLACK, FONT_EMOJI.
1. Obtener el material
- Coloca cada exportación/zip en su propia carpeta dentro de
raw/. Nunca modifiques los originales. - Google Photos no tiene API para bibliotecas existentes: controla el navegador del usuario con su sesión iniciada mediante CDP (consulta la habilidad
brave-cdpen este repositorio). Los elementos de la cuadrícula sona[href*="/photo/"]; los encabezados de día son[role=checkbox][aria-label^="Select all photos from"]. - Selecciona los encabezados de día con eventos de ratón reales (
Input.dispatchMouseEvent), verifica el texto"N seleccionados", presiona Shift+D. La pestaña debe estar en primer plano (Page.bringToFront) o la cuadrícula permanece gris. - Google borra la selección después de una descarga y el
aria-checkeddel encabezado puede quedar obsoleto; recarga antes de volver a seleccionar. Browser.setDownloadBehaviorconallowAndNameguarda el zip bajo su GUID sin extensión; monitoreaBrowser.downloadProgresshastacompleted.
2. Analizar y seleccionar
python analyze.py→work/media.json(hora de captura desde EXIF /creation_timedel video en zona horaria local, desenfoque, brillo, duplicados, sonoridad) + hojas de contacto por día enwork/sheets/.python work/vsheets.py→ tiras de 3 fotogramas por video. MIRA cada hoja. Omite clips que giran durante la grabación, tomas de bolsillo y clips nocturnos oscuros.python work/loudest.py ID:segundos ...encuentra la ventana más ruidosa (canto, discurso, vítores). Verifícalo con una tira de 1 fps antes de recortar.- Los archivos compartidos/enviados no tienen fecha de captura (se usa la fecha de modificación del archivo) — colócalos a ojo.
3. Edición (lo que gusta al público)
- Empieza con VIDEO (~90% clips); fotos solo para tomas grupales fuertes. Los cortos con muchas fotos se sienten lentos.
- Gancho de apertura en frío: 8–10 clips de 1,2–1,8 s,
hook_xf0,1 cortes casi duros, luego la tarjeta de título. Luego los días en orden con un pequeño"label": "Día N · fecha"en el primer clip de cada día, cortes de 3–4 s, y algunos momentos de 9–12 s marcados con"speech": true(la música baja bajo ellos). - La música hace o deshace el gancho. Encadena 2 pistas de tempo similar (
"music": [a, b], fundido cruzado de 3 s) en lugar de repetir una.python work/bpm.py archivo.mp3estima el tempo. - Claves de
edit.json:title, subtitle, closing, closing_sub, title_bg / closing_bg(+_bg_atpara ids de video),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(un fotograma en cada corte). Muéstraselo al usuario antes de renderizar.
4. Renderizar y control de calidad
python build.py [horizontal|vertical|vertical-full]— caché por segmento enwork/seg, cadena xfade, loudnorm −14 LUFS, x264.EDIT=otro.json OUT_SUFFIX=-v2 python build.pyrenderiza una edición alternativa sin tocar la principal.- Control de calidad en cada renderizado:
nb_frames == duration*30(las cadenas xfade pueden perder fotogramas), recuentoblackdetect0, ebur128 ≈ −14 LUFS, y una hoja de mosaico de fotogramas (fps=1/5,tile=8x6) para ver rotación y títulos cortados. - ffmpeg 7+ prefiere
-/filter_complex filesobre el obsoleto-filter_complex_script.
5. Reel sincronizado con el ritmo (IG / TikTok / FB) — reel.py + reel.json
python reel.py→output/reel.mp4(~45 s), construido sobre el renderizador de segmentos de build.py.- Los cortes caen exactamente en la cuadrícula de ritmo (pista
bpm, fase automática, tiempos de corte redondeados a fotogramas). Verifica el tempo: los estimadores ingenuos a menudo devuelven la mitad/doble (85 vs 154). - Primer segundo = el momento más sorprendente, con una línea "POV: …". Cortes de 4 tiempos, 6–8 tiempos en momentos destacados, lento zoom hacia adentro en cada clip, destello blanco al inicio de las secciones.
- Texto: sans serif grueso, blanco con contorno negro grueso, ~24% desde arriba (libre de botones de TikTok/IG), aparición deslizante hacia arriba. 5–7 líneas cortas en total; la última es el nombre del evento.
6. Corte de meme/movimiento — motion.py + motion.json (solo cuando el usuario pida memes/pegatinas)
- Muchas personas encuentran este estilo demasiado exagerado para un evento emotivo. Por defecto, usa las secciones 3–5.
- Una pista larga y constante (~3 min) en lugar de encadenar; cuadrícula de ritmo mediante búsqueda de filtro comb.
- Por clip:
beats,speed(0,5 cámara lenta / 2 avance rápido),freeze(tiempos; parada de cinta + silencio + desaturación), pegatinacap(caja redondeada, inclinada, pop elástico, emoji de color), títulosdaycon golpe + whoosh,title,verse,shake,punch,flash,zoom. - Si un renderizado se cancela, elimina los archivos de clip que fallen en ffprobe antes de volver a ejecutar (la caché los reutilizaría).
7. Entrega vertical y para redes sociales
- Cuando alguien apruebe la edición 16:9 y pida 9:16, dale la MISMA edición en vertical (
python build.py vertical-full). No rediseñes una edición aprobada; "hacerlo más atractivo" significa solo pulir. - Las plataformas (especialmente Facebook) silencian las publicaciones con música sin licencia. Ofrece una versión con sonido original (una edición con
"music": null) y/o una copia silenciosa (ffmpeg -i in.mp4 -an -c copy out.mp4) para que se pueda añadir música desde la biblioteca con licencia de la plataforma. - Pulido 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+ audiohighpass=80,dynaudnorm,acompressor,loudnorm=I=-14.