← back to Beverlyhillsvideos
social: retool every film into all social dimensions (9:16 Reels/TikTok/Shorts, 4:5 IG feed, 1:1 IG feed) — pipeline + reprocess for existing 18
839d4d40b1052aa8bd93e72333bfc4d8705fdef2 · 2026-08-05 16:46:26 -0700 · Steve Abrams
Files touched
M filmgen/heygen_batch.mjsA filmgen/social_reframe.shM social/LAUNCH-PACK.md
Diff
commit 839d4d40b1052aa8bd93e72333bfc4d8705fdef2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 5 16:46:26 2026 -0700
social: retool every film into all social dimensions (9:16 Reels/TikTok/Shorts, 4:5 IG feed, 1:1 IG feed) — pipeline + reprocess for existing 18
---
filmgen/heygen_batch.mjs | 19 ++++++++++++++-----
filmgen/social_reframe.sh | 17 +++++++++++++++++
social/LAUNCH-PACK.md | 9 ++++++---
3 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/filmgen/heygen_batch.mjs b/filmgen/heygen_batch.mjs
index 202ed0d..338515d 100644
--- a/filmgen/heygen_batch.mjs
+++ b/filmgen/heygen_batch.mjs
@@ -15,6 +15,16 @@ const CONCURRENCY = 3;
const FLOOR = 130; // stop submitting if wallet drops below this
const RATE = 0.0337; // $/sec, measured from wallet delta
const VIDEO_DIR = ROOT + 'public/video';
+// social-media aspect ratios (blurred-fill, whole 16:9 frame preserved, no crop)
+const SOCIAL = [
+ { dir: 'vertical', w: 1080, h: 1920 }, // 9:16 — Reels / TikTok / YouTube Shorts
+ { dir: 'portrait', w: 1080, h: 1350 }, // 4:5 — Instagram feed (best engagement)
+ { dir: 'square', w: 1080, h: 1080 }, // 1:1 — Instagram feed
+];
+SOCIAL.forEach(s => { try { mkdirSync(`${VIDEO_DIR}/${s.dir}`, { recursive: true }); } catch {} });
+function reframe(src, w, h, out) {
+ sh(`ffmpeg -y -i ${src} -filter_complex "[0:v]scale=${w}:${h}:force_original_aspect_ratio=increase,crop=${w}:${h},gblur=sigma=24,eq=brightness=-0.10[bg];[0:v]scale=${w}:-1[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2,format=yuv420p[v]" -map "[v]" -map 0:a -c:v libx264 -preset medium -crf 21 -c:a aac -b:a 128k ${out} 2>/dev/null`);
+}
const sh = (c) => execSync(c, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
const slug = (n) => n.toLowerCase().replace(/&/g,'and').replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');
@@ -79,11 +89,10 @@ function download(r) {
sh(`ffmpeg -y -ss ${start} -i ${raw} -t 29 -c:v libx264 -preset medium -crf 20 -c:a aac -b:a 128k -movflags +faststart ${mp4} 2>/dev/null`);
try { sh(`rm -f ${raw}`); } catch {}
try { sh(`ffmpeg -y -ss 1 -i ${mp4} -vframes 1 -q:v 3 ${VIDEO_DIR}/${r.slug}.jpg 2>/dev/null`); } catch {}
- // 9:16 vertical for Instagram Reels / TikTok, built from the trimmed film
- try {
- const v = `${VIDEO_DIR}/vertical/${r.slug}.mp4`;
- sh(`ffmpeg -y -i ${mp4} -filter_complex "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,gblur=sigma=26,eq=brightness=-0.10[bg];[0:v]scale=1080:-1[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2,format=yuv420p[v]" -map "[v]" -map 0:a -c:v libx264 -preset medium -crf 21 -c:a aac -b:a 128k ${v} 2>/dev/null`);
- } catch {}
+ // social-media aspect ratios (vertical 9:16, portrait 4:5, square 1:1), from the trimmed film
+ for (const s of SOCIAL) {
+ try { reframe(mp4, s.w, s.h, `${VIDEO_DIR}/${s.dir}/${r.slug}.mp4`); } catch {}
+ }
r.downloaded = true;
}
diff --git a/filmgen/social_reframe.sh b/filmgen/social_reframe.sh
new file mode 100755
index 0000000..f52d2b1
--- /dev/null
+++ b/filmgen/social_reframe.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Generate all social aspect ratios (9:16 vertical, 4:5 portrait, 1:1 square)
+# for every film in public/video/. Blurred-fill, whole frame preserved. Idempotent.
+cd /Users/macstudio3/Projects/beverlyhillsvideos || exit 1
+VD=public/video
+mkdir -p "$VD/vertical" "$VD/portrait" "$VD/square"
+reframe(){ ffmpeg -y -i "$1" -filter_complex "[0:v]scale=$2:$3:force_original_aspect_ratio=increase,crop=$2:$3,gblur=sigma=24,eq=brightness=-0.10[bg];[0:v]scale=$2:-1[fg];[bg][fg]overlay=(W-w)/2:(H-h)/2,format=yuv420p[v]" -map "[v]" -map 0:a -c:v libx264 -preset medium -crf 21 -c:a aac -b:a 128k "$4" 2>/dev/null; }
+n=0
+for f in "$VD"/*.mp4; do
+ [ -f "$f" ] || continue
+ b=$(basename "$f" .mp4)
+ [ -f "$VD/vertical/$b.mp4" ] || reframe "$f" 1080 1920 "$VD/vertical/$b.mp4"
+ [ -f "$VD/portrait/$b.mp4" ] || { reframe "$f" 1080 1350 "$VD/portrait/$b.mp4"; n=$((n+1)); }
+ [ -f "$VD/square/$b.mp4" ] || reframe "$f" 1080 1080 "$VD/square/$b.mp4"
+done
+echo "portrait/square generated for $n film(s)"
+echo "counts: vertical=$(ls $VD/vertical/*.mp4 2>/dev/null|wc -l|tr -d ' ') portrait=$(ls $VD/portrait/*.mp4 2>/dev/null|wc -l|tr -d ' ') square=$(ls $VD/square/*.mp4 2>/dev/null|wc -l|tr -d ' ')"
diff --git a/social/LAUNCH-PACK.md b/social/LAUNCH-PACK.md
index ddf1ab0..a4defba 100644
--- a/social/LAUNCH-PACK.md
+++ b/social/LAUNCH-PACK.md
@@ -45,10 +45,13 @@ Anchor launch on the strongest films: **Spago · Funke · Mr. Chow · Matsuhisa
---
-## Asset inventory (ready now)
-- `public/video/*.mp4` — landscape 16:9 films (YouTube long-form + site embeds)
-- `public/video/vertical/*.mp4` — 9:16 1080×1920 (IG Reels / TikTok / Shorts) — auto-generated per film by `filmgen/heygen_batch.mjs`
+## Asset inventory (ready now) — every film in 4 formats
+- `public/video/*.mp4` — **16:9** landscape (YouTube long-form + site embeds)
+- `public/video/vertical/*.mp4` — **9:16** 1080×1920 (IG Reels / TikTok / YouTube Shorts)
+- `public/video/portrait/*.mp4` — **4:5** 1080×1350 (Instagram feed — best engagement)
+- `public/video/square/*.mp4` — **1:1** 1080×1080 (Instagram feed)
- `public/video/*.jpg` — poster frames
+All social ratios are blurred-fill (whole frame preserved, no crop) and auto-generated per film by `filmgen/heygen_batch.mjs`. Regenerate for existing films with `filmgen/social_reframe.sh`.
## What needs Steve (gated)
Creating the accounts (identity/phone verification), claiming handles, and any posting are Steve-gated. See `~/.claude/yolo-queue/pending-approval/` go-live memo.
← 28fc238 auto-save: 2026-08-05T16:43:55 (1 files) — filmgen/heygen_ba
·
back to Beverlyhillsvideos
·
films: integrate completed restaurant film(s) [auto] 9bf88f3 →