Que fait Vidéo récapitulative d'événement ?
Testé de bout en bout sur une bibliothèque de 780 éléments. Nécessite ffmpeg/ffprobe dans le PATH et Python 3.10+ avec pip install pillow pillow-heif numpy.
Les scripts se trouvent à côté de ce fichier. Copiez-les dans le projet :
scripts/analyze.py,build.py,reel.py,motion.py,fonts.py→ racine du projetscripts/work/*.py→<projet>/work/examples/*.json→ racine du projet, puis modifiez-les
Structure du projet : raw/ (originaux non modifiés), music/, work/ (cache), output/, edit.json (le montage complet).
Les polices sont trouvées automatiquement sur Windows/macOS/Linux ; remplacez avec FONT_SERIF, FONT_SERIF_BOLD, FONT_SANS_BOLD, FONT_BLACK, FONT_EMOJI.
1. Obtenir les médias
- Placez chaque export/zip dans son propre dossier sous
raw/. Ne modifiez jamais les originaux. - Google Photos n'a pas d'API pour une bibliothèque existante : pilotez le navigateur connecté de l'utilisateur via CDP (voir le skill
brave-cdpdans ce dépôt). Les éléments de la grille sonta[href*="/photo/"]; les en-têtes de jour sont[role=checkbox][aria-label^="Select all photos from"]. - Sélectionnez les en-têtes de jour avec de VRAIS événements souris (
Input.dispatchMouseEvent), vérifiez le texte"N selected", appuyez sur Shift+D. L'onglet doit être au premier plan (Page.bringToFront) sinon la grille reste grise. - Google efface la sélection après un téléchargement et l'attribut
aria-checkeddes en-têtes peut devenir obsolète ; rechargez avant de resélectionner. Browser.setDownloadBehavioravecallowAndNameenregistre le zip sous son GUID sans extension ; surveillezBrowser.downloadProgresspour l'étatcompleted.
2. Analyser et choisir
python analyze.py→work/media.json(heure de capture depuis EXIF /creation_timevidéo en fuseau horaire local, flou, luminosité, doublons, loudness) + planches-contact par jour danswork/sheets/.python work/vsheets.py→ bandes de 3 images par vidéo. REGARDEZ chaque planche. Ignorez les clips qui tournent pendant l'enregistrement, les plans de poche et les clips de nuit sombres.python work/loudest.py ID:secondes ...trouve la fenêtre la plus forte (chant, discours, acclamations). Vérifiez avec une bande d'images à 1 ips avant de recadrer.- Les fichiers partagés/transférés n'ont pas de date de capture (retour à la date de modification) — placez-les manuellement.
3. Montage (ce que le public a aimé)
- Commencez par la VIDÉO (~90% des clips) ; les photos uniquement pour les plans de groupe forts. Les montages chargés en photos semblent lents.
- Accroche d'ouverture : 8–10 clips de 1,2–1,8 s,
hook_xf0,1 coupures quasi-sèches, puis le carton titre. Ensuite, les jours dans l'ordre avec un petit"label": "Jour N · date"sur le premier clip de chaque jour, coupures de 3–4 s, et quelques moments de 9–12 s marqués"speech": true(la musique baisse sous eux). - La musique fait ou défait l'accroche. Enchaînez 2 morceaux de tempo similaire (
"music": [a, b], fondu enchaîné de 3 s) plutôt que d'en boucler un seul.python work/bpm.py fichier.mp3estime le tempo. - Clés de
edit.json:title, subtitle, closing, closing_sub, title_bg / closing_bg(+_bg_atpour les ids vidéo),music/vertical_music,music_volume, duck_volume, ambient_volume, photo_seconds, hook_xf, closing_seconds, horizontal_hook, horizontal, vertical_hook, vertical. Segment :{"id", "at" (centre 0–1) | "start", "dur", "speech", "label", "xf"}. python work/shortlist.py→output/shortlist.jpg(une image à chaque coupure). Montrez-la à l'utilisateur avant de rendre.
4. Rendu et contrôle qualité
python build.py [horizontal|vertical|vertical-full]— cache par segment danswork/seg, chaîne xfade, loudnorm −14 LUFS, x264.EDIT=autre.json OUT_SUFFIX=-v2 python build.pyrend un montage alternatif sans toucher au principal.- Contrôle qualité de chaque rendu :
nb_frames == duration*30(les chaînes xfade peuvent perdre des images), nombre deblackdetect= 0, ebur128 ≈ −14 LUFS, et une planche d'images (fps=1/5,tile=8x6) pour vérifier la rotation et les titres coupés. - ffmpeg 7+ préfère
-/filter_complex fileau-filter_complex_scriptdéprécié.
5. Reel synchronisé sur le rythme (IG / TikTok / FB) — reel.py + reel.json
python reel.py→output/reel.mp4(~45 s), construit sur le renderer de segments de build.py.- Les coupures tombent exactement sur la grille de temps (
bpmindice, phase automatique, temps de coupure arrondis à l'image). Vérifiez le tempo : les estimateurs naïfs renvoient souvent la moitié/double (85 vs 154). - Première seconde = le moment le plus surprenant, avec une ligne "POV: …". Coupures de 4 temps, 6–8 temps sur les moments forts, léger zoom avant sur chaque clip, flash blanc au début des sections.
- Texte : sans sérif gras, blanc avec contour noir épais, ~24% depuis le haut (hors des boutons TikTok/IG), apparition glissée avec fondu. 5–7 lignes courtes au total ; la dernière est le nom de l'événement.
6. Coupe mème / mouvement — motion.py + motion.json (uniquement si l'utilisateur demande des mèmes/autocollants)
- Beaucoup trouvent ce style trop chargé pour un événement sincère. Par défaut, utilisez les sections 3–5.
- Une seule piste longue et stable (~3 min) au lieu d'enchaîner ; grille de temps via recherche par filtre en peigne.
- Par clip :
beats,speed(0,5 ralenti / 2 avance rapide),freeze(temps ; arrêt sur bande + silence + désaturation), autocollantcap(boîte arrondie, inclinaison, pop élastique, emoji couleur), titresdaypercutants + whoosh,title,verse,shake,punch,flash,zoom. - Si un rendu est tué, supprimez les fichiers de clip qui échouent à ffprobe avant de relancer (le cache les réutiliserait).
7. Livraison verticale et pour les réseaux sociaux
- Lorsque quelqu'un approuve le montage 16:9 et demande du 9:16, donnez-lui le MÊME montage en vertical (
python build.py vertical-full). Ne restylez pas un montage approuvé ; « rendez-le plus attrayant » signifie seulement peaufiner. - Les plateformes (surtout Facebook) coupent le son des publications avec de la musique non licenciée. Proposez une version son original (un montage avec
"music": null) et/ou une copie silencieuse (ffmpeg -i in.mp4 -an -c copy out.mp4) pour que la musique puisse être ajoutée depuis la bibliothèque licenciée de la plateforme. - Polissage 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.