Stills and tutorial videos of real product flows — one name grammar from ~/media to a CDN grouped by block

Shoot

shoot takes stills of real product pages and scripted flows, framed like Abdout's 14" MacBook at Chrome 125%, and its sim films the same flow into a branded tutorial. Say it in prose: "shoot add-student in both languages", "publish the bulk flows". Raw screen takes are record; cuts and reels are edit.

One name, everywhere

A flow is <verb>-<entity>, singular kebab-case (add-student, bulk-teacher). That one word names the flow file, its local folder, its files on the CDN and its manifest ids. hogwarts is the internal name in every path; balqalam is only the public brand drawn on screen.

AssetLocal (~/media/<repo>/)Manifest idCDN (cdn.databayt.org/hogwarts/)
page stillpage/<slug>-<locale>.pngpage/<slug>-<locale><slug>/page.avif (home → page.avif)
flow still<flow>/NN-<step>-<locale>.png<flow>/<step>-<locale><block>/<name>/<step>.avif (+ @2x, .webp)
phone still<flow>/iphone-16/NN-<step>-<locale>.png<flow>/iphone-16/<step>-<locale><block>/<name>/iphone-16/<step>.avif
tutorial<flow>/<flow>-<locale>.mp4 (+ .av1.mp4, .poster.webp, .vtt)<flow>/video-<locale><block>/<name>.mp4 (+ same sidecars)
reel<flow>/<flow>-<locale>.reel.mp4<flow>/reel-<locale><block>/<name>/reel.mp4
support clip / story cut<flow>/<flow>-<clip>-<locale>.*<flow>/<clip>-<locale><block>/<name>/<clip>.mp4
block media (ad, block reel)—your id<block>/<file>.mp4
raw take<flow>/raw/<flow>[-<part>]-<locale>.mov——
  • Grouped by block, short names. A flow's export const cdn = "<block>/<name>" is its home: add-student → students/add gives hogwarts/students/add.mp4 and hogwarts/students/add/list.avif; bulk-student → students/bulk; add-teacher → teachers/add. Block-wide media such as hogwarts/students/marketing.mp4 or hogwarts/students/reel.mp4 sits beside the flows.
  • Arabic is bare, English adds -en (list.avif, list-en.avif). The narrowest width is bare and wider ones add @2x (list.avif 1600 px, list@2x.avif 2400 px).
  • Same name = overwrite. No -v2 / -final. The NN- order prefix stays in the local file and becomes the manifest entry's order, so the docs list stills in shot order.
  • Keys are stable: a re-render overwrites the object and invalidates CloudFront for that key; browsers cache an hour, the edge a year, and manifest URLs carry ?v=<sha8> so pages pick up new bytes at once.
  • ~/media sits outside every repo and mirrors to Drive (record.sh sync). Nothing is captured into a repo's public/.

Publish

node ~/.claude/skills/shoot/scripts/shoot.mjs --flow add-student --lang ar,en --publish
node ~/.claude/skills/shoot/scripts/publish.mjs --flow add-student [--only stills|video] [--dry-run]
node ~/.claude/skills/shoot/scripts/publish.mjs --flow page

Each asset is uploaded, HEAD-verified and upserted into the product's media-manifest.json; pages render it by id — <FlowMedia flow="add-student" />, <Shot id="add-student/created-ar" />, <TutorialVideo id="add-student/video-ar" /> — never by URL. Publishing also stamps the repo sha into ~/media/manifest.json, so record.sh stale flags a still whose source (export const block / paths) has changed.