← back to Dw Slideshow Gen

README.md

87 lines

# dw-slideshow-gen

Designer Wallcoverings marketing **slideshow generator** — turn a marketing topic
or product into a 6-slide, 1080×1920 TikTok/Instagram photo-carousel as PNGs.
**Zero image-gen cost**: slides are drawn headless with `@napi-rs/canvas` (no
browser, no OpenAI), deterministic and on-brand.

## Why this shape
- **6 slides:** slide 1 = hook, 2–5 = substantive body, 6 = CTA — the proven
  TikTok carousel structure (slide 1 is the whole ballgame).
- **Brand from `DESIGN.md`:** fonts (Didot display / Avenir body), warm-charcoal
  ground, brass accent, cream type, hashtags — one source of truth.
- **`renderSpec()` is server-callable:** the same function a CLI uses here can be
  called from a marketing endpoint later to serve PNGs. No rewrite.

## Use
```bash
# straight from the live catalog (queries dw_unified, downloads product photos, renders):
node index.js catalog --limit 6                       # 6 newest -> one carousel each
node index.js catalog --vendor "Anna French" --mode roundup   # "New Anna French arrivals"
node index.js catalog --type Wallcovering --since 2026-06-01

# from a hand-authored queue:
node index.js all samples/queue.json          # plan + render (template copy)
node index.js plan  samples/queue.json         # -> output/plan-<date>.json
node index.js render output/plan-<date>.json   # -> output/renders/<id>/slide_N.png

# assemble the postable draft package (fail-closed — never posts without creds+--confirm):
node index.js post                             # all render dirs -> zip + caption + instructions
```
Flags:
- `--ai` — generate slide copy via headless Claude Code (`claude -p`, reuses the
  Max-plan login, **no metered API key**). Falls back to the template generator
  on any failure.
- `--bg DIR` — use real product photography as slide backgrounds
  (`slide_N.jpg` per slide or one `bg.jpg`) with an automatic legibility scrim.
- `--design FILE` — point at a different `DESIGN.md`.

## Queue shape
```json
{ "items": [ {
  "id": "grasscloth-moody-dining",
  "topic": "Grasscloth wallcoverings",
  "angle": "a moody dining room",
  "style": "Organic Modern", "colorway": "Charcoal & Flax", "room": "dining room",
  "facts": ["Title: one substantive sentence.", "..."],   // one per body slide (≤4)
  "hashtags": ["#grasscloth", "..."]                        // optional, ≤5
} ] }
```
If `facts` is omitted the generator writes on-brand body slides from the topic.

## Posting (Postiz) — verified API, fail-closed
Client (`lib/postiz.js`) is built to the verified Postiz public API
(`https://api.postiz.com/public/v1`, `Authorization: <key>`; shapes lifted from the
official SDK + DTOs). `post` **always** builds the local zip package ($0). With
`--postiz` it stages into Postiz:

```bash
node index.js integrations                 # list connected channels (needs key)
node index.js post --postiz                # DRY-RUN: prints the exact request body, no network
node index.js post --postiz --confirm      # stage as a Postiz DRAFT (reversible)
node index.js post --postiz --channel tiktok --schedule 2026-07-01T15:00:00Z --confirm
```

**Why `draft`, not direct-to-TikTok:** we stage as Postiz `type:'draft'`. It needs no
per-provider settings, lands in the Postiz dashboard for you to review + publish (you
add the trending sound there), and is reversible via `DELETE /posts/{id}`. This
deliberately sidesteps Postiz issue **#1338** (still OPEN) — TikTok `UPLOAD`+`SELF_ONLY`
posts get stuck in QUEUE because `privacy_level` is dropped upstream. `--schedule`
uses the schedule path with full TikTok settings if you'd rather queue directly.

**Gate:** network calls fire only with `POSTIZ_API_KEY` **and** `--confirm`. Without
`--confirm` it's a dry-run. Going live is outward-facing → Steve-gated.

**Setup:** Postiz → connect TikTok → Settings → Developers → Public API → generate key
→ paste it (routed via `/secrets` as `POSTIZ_API_KEY`). No `INTEGRATION_ID` needed —
the engine lists channels and picks TikTok automatically (override with `--channel`).

**Analytics:** Postiz's analytics are **dashboard-only** (platform.postiz.com/analytics) —
there is no public-API analytics endpoint, so per-post stats can't be pulled
programmatically. The engine integrates posting + channels; analytics stays in the UI.

## Cost
Render: **$0 (local)** — no image-gen API. Catalog photo download: **$0**.
Optional `--ai` copy uses the existing Claude Code login (no separate billing).
`output/` is gitignored.