Communitygithub.com

binay-py/event-recap-video

Convierte un montón de fotos/videos de un evento (conferencia, viaje, retiro, boda) en un video resumen atractivo con ffmpeg: un corte principal 16:9, una versión 9:16 y un reel sincronizado con el ritmo de ~45 s. Úsalo cuando el usuario quiera un video resumen/destacado/montaje a partir de su propio material.

¿Qué es event-recap-video?

Esta habilidad transforma una colección de fotos y videos de un evento (como conferencias, viajes, retiros o bodas) en un video resumen profesional, optimizado para distintas plataformas. Genera tres formatos: un corte principal en 16:9, una versión vertical en 9:16 y un reel de aproximadamente 45 segundos sincronizado con el ritmo de la música. El flujo de trabajo está completamente automatizado con scripts en Python y ffmpeg, desde el análisis del material (detección de duplicados, brillo, desenfoque, volumen) hasta la edición final con transiciones, ajuste de color y normalización de audio. Soporta la descarga de archivos desde Google Photos mediante CDP (Chrome DevTools Protocol) y permite ediciones alternativas sin afectar la principal. Está diseñado para usuarios que desean un video atractivo sin necesidad de editar manualmente, manteniendo un control fino sobre cada detalle.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/binay-py/claude-skills/tree/HEAD/skills/event-recap-video

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

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 proyecto
  • scripts/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-cdp en este repositorio). Los elementos de la cuadrícula son a[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-checked del encabezado puede quedar obsoleto; recarga antes de volver a seleccionar.
  • Browser.setDownloadBehavior con allowAndName guarda el zip bajo su GUID sin extensión; monitorea Browser.downloadProgress hasta completed.

2. Analizar y seleccionar

  • python analyze.py → work/media.json (hora de captura desde EXIF / creation_time del video en zona horaria local, desenfoque, brillo, duplicados, sonoridad) + hojas de contacto por día en work/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_xf 0,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.mp3 estima el tempo.
  • Claves de edit.json: title, subtitle, closing, closing_sub, title_bg / closing_bg (+ _bg_at para 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 en work/seg, cadena xfade, loudnorm −14 LUFS, x264.
  • EDIT=otro.json OUT_SUFFIX=-v2 python build.py renderiza una edición alternativa sin tocar la principal.
  • Control de calidad en cada renderizado: nb_frames == duration*30 (las cadenas xfade pueden perder fotogramas), recuento blackdetect 0, 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 file sobre 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), pegatina cap (caja redondeada, inclinada, pop elástico, emoji de color), títulos day con 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 + audio highpass=80,dynaudnorm,acompressor,loudnorm=I=-14.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills relacionados