イベントレキャップ動画
780点のイベントライブラリでエンドツーエンドのテスト済み。ffmpeg/ffprobe がPATHに含まれていることと、Python 3.10+ で pip install pillow pillow-heif numpy が必要です。
スクリプトはこのファイルの隣にあります。プロジェクトにコピーしてください:
scripts/analyze.py,build.py,reel.py,motion.py,fonts.py→ プロジェクトルートscripts/work/*.py→<プロジェクト>/work/examples/*.json→ プロジェクトルート(編集して使用)
プロジェクト構成: raw/(未加工のオリジナル), music/, work/(キャッシュ), output/, edit.json(編集全体)。
フォントはWindows/macOS/Linuxで自動的に検出されます。FONT_SERIF, FONT_SERIF_BOLD, FONT_SANS_BOLD, FONT_BLACK, FONT_EMOJI で上書き可能です。
1. メディアを入手する
- 各エクスポート/zipを
raw/の下に独自フォルダに配置します。オリジナルは絶対に変更しないでください。 - Google Photosには既存のライブラリ用のAPIがありません。CDP(このリポジトリの
brave-cdpスキルを参照)を介して、ユーザーのログイン済みブラウザを操作します。グリッドアイテムはa[href*="/photo/"]、日付ヘッダーは[role=checkbox][aria-label^="Select all photos from"]です。 - 日付ヘッダーは実際のマウスイベント(
Input.dispatchMouseEvent)で選択し、"N selected"のテキストを確認し、Shift+D を押します。タブは前面にある必要があります(Page.bringToFront)。そうしないとグリッドがグレー表示のままになります。 - Googleはダウンロード後に選択をクリアするため、ヘッダーの
aria-checkedが古くなる可能性があります。再選択する前にリロードしてください。 Browser.setDownloadBehaviorをallowAndNameで設定すると、zipがGUIDのまま拡張子なしで保存されます。Browser.downloadProgressでcompletedを監視します。
2. 分析と選択
python analyze.py→work/media.json(EXIF / 動画creation_timeからローカルタイムゾーンでの撮影時間、ブラー、明るさ、重複、ラウドネス)+ 日ごとのコンタクトシートがwork/sheets/に生成されます。python work/vsheets.py→ 動画あたり3フレームのストリップを生成。すべてのシートを確認してください。撮影中に回転するクリップ、ポケットショット、暗い夜のクリップはスキップします。python work/loudest.py ID:seconds ...で最もラウドなウィンドウ(歌、スピーチ、歓声)を見つけます。トリミング前に1 fpsのフレームストリップで確認してください。- 共有/転送されたファイルには撮影日がありません(ファイルのmtimeにフォールバック)—目視で配置します。
3. 編集(視聴者が好むもの)
- 動画を先頭に配置(クリップの約90%)。写真は強力なグループショットにのみ使用します。写真中心のカットは間延びします。
- 冷たいオープンフック: 1.2~1.8秒のクリップを8~10個、
hook_xf0.1でほぼハードカット、その後タイトルカード。次に日付順に、各日の最初のクリップに小さな"label": "Day N · 日付"を付け、3~4秒のカット、そして"speech": trueとフラグが立てられた数個の9~12秒の瞬間(音楽がその下にダックします)。 - 音楽でフックの良し悪しが決まります。同じテンポの2トラックをチェーン(
"music": [a, b]、3秒クロスフェード)で接続し、ループを避けます。python work/bpm.py file.mp3でテンポを推定します。 edit.jsonのキー:title, subtitle, closing, closing_sub, title_bg / closing_bg(+_bg_at動画ID用)、music/vertical_music,music_volume, duck_volume, ambient_volume, photo_seconds, hook_xf, closing_seconds, horizontal_hook, horizontal, vertical_hook, vertical。セグメント:{"id", "at" (0–1 中央) | "start", "dur", "speech", "label", "xf"}。python work/shortlist.py→output/shortlist.jpg(すべてのカット位置のフレーム)。レンダリング前にユーザーに表示します。
4. レンダリングとQC
python build.py [horizontal|vertical|vertical-full]— セグメントごとのキャッシュをwork/segに、xfadeチェーン、loudnorm −14 LUFS、x264。EDIT=other.json OUT_SUFFIX=-v2 python build.pyでメインの編集に触れずに代替編集をレンダリングします。- すべてのレンダリングをQC:
nb_frames == duration*30(xfadeチェーンはフレームを落とす可能性あり)、blackdetectカウント0、ebur128 ≈ −14 LUFS、およびフレームのタイルシート(fps=1/5,tile=8x6)で回転やタイトルの切り捨てを目視確認。 - ffmpeg 7以降は非推奨の
-filter_complex_scriptよりも-/filter_complex fileを推奨します。
5. ビート同期リール(IG / TikTok / FB)— reel.py + reel.json
python reel.py→output/reel.mp4(約45秒)、build.pyのセグメントレンダラー上に構築。- カットは正確にビートグリッド上に配置されます(
bpmヒント、自動位相、フレーム丸めカット時刻)。テンポを再確認: 単純な推定器はしばしば半分/倍(85 vs 154)を返します。 - 最初の1秒は最も驚くべき瞬間で、"POV: …" の行を付けます。4ビートカット、ヒーローモーメントには6~8ビート、すべてのクリップにスロープッシュイン、セクション開始時にホワイトフラッシュ。
- テキスト: 太いサンセリフ、白に太い黒アウトライン、上部から約24%(TikTok/IGボタンに被らない位置)、スライドアップフェード。合計5~7行の短いテキスト。最後の行はイベント名。
6. ミーム/モーションカット — motion.py + motion.json(ユーザーがミーム/ステッカーを要求した場合のみ)
- 多くの人は、このスタイルは心のこもったイベントにはやりすぎだと感じます。デフォルトではセクション3~5を使用します。
- 1つの長い安定したトラック(約3分)をチェーンせずに使用。ビートグリッドはコムフィルター検索で検出します。
- クリップごと:
beats,speed(0.5スローモーション / 2早送り),freeze(ビート数; テープストップ+無音+彩度低下),capステッカー(角丸、傾き、弾むポップ、カラー絵文字),dayスラムタイトル+ウーッシュ効果,title,verse,shake,punch,flash,zoom。 - レンダリングが強制終了された場合、ffprobeが失敗したクリップファイルを削除してから再実行してください(キャッシュが再利用されます)。
7. 縦長・ソーシャル配信
- 誰かが16:9編集を承認し、9:16を要求した場合、同じ編集を縦長で提供します(
python build.py vertical-full)。承認済みの編集を再スタイルしないでください。「もっと魅力的にして」は研磨のみを意味します。 - プラットフォーム(特にFacebook)は無許可音楽を含む投稿をミュートします。オリジナルサウンド版(
"music": nullの編集)や無音コピー(ffmpeg -i in.mp4 -an -c copy out.mp4)を提供し、プラットフォームのライセンスライブラリから音楽を追加できるようにします。 - 最終研磨:
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+ オーディオhighpass=80,dynaudnorm,acompressor,loudnorm=I=-14。