Add multi client video automation example - #15
Conversation
Companion code for the "A guide to automating video content production for multiple clients" guide: one master template renders branded promos for three clients in three aspect ratios, with a JSON-lines file recording which render belongs to which client. The live article already links this directory.
Derive the API base from SHOTSTACK_ENV (stage default, v1 for
production) instead of hand-editing two files, catch network failures
in status.mjs so they print one line instead of a stack trace, and
widen apiError to the {success, message, response} error shape.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
dazzatronus
left a comment
There was a problem hiding this comment.
Review — STANDARDS.md must be followed
Public cookbook code must match STANDARDS.md and examples/_template/. Product teaching is solid; blocking on standards/correctness gaps.
Blockers (STANDARDS)
.env.exampleincomplete — example requiresSHOTSTACK_TEMPLATE_IDand optionallySHOTSTACK_ENV. Document each with empty value + comment/URL above the line (same pattern asSHOTSTACK_API_KEY).- README template curl hardcodes
edit/stagewhile the scripts honorSHOTSTACK_ENV. STANDARDS: every command in the README must stay correct. Use the same env in the curl URL (or say explicitly: create the template in the same environment asSHOTSTACK_ENV). - README shape / STE — prefer
_template’sRelated guide:line; keep instructions ≤20 words and descriptions ≤25.
Already good
- Lean core (clients → merge → JSONL → status)
- Concurrent append rationale in
db.mjs - Second commit:
SHOTSTACK_ENV, network errors, clearer API errors - No stack traces on the happy failure paths
Re-run the STANDARDS pre-PR checklist after fixes.
| @@ -0,0 +1,2 @@ | |||
| # https://dashboard.shotstack.io/register | |||
| SHOTSTACK_API_KEY= | |||
There was a problem hiding this comment.
STANDARDS — API keys: also document SHOTSTACK_TEMPLATE_ID (required) and SHOTSTACK_ENV (optional, stage|v1) with empty values and a comment above each. Empty placeholders so a missing var fails with your message, not a vague API error.
| ```bash | ||
| curl --fail-with-body \ | ||
| --request POST \ | ||
| "https://api.shotstack.io/edit/stage/templates" \ |
There was a problem hiding this comment.
STANDARDS — correct commands: this curl always hits edit/stage, but render.mjs / status.mjs use SHOTSTACK_ENV. Point the curl at the same environment (or state that the template must be created in that env). Wrong-env template ids are a common footgun.
| footage, music and brand mark. A JSON-lines file records which render belongs to which client, | ||
| because the API has no endpoint that lists renders. | ||
|
|
||
| Companion code for [A guide to automating video content production for multiple clients](https://shotstack.io/learn/automating-video-production-multiple-clients/). |
There was a problem hiding this comment.
STANDARDS — README: match examples/_template (Related guide: …) and Simplified Technical English sentence limits.
dazzatronus
left a comment
There was a problem hiding this comment.
Follow-up from a STANDARDS / correctness pass:
- Footage URLs —
clients.mjsusesshotstack-ingest-api-v1-sources/.../35tqpmb0ya/...paths. They respond 200 today, but ingest-owner prefixes are fragile for a public example. Prefer durableshotstack-assets(or other documented public) URLs so a fresh clone still renders months later. - WIDTH / HEIGHT merge types — template uses
"{{ WIDTH }}"/"{{ HEIGHT }}"strings;mergeFieldsForcurrently passes numbers. Stringify the replaces so merge stays string-in/string-out. - render.mjs network errors — align with
status.mjs: one actionable line + non-zero exit (STANDARDS failures section), not a rawfetchrejection message viaallSettled. - db.mjs
all()— don’t swallow corrupt JSONL as[]; fail with one line so a bad notebook isn’t silently empty.
Still blocked on the earlier STANDARDS items (.env.example, stage-hardcoded template curl, README/STE).
| headline: 'Twelve new listings this week.', | ||
| font: 'Montserrat', | ||
| footage: | ||
| 'https://shotstack-ingest-api-v1-sources.s3.ap-southeast-2.amazonaws.com/35tqpmb0ya/zzz01m08-qxa25-864e6-zty3t-3sttne/source.mp4', |
There was a problem hiding this comment.
Prefer public durable assets (e.g. shotstack-assets) over ingest-bucket owner-prefixed URLs. Fresh clones must keep working after the source account rotates media.
| { find: 'BRAND_MARK', replace: client.brandMark }, | ||
| { find: 'FOOTAGE', replace: client.footage }, | ||
| { find: 'MUSIC', replace: client.music }, | ||
| { find: 'WIDTH', replace: variant.width }, |
There was a problem hiding this comment.
Template placeholders are strings ("{{ WIDTH }}"). Pass String(variant.width) / String(variant.height) (or template literals) so merge replace values stay strings.
.env.example documents SHOTSTACK_TEMPLATE_ID and SHOTSTACK_ENV with empty
values and a comment above each. Scripts use || so an empty SHOTSTACK_ENV in
.env falls back to stage.
README follows the _template shape (Related guide line, five sections) and
STE sentence limits. The template curl targets ${SHOTSTACK_ENV:-stage} so it
creates the template in the same environment the scripts use, and the setup
adds the copy-id-then-reload step.
clients.mjs points footage at durable shotstack-assets URLs instead of
ingest-bucket owner-prefixed paths, and passes WIDTH/HEIGHT as strings to
match the string placeholders in the template.
render.mjs turns network failures and 401/403 into one actionable line per
variant with a 30 s timeout and exits non-zero when the notebook cannot be
written. db.mjs returns [] only when renders.jsonl does not exist and fails
with one line on a corrupt line; status.mjs reports that and exits 1, and
says what to do when no renders are recorded yet.
Companion code for A guide to automating video content production for multiple clients (https://shotstack.io/learn/automating-video-production-multiple-clients/) — the live article already links this directory, so its GitHub link 404s until this merges. One master template renders branded promos for three clients in three aspect ratios; renders.jsonl records which render belongs to which client, since the API has no endpoint that lists renders.
Gated live twice (including from a fresh clone following the README verbatim): template created, 9/9 submitted, all nine done with URLs. A wrong key prints one-line errors and exits non-zero. Second commit addresses code review: the API base derives from SHOTSTACK_ENV (stage default, v1 for production) instead of hand-editing two files, status.mjs catches network failures with one line instead of a stack trace, and API errors reduce to their message across both response shapes. Prettier passes.