← back to Beverlyhillsvideos
social: 6-hour BHV IG cadence + V6 logo
c6a39a6e1a22d91ef74bf547f02bc7065e7fa309 · 2026-08-06 15:39:25 -0700 · Steve Abrams
- every-6h launchd (com.steve.bhv-ig-6h, 00/06/12/18) alternating restaurant/
store; run.sh picks KIND by hour + falls back to restaurant until stores deploy.
- poster verticalUrl now store-aware (store cards are authored 9:16).
- avatar.png → approved V6 (BHV monogram + gold play-triangle).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A filmgen/logo_variants.pyM public/social/avatar.pngA public/social/avatar.pre-v6.bak.pngA social/poster/com.steve.bhv-ig-6h.plistM social/poster/post-daily.mjsM social/poster/run.sh
Diff
commit c6a39a6e1a22d91ef74bf547f02bc7065e7fa309
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 6 15:39:25 2026 -0700
social: 6-hour BHV IG cadence + V6 logo
- every-6h launchd (com.steve.bhv-ig-6h, 00/06/12/18) alternating restaurant/
store; run.sh picks KIND by hour + falls back to restaurant until stores deploy.
- poster verticalUrl now store-aware (store cards are authored 9:16).
- avatar.png → approved V6 (BHV monogram + gold play-triangle).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
filmgen/logo_variants.py | 95 ++++++++++++++++++++++++++++++++
public/social/avatar.png | Bin 30439 -> 28041 bytes
public/social/avatar.pre-v6.bak.png | Bin 0 -> 30439 bytes
social/poster/com.steve.bhv-ig-6h.plist | 34 ++++++++++++
social/poster/post-daily.mjs | 7 ++-
social/poster/run.sh | 17 ++++--
6 files changed, 147 insertions(+), 6 deletions(-)
diff --git a/filmgen/logo_variants.py b/filmgen/logo_variants.py
new file mode 100644
index 0000000..b5196b2
--- /dev/null
+++ b/filmgen/logo_variants.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+"""Generate BHV logo/avatar options (1080x1080) — $0, local. Writes filmgen/work/logo-*.png."""
+import os, math
+from PIL import Image, ImageDraw, ImageFont, ImageFilter
+
+S = 1080
+GREEN, GREEN2 = (18, 40, 31), (9, 22, 17)
+CREAM, GOLD, MUTED = (246, 243, 236), (184, 147, 90), (176, 170, 158)
+SERIF = "/System/Library/Fonts/Supplemental/Didot.ttc"
+SANS = "filmgen/fonts/Jost.ttf"
+sf = lambda s: ImageFont.truetype(SERIF, s)
+jf = lambda s: ImageFont.truetype(SANS, s)
+OUT = "filmgen/work"; os.makedirs(OUT, exist_ok=True)
+
+def ctr(d, cy, y, txt, fnt, fill):
+ w = d.textlength(txt, font=fnt); d.text((cy - w/2, y), txt, font=fnt, fill=fill)
+
+def trk(d, cy, y, txt, fnt, fill, tr):
+ ws = [d.textlength(c, font=fnt) for c in txt]; tot = sum(ws)+tr*(len(txt)-1); x = cy-tot/2
+ for c, w in zip(txt, ws): d.text((x, y), c, font=fnt, fill=fill); x += w+tr
+
+def ground(green=True):
+ if green:
+ img = Image.new("RGB", (S, S), GREEN)
+ vig = Image.new("L", (S, S), 0)
+ ImageDraw.Draw(vig).ellipse([-S*0.2,-S*0.2,S*1.2,S*1.2], fill=110)
+ vig = vig.filter(ImageFilter.GaussianBlur(200))
+ img = Image.composite(img, Image.new("RGB",(S,S),GREEN2), vig)
+ else:
+ img = Image.new("RGB", (S, S), CREAM)
+ return img
+
+def ring(d, inset, color=GOLD, w=3):
+ d.ellipse([inset, inset, S-inset, S-inset], outline=color, width=w)
+
+# V1 — refined BHV monogram, breathing room, double hairline ring
+def v1():
+ img = ground(); d = ImageDraw.Draw(img)
+ ring(d, 70, GOLD, 3); ring(d, 84, GOLD, 1)
+ ctr(d, S/2, S*0.30, "BHV", sf(300), GOLD)
+ d.line([(S/2-70, S*0.585), (S/2+70, S*0.585)], fill=GOLD, width=2)
+ trk(d, S/2, S*0.66, "BEVERLY HILLS VIDEOS", jf(34), CREAM, 12)
+ img.save(f"{OUT}/logo-v1.png")
+
+# V2 — wordmark stack, no ring
+def v2():
+ img = ground(); d = ImageDraw.Draw(img)
+ trk(d, S/2, S*0.24, "THE INSIDER FILM GUIDE", jf(26), GOLD, 10)
+ ctr(d, S/2, S*0.33, "Beverly", sf(150), CREAM)
+ ctr(d, S/2, S*0.48, "Hills", sf(150), CREAM)
+ ctr(d, S/2, S*0.63, "Videos", sf(150), CREAM)
+ d.line([(S*0.30, S*0.82), (S*0.70, S*0.82)], fill=GOLD, width=2)
+ img.save(f"{OUT}/logo-v2.png")
+
+# V3 — big BH monogram, ring, wordmark
+def v3():
+ img = ground(); d = ImageDraw.Draw(img)
+ ring(d, 74, GOLD, 2)
+ ctr(d, S/2, S*0.26, "BH", sf(420), CREAM)
+ trk(d, S/2, S*0.70, "BEVERLY HILLS VIDEOS", jf(32), GOLD, 12)
+ img.save(f"{OUT}/logo-v3.png")
+
+# V4 — cream inverse, green BHV
+def v4():
+ img = ground(green=False); d = ImageDraw.Draw(img)
+ ring(d, 74, GREEN, 3)
+ ctr(d, S/2, S*0.30, "BHV", sf(300), GREEN)
+ d.line([(S/2-70, S*0.585), (S/2+70, S*0.585)], fill=GOLD, width=3)
+ trk(d, S/2, S*0.66, "BEVERLY HILLS VIDEOS", jf(34), (120,110,95), 12)
+ img.save(f"{OUT}/logo-v4.png")
+
+# V5 — badge: BEVERLY HILLS top, BHV center, VIDEOS · CALIFORNIA bottom
+def v5():
+ img = ground(); d = ImageDraw.Draw(img)
+ ring(d, 66, GOLD, 3)
+ trk(d, S/2, S*0.20, "BEVERLY HILLS", jf(40), GOLD, 10)
+ ctr(d, S/2, S*0.34, "BHV", sf(280), CREAM)
+ d.line([(S/2-90, S*0.66), (S/2+90, S*0.66)], fill=GOLD, width=2)
+ trk(d, S/2, S*0.72, "VIDEOS · CALIFORNIA", jf(32), MUTED, 10)
+ img.save(f"{OUT}/logo-v5.png")
+
+# V6 — film motif: small play triangle over BHV
+def v6():
+ img = ground(); d = ImageDraw.Draw(img)
+ ring(d, 74, GOLD, 2)
+ cx, ty = S/2, S*0.235
+ r = 26
+ d.polygon([(cx-r*0.7, ty-r), (cx-r*0.7, ty+r), (cx+r, ty)], fill=GOLD)
+ ctr(d, S/2, S*0.34, "BHV", sf(290), CREAM)
+ d.line([(S/2-70, S*0.63), (S/2+70, S*0.63)], fill=GOLD, width=2)
+ trk(d, S/2, S*0.70, "BEVERLY HILLS VIDEOS", jf(32), GOLD, 12)
+ img.save(f"{OUT}/logo-v6.png")
+
+for f in (v1, v2, v3, v4, v5, v6): f()
+print("wrote logo-v1..v6")
diff --git a/public/social/avatar.png b/public/social/avatar.png
index d38d55e..3daf319 100644
Binary files a/public/social/avatar.png and b/public/social/avatar.png differ
diff --git a/public/social/avatar.pre-v6.bak.png b/public/social/avatar.pre-v6.bak.png
new file mode 100644
index 0000000..d38d55e
Binary files /dev/null and b/public/social/avatar.pre-v6.bak.png differ
diff --git a/social/poster/com.steve.bhv-ig-6h.plist b/social/poster/com.steve.bhv-ig-6h.plist
new file mode 100644
index 0000000..981988c
--- /dev/null
+++ b/social/poster/com.steve.bhv-ig-6h.plist
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- BHV Instagram — one post every 6 hours (00/06/12/18), alternating
+ restaurant/store. Install:
+ cp social/poster/com.steve.bhv-ig-6h.plist ~/Library/LaunchAgents/
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.steve.bhv-ig-6h.plist
+ Uninstall: launchctl bootout gui/$(id -u)/com.steve.bhv-ig-6h -->
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.steve.bhv-ig-6h</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/bin/zsh</string>
+ <string>/Users/macstudio3/Projects/beverlyhillsvideos/social/poster/run.sh</string>
+ </array>
+ <key>EnvironmentVariables</key>
+ <dict>
+ <key>BHV_IG_LIVE</key>
+ <string>1</string>
+ </dict>
+ <key>StartCalendarInterval</key>
+ <array>
+ <dict><key>Hour</key><integer>0</integer><key>Minute</key><integer>0</integer></dict>
+ <dict><key>Hour</key><integer>6</integer><key>Minute</key><integer>0</integer></dict>
+ <dict><key>Hour</key><integer>12</integer><key>Minute</key><integer>0</integer></dict>
+ <dict><key>Hour</key><integer>18</integer><key>Minute</key><integer>0</integer></dict>
+ </array>
+ <key>StandardOutPath</key>
+ <string>/Users/macstudio3/Projects/beverlyhillsvideos/social/poster/launchd.out</string>
+ <key>StandardErrorPath</key>
+ <string>/Users/macstudio3/Projects/beverlyhillsvideos/social/poster/launchd.err</string>
+</dict>
+</plist>
diff --git a/social/poster/post-daily.mjs b/social/poster/post-daily.mjs
index 73bbdb7..821eab5 100644
--- a/social/poster/post-daily.mjs
+++ b/social/poster/post-daily.mjs
@@ -44,7 +44,12 @@ function rankByLRU(films, ledger) {
.map((x) => x.film);
}
-const verticalUrl = (film) => `${cfg.siteBase}${film.src.replace('/video/', '/video/vertical/')}`;
+// Restaurant films are landscape with a vertical counterpart under /video/vertical/;
+// store cards are authored vertical already (src points straight at the 9:16 file).
+const verticalUrl = (film) =>
+ KIND === 'store' || film.src.includes('/vertical/') || film.src.includes('/store/')
+ ? `${cfg.siteBase}${film.src}`
+ : `${cfg.siteBase}${film.src.replace('/video/', '/video/vertical/')}`;
async function reachable(url) {
try {
diff --git a/social/poster/run.sh b/social/poster/run.sh
index ab40c3d..b2ae037 100755
--- a/social/poster/run.sh
+++ b/social/poster/run.sh
@@ -1,9 +1,16 @@
#!/bin/zsh
-# run.sh — launchd/cron wrapper for the daily BHV Instagram post.
-# Token + user-id are read from the secrets master .env by env.mjs.
-# LIVE posting requires BHV_IG_LIVE=1 (set by the plist once Steve approves go-live).
+# run.sh — 6-hourly BHV Instagram post. Alternates restaurant/store by the hour
+# (slots 00/06/12/18 → restaurant, store, restaurant, store). Falls back to
+# restaurant while the store library isn't deployed yet. Token + user-id come from
+# the secrets master .env; LIVE posting requires BHV_IG_LIVE=1 (set by the plist).
cd "$(dirname "$0")/../.." || exit 1
+HOUR=$(( 10#$(date +%H) ))
+SLOT=$(( HOUR / 6 )) # 0..3
+if (( SLOT % 2 == 1 )); then KIND=store; else KIND=restaurant; fi
+# store library not deployed/public yet → fall back so a slot never posts nothing
+[[ "$KIND" == "store" && ! -f data/store-films.json ]] && KIND=restaurant
+
LOG="social/poster/post.log"
-echo "----- $(date '+%Y-%m-%d %H:%M:%S %Z') -----" >> "$LOG"
-/opt/homebrew/bin/node social/poster/post-daily.mjs >> "$LOG" 2>&1
+echo "----- $(date '+%Y-%m-%d %H:%M:%S %Z') · slot=$SLOT kind=$KIND -----" >> "$LOG"
+BHV_IG_KIND="$KIND" /opt/homebrew/bin/node social/poster/post-daily.mjs >> "$LOG" 2>&1
echo "exit: $?" >> "$LOG"
← 569b718 auto-data-snapshot: 2026-08-06T15:32:11 (1 data files) — soc
·
back to Beverlyhillsvideos
·
stores: deploy 8 animated Rodeo Drive cards + store-films.js facb575 →