Event Recap Video
Ende-zu-Ende getestet auf einem Event-Fundus von 780 Elementen. Erfordert ffmpeg/ffprobe im PATH und Python 3.10+ mit pip install pillow pillow-heif numpy.
Skripte befinden sich neben dieser Datei. In das Projekt kopieren:
scripts/analyze.py,build.py,reel.py,motion.py,fonts.py→ Projekt-Rootscripts/work/*.py→<projekt>/work/examples/*.json→ Projekt-Root, dann bearbeiten
Projektstruktur: raw/ (unberührte Originale), music/, work/ (Cache), output/, edit.json (der gesamte Schnitt).
Schriftarten werden automatisch unter Windows/macOS/Linux gefunden; Überschreibung mit FONT_SERIF, FONT_SERIF_BOLD, FONT_SANS_BOLD, FONT_BLACK, FONT_EMOJI.
1. Medien beschaffen
- Jeden Export/Jedes Zip in einen eigenen Ordner unter
raw/legen. Originale niemals verändern. - Google Photos hat keine API für eine bestehende Bibliothek: Den eigenen, eingeloggten Browser des Nutzers über CDP steuern (siehe das
brave-cdp-Skill in diesem Repo). Grid-Elemente sinda[href*="/photo/"]; Tages-Header sind[role=checkbox][aria-label^="Select all photos from"]. - Tages-Header mit echten Mouse-Events (
Input.dispatchMouseEvent) auswählen, den Text"N ausgewählt"verifizieren, Shift+D drücken. Der Tab muss im Vordergrund sein (Page.bringToFront), sonst bleibt das Grid grau. - Google löscht die Auswahl nach einem Download und der Header
aria-checkedkann veralten; vor dem erneuten Auswählen neu laden. Browser.setDownloadBehaviormitallowAndNamespeichert das Zip unter seiner GUID ohne Erweiterung;Browser.downloadProgressaufcompletedüberwachen.
2. Analysieren und auswählen
python analyze.py→work/media.json(Aufnahmezeit aus EXIF / Videocreation_timein lokaler Zeitzone, Unschärfe, Helligkeit, Duplikate, Lautstärke) + tagesweise Kontaktbögen inwork/sheets/.python work/vsheets.py→ 3-Frame-Streifen pro Video. JEDEN Bogen ansehen. Clips überspringen, die sich während der Aufnahme drehen, Taschenaufnahmen und dunkle Nachtclips.python work/loudest.py ID:Sekunden ...findet das lauteste Fenster (Gesang, Sprache, Jubel). Mit einem 1-fps-Frame-Streifen überprüfen, bevor getrimmt wird.- Geteilte/weitergeleitete Dateien haben kein Aufnahmedatum (Fallback auf Datei-mtime) – nach Augenmaß platzieren.
3. Schnitt (was dem Publikum gefällt)
- Mit VIDEO führen (~90% Clips); Fotos nur für starke Gruppenaufnahmen. Fotolastige Schnitte wirken langsam.
- Cold-Open-Hook: 8–10 Clips von 1,2–1,8 s,
hook_xf0,1 fast harte Schnitte, dann der Titel-Text. Dann Tage in Reihenfolge mit einem kleinen"label": "Tag N · Datum"auf dem ersten Clip jedes Tages, 3–4 s Schnitte und ein paar 9–12 s Momente, die mit"speech": truemarkiert sind (Musik duckt darunter). - Musik macht oder bricht den Hook. Zwei Tracks mit ähnlichem Tempo verketten (
"music": [a, b], 3 s Crossfade) anstatt einen zu loopen.python work/bpm.py datei.mp3schätzt das Tempo. edit.json-Schlüssel:title, subtitle, closing, closing_sub, title_bg / closing_bg(+_bg_atfür Video-IDs),music/vertical_music,music_volume, duck_volume, ambient_volume, photo_seconds, hook_xf, closing_seconds, horizontal_hook, horizontal, vertical_hook, vertical. Segment:{"id", "at" (0–1 Mitte) | "start", "dur", "speech", "label", "xf"}.python work/shortlist.py→output/shortlist.jpg(ein Frame an jedem Schnitt). Vor dem Rendern dem Nutzer zeigen.
4. Rendern und Qualitätskontrolle
python build.py [horizontal|vertical|vertical-full]— segmentweiser Cache inwork/seg, xfade-Kette, loudnorm −14 LUFS, x264.EDIT=anderes.json OUT_SUFFIX=-v2 python build.pyrendert einen alternativen Schnitt, ohne den Hauptschnitt zu berühren.- Jeden Render prüfen:
nb_frames == dauer*30(xfade-Ketten können Frames verlieren),blackdetect-Zähler 0, ebur128 ≈ −14 LUFS, und ein Tile-Bogen von Frames (fps=1/5,tile=8x6) zur Sichtprüfung von Rotation und abgeschnittenen Titeln. - ffmpeg 7+ bevorzugt
-/filter_complex filegegenüber dem veralteten-filter_complex_script.
5. Beat-synchroner Reel (IG / TikTok / FB) — reel.py + reel.json
python reel.py→output/reel.mp4(~45 s), basierend auf dem Segment-Renderer von build.py.- Schnitte landen exakt auf dem Beat-Gitter (
bpm-Hinweis, Auto-Phase, auf Frames gerundete Schnittzeiten). Tempo doppelt prüfen: Naive Schätzer liefern oft die Hälfte/das Doppelte (85 vs. 154). - Erste Sekunde = der überraschendste Moment, mit einer „POV: …"-Zeile. 4-Beat-Schnitte, 6–8 Beats bei Heldenmomenten, langsames Hereinziehen bei jedem Clip, weißer Blitz bei Abschnittsbeginn.
- Text: schwere serifenlose Schrift, weiß mit dicker schwarzer Kontur, ~24% vom oberen Rand (frei von TikTok/IG-Buttons), Slide-Up-Fade. Insgesamt 5–7 kurze Zeilen; die letzte ist der Event-Name.
6. Meme / Bewegungsschnitt — motion.py + motion.json (nur wenn der Nutzer nach Memes/Stickern fragt)
- Viele Leute finden diesen Stil für ein herzliches Event zu übertrieben. Standardmäßig auf Abschnitte 3–5 beschränken.
- Ein langer, gleichmäßiger Track (~3 min) anstelle einer Kette; Beat-Gitter über Comb-Filter-Suche.
- Pro Clip:
beats,speed(0,5 Zeitlupe / 2 Vorspulen),freeze(Beats; Tonbandstopp + Stille + Entsättigen),cap-Sticker (abgerundetes Kästchen, Neigung, elastischer Pop, farbiges Emoji),day-Wucht-Titel + Whoosh,title,verse,shake,punch,flash,zoom. - Wird ein Render abgebrochen, Clip-Dateien löschen, die bei ffprobe fehlschlagen, bevor neu gestartet wird (der Cache würde sie wiederverwenden).
7. Vertikale und Social-Media-Auslieferung
- Wenn jemand den 16:9-Schnitt genehmigt und nach 9:16 fragt, den GLEICHEN Schnitt vertikal ausliefern (
python build.py vertical-full). Keinen genehmigten Schnitt umstylen; „attraktiver machen" bedeutet nur Polieren. - Plattformen (besonders Facebook) stummschalten Beiträge mit unlizenzierter Musik. Eine Version mit Originalton (ein Schnitt mit
"music": null) und/oder eine stumme Kopie anbieten (ffmpeg -i in.mp4 -an -c copy out.mp4), damit Musik aus der lizenzierten Bibliothek der Plattform hinzugefügt werden kann. - Finales Polieren:
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.