shots capture / render: --concurrency, per-frame timing, per-language page reuse, input-hash cache

Web capture runs frames in parallel browser contexts (default from the CPU count, lower for GPU-heavy capture),
logs n/total + ETA and goto / waitFor / eval / delay / screenshot per frame, writes .storebound/capture-report.json
with the slowest frames and steps. shots.capture.session 'per-language' boots the app once per language and device
and switches shots via pushState / popstate (navigate 'spa') or goto (navigate 'reload'), falling back to a fresh
page when a precondition fails. .storebound/capture-cache.json skips frames with unchanged inputs; --only-missing,
--force. shots render renders with --concurrency pages and async ffmpeg.
This commit is contained in:
CRTkafa 2026-10-12 03:49:53 +08:00
parent 296aa6a6ae
commit 5cb5dc2c32
10 changed files with 561 additions and 74 deletions

View file

@ -52,6 +52,16 @@ in a plain Android tablet body instead of the iPad one.
`<shots.frames>/<lang>/<phone|tablet>/<shot>.png`, local, not committed.
- `web` (default): `baseUrl`, `query` with `{lang}`, shots: `path` / `waitFor` / `eval` / `delayMs` / `device`.
Phone 440 x 902 @3x and tablet 800 x 1240 @2x by default; the template draws the status bar above.
Speed (many languages): frames run in parallel browser contexts (`--concurrency N`, `capture.concurrency`;
default min(3, cores / 4) since game boots are GPU-heavy). `capture.session: 'per-language'` boots the app once
per language and device and switches shots in that page (per shot `navigate: 'spa'` = pushState + popstate to its
path, then `waitFor` / `eval`; `'reload'` = goto in the same page); a shot whose `waitFor` fails there within
`reuseTimeoutMs` (15000) gets a fresh page. Re-runs skip frames whose inputs did not change (shot entry, URL,
frame size, the language's copy, the build: `capture.build` files / folders, else the HTML and scripts at
`baseUrl`, fonts) via `.storebound/capture-cache.json`; `--only-missing` captures only missing files, `--force`
everything. Each progress line shows n/total, ETA and the time per step (goto, wait, eval, delay, shot); the end
lists the slowest frames and steps, and `.storebound/capture-report.json` keeps them. Put the slow step right
(a long `waitFor` = the game reaching the scene: add a URL switch that jumps there).
- `ios-sim` (Unity, Godot, Flutter, native, Capacitor: any Simulator build): `ios.device` (simulator name or UDID,
e.g. a 6.9" iPhone), `ios.bundleId`, optional `ios.app` (.app to install), `ios.args`. Per shot: `args` (launch
arguments, read them with UserDefaults / your engine) and / or `url` (deep link). Language goes in as
@ -89,7 +99,7 @@ in a plain Android tablet body instead of the iPad one.
`{ bg, glow, ink, shadow }`), frames (one front phone + optional back phones with x / dy / scale / rotate), badge
slots (y, side, icon, hot, stars). Fonts default to Paytone One + Montserrat (game) or Inter (app): all cover
Latin Extended and Vietnamese.
6. **Render.** `storebound shots render --lang en` first, review `store/renders/en/sheet.png` (contact sheet, sizes
6. **Render.** (`--concurrency N`, default min(4, cores / 2) pages) `storebound shots render --lang en` first, review `store/renders/en/sheet.png` (contact sheet, sizes
packed into a grid), then
all languages in the background: `storebound shots render > .storebound/render.log 2>&1 &`.
7. **JPEG for Play.** `storebound shots jpeg` (q92, about 4x smaller than PNG; App Store stays PNG).