@officexapp/vidfarm-devcli 0.21.29 → 0.21.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -210,6 +210,7 @@ Present both harnesses to the director, recommend (A) unless they've asked for p
210
210
  - **Stickers are usually animated, not pasted.** Once placed, animate each one with `vidfarm keyframes` presets (`pop-in`, `float`, `shake`, `grow`, `slide-in-left`, `drift`) — that's HTML/CSS canvas motion, deterministic, free, and identical in preview and render. Layer moves up (pop-in, then idle float) for real life. See `references/editor-workflows.md` → "Cutout graphics for explainers".
211
211
  - **A sticker can carry its OWN motion too.** A *moving* subject has no single bounding box, so it isn't a PNG: key the clip with `vidfarm remove-greenscreen <video>` → transparent WebM (browser/editor-playable, the right choice on a composition).
212
212
  - **The plate must be a color the ART DOESN'T USE — green is a default, not a law.** A chroma key removes exactly ONE color, so a green plate under green artwork (leaves, frogs, plants, money, an "eco" icon set) keys holes straight through the stickers. `sticker-pack`/`cutout` handle this for you in two ways, and you should still say it out loud when it matters: **(a)** when generating, they read the subject and move the plate off any hue it mentions — green → magenta (`#FF00FF`) → blue (`#0047BB`) → black → white — and print which plate they picked and why; **(b)** when splitting a sheet you already have, they **detect the plate from the sheet's four corners**, so a red/purple/blue sheet handed back from a web generator just works. Pin it yourself with `--key-color "#FF00FF"` / `--preset magenta` (that disables the auto-pick), or `--no-auto-key` to force plain green. If a pack's palette collides with *every* standard plate, split it into two sheets on two different plates rather than fighting the key.
213
+ - **The ART must be CLOSED and SOLIDLY FILLED — this is the other half of surviving the key, and the #1 way stickers come back broken.** Ask an image model for "icons on a green plate" and it will happily draw **outline art**: a colored stroke with the shape's interior left as bare plate. It looks perfect on the sheet, and after the key each sticker is a **rim floating around a see-through hole** (an apple-shaped outline with nothing inside it). Same outcome from a *near-plate* fill (the keyer works on tolerance, not exact match), a translucent/glassy/glowing material, or a soft glow fading into the plate. **You cannot key those pixels back — it has to be in the prompt:** *"every object is a closed, solidly filled shape; outlines must enclose an opaque fill of a different color; no outline-only or hollow art; nothing on the art in the plate color or any near-shade of it; fully opaque, no translucency, glow or drop shadow."* `cutout --generate`, `sticker-pack --generate`, `handoff image` and the `create-overlay` primitive **append that clause for you** with the chosen plate hex — write it yourself only when you prompt a generator directly. After the key, both commands report per-item `hole_pct`/`hollow` (console `⚠ N% hollow`, `--json`, `stickers.json`) — a ring or picture frame reads the same way, so it **warns, never blocks**. Flagged and it shouldn't be? Re-generate with the fill clause; a *near*-plate fill can sometimes be rescued with a lower `--tolerance`; one stubborn item can be lifted with `vidfarm mask --crop …` (ONNX matting ignores fill color).
213
214
  - **Transparent GIF is supported, for GIF-only surfaces.** `vidfarm sticker-pack … --output-format gif` (stills) and `vidfarm remove-greenscreen <video> --gif` (animated) emit transparent GIFs. GIF alpha is **1-bit**, so edges go hard — fine for chat/forum/Notion sticker surfaces, worse than PNG/WebP/WebM for compositing on a timeline. Prefer PNG/WebP/WebM unless the destination only eats GIF.
214
215
 
215
216
  **Explainer house style — the defaults to build with unless told otherwise.** **White background / light mode** (plain white stage, no gradients, no dark mode, no photo backdrop), **kinetic word-by-word captions** in dark ink on the light stage (`vidfarm captions generate --style word-pop --color "#111111" --active-color "#7C3AED" --background-style plain` — skip outlines/shadows, they're only needed over busy footage), and **female TTS narration** (`vidfarm tts --voice coral` on OpenAI — `nova` for energy, `sage` for calm; `Kore`/`Leda` on Gemini; any ElevenLabs voice via `vidfarm voices`). **Keep it clean and simple** — one idea on screen at a time, two or three cutouts per beat, one accent color, one font, lots of white space; remove before you add. **Illustrations default to simplicity**: flat vector, simple shapes, minimal detail, 2–3 flat colors, no baked-in text — simple art keys cleanly, trims tight, and stays on-style across the whole cast. State the defaults once so the director can override any of them. Full detail: recipe `recipes/cutout-graphics-for-explainers.md` (“House style — the explainer defaults”).
@@ -317,6 +318,7 @@ Choose the narrowest path that satisfies the request.
317
318
  3. If the task is “change this video,” read `references/editor-workflows.md`.
318
319
  4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
319
320
  4b. If the task is **“download this video/audio off a website”** (a pasted YouTube / TikTok / Instagram / X post URL the user wants the actual file from), Vidfarm does that for you on a **paid plan** — `POST /api/v1/primitives/videos/download` (or `/audio/download`), devcli `vidfarm download-video <url>` / `vidfarm download-audio <url>`. **Free plan → do not call it; walk the user through opening the URL in Chrome and downloading it from the page, then `vidfarm put-file` the local file in for $0.** Details in `references/assets-and-sourcing.md` → `references/primitives.md`.
321
+ 4c. If the task is **“turn this Reddit/X thread, subreddit, or account into a video”** — “tweet to TikTok”, “Reddit to TikTok”, “make a video from this thread”, “what are the top comments saying” — run `vidfarm recycle <source>` (or `POST /api/v1/primitives/social/recycle`) with the URL. It **decomposes** the source into raw JSON (text, comment tree, media URLs, author pics, stats) and hands it back unranked so YOU pick what to remix. **Paid plan; `max_records` is the spend ceiling.** Brokers the reddit-lead-gen / x-lead-gen OfficeX apps, so it waits out their async job for you. Details in `references/assets-and-sourcing.md` → `references/primitives.md`.
320
322
  5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
321
323
  6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
322
324
  7. If the task is the MARKETPLACE (ordering videos from specialist agents): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally. Placing/listing orders is the thin REST wrapper in `references/core-workflows.md` (§ Marketplace); anything deeper on a gig (inbox, proofs, payouts) needs the external Dollar Platoon skill — `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` — the same way FlockPoster work beyond scheduling needs `npx skills add https://github.com/OfficeXApp/flockposter-skill`.
@@ -18,7 +18,7 @@ The mechanical trio — **generate on a chroma plate → key it out → trim to
18
18
  - **Female TTS narration.** Default to a warm, friendly **female** voice and say which one you picked: local-first `vidfarm tts "<script>" --voice coral` (OpenAI — `nova` if the script wants more energy, `sage` for calmer), `--voice Kore` or `Leda` on Gemini, or `vidfarm voices` → `vidfarm tts --cloud --voice <voice_id>` on ElevenLabs. Tell the director they can swap it in one flag.
19
19
  - **Clean and simple wins.** One idea on screen at a time. Two or three cutouts per beat, not eight. Generous white space, one accent color, one font. When in doubt, remove an element rather than add one.
20
20
 
21
- **Illustrations default to simplicity.** Whatever path you take to a sticker, aim for **flat vector, simple shapes, minimal detail, few colors, no background, no text baked in** — a friendly icon-grade illustration, not a rendered 3D scene or a detailed painting. Simple art keys cleanly, trims tight, scales without mush, animates readably at 9:16, and stays on-style across a whole cast. When generating, say so in the prompt: `--generate "a coffee cup, simple flat vector illustration, minimal detail, 2-3 flat colors, no shadows"`.
21
+ **Illustrations default to simplicity — and to SOLID FILLS.** Whatever path you take to a sticker, aim for **flat vector, simple shapes, minimal detail, few colors, solid opaque fills, no background, no text baked in** — a friendly icon-grade illustration, not a rendered 3D scene or a detailed painting. Simple art keys cleanly, trims tight, scales without mush, animates readably at 9:16, and stays on-style across a whole cast. "Solid fills" is the load-bearing word: **outline-only art has its interior keyed away and comes back as a rim around a transparent hole** (see "Then make the ART key-safe too" below). When generating, say so in the prompt: `--generate "a coffee cup, simple flat vector illustration, minimal detail, 2-3 flat colors, solid filled shapes (not outline-only), no shadows"`.
22
22
 
23
23
  **In cost-saving mode, don't generate illustrations at all — mask them out of images the director already has.** If `vidfarm cost-mode` is `minimize` (or the director says "without burning credits"), the default for adding an illustration is `vidfarm mask <their-image> --crop …` — lifting art out of an infographic, poster, deck slide, brand sheet, or screenshot for **$0 and zero AI calls**. Ask for source art before you ask for a generation budget; the guided loop is **"Mask from an image you already have"** below. **If no source art exists and the graphic must be custom, you still don't have to spend** — hand the director a prompt for a **free** image generator (meta.ai / free ChatGPT / a Hugging Face Space) and cut the returned sheet into stickers locally: **"Free manual image-gen"** below.
24
24
 
@@ -63,6 +63,20 @@ Out comes `./stickers/sticker-01-red-barn.png`, `sticker-02-tractor.png`, … ea
63
63
 
64
64
  Override whenever you know better: `--key-color "#FF00FF"` or `--preset magenta` pins the plate (and turns the auto-pick off); `--no-auto-key` forces plain green. If a pack's palette collides with *every* standard plate — a rainbow icon set — split it across two sheets on two different plates rather than fighting `--tolerance`. And when a sticker is a *green* subject, never let the user generate it on green: the hand-off brief (`vidfarm handoff image`) already picks the plate for them and carries the matching `--key-color` into the follow-up command.
65
65
 
66
+ **Then make the ART key-safe too — CLOSED, SOLIDLY FILLED shapes.** Picking the right plate is only half of surviving the key. The other half is the failure you actually see in the wild: you ask for "sticker icons on a green plate" and the model draws **outline art** — a colored stroke with the shape's *interior left as bare plate*. On the sheet it looks perfect. After the key, the interior is gone with the background, and every sticker composites as a **rim floating around a see-through hole** (an apple that's just an apple-shaped outline, a clock with no face). Same result from three cousins: a fill that's a *near-shade* of the plate (the keyer works on tolerance, not exact match), a **translucent/glassy/glowing** material that lets the plate through, and a **soft glow or drop shadow** that fades into the plate at the edges.
67
+
68
+ None of that is fixable after generation — you cannot key back pixels that were never drawn. So it belongs in the **prompt**, every time:
69
+
70
+ > Every object must be a CLOSED, SOLIDLY FILLED shape — outlines and strokes must enclose an opaque fill of a different color. No outline-only, hollow, or line-art objects; never leave a shape's interior as bare background. No part of any object — fill, outline, highlight, gradient, shading or detail — may be `<PLATE HEX>` or any near-shade, tint or tone of it. No transparent, translucent, glassy, glowing or ghosted materials; every pixel of every object is fully opaque. No soft glows, blurs or drop shadows fading into the background. Keep the whole palette in strong contrast to `<PLATE HEX>`. The background must be visible ONLY around the outside of the objects, never showing through inside them.
71
+
72
+ `cutout --generate`, `sticker-pack --generate`, `vidfarm handoff image`, and the `create-overlay` REST primitive all append that clause automatically with the chosen plate hex substituted in — **you only have to write it yourself when you're prompting a generator directly** (a free web tool by hand, or your own `POST /api/v1/primitives/images/generate` call). Do write it. It costs one paragraph and it is the difference between a usable pack and a re-generation.
73
+
74
+ **And it's checked after the key.** `sticker-pack` and `cutout` measure, per item, the transparent area **fully enclosed by that item's own art** and report it as `hole_pct` (plus `holes`, and `hollow: true` at ≥20%) in `--json`, in `stickers.json`, and as a `⚠ N% hollow` flag on the console line. A ring, donut, picture frame or letter "O" reads the same way, so it **warns, never blocks** — the judgement is yours. When items you expected to be solid come back flagged:
75
+
76
+ - **Re-generate the sheet** with the fill clause above spelled out. This is the fix ~90% of the time.
77
+ - If the fill was merely *close* to the plate rather than absent, a lower `--tolerance` can rescue the sheet you already have. Nothing rescues a genuinely empty interior.
78
+ - Last resort for one stubborn item: `vidfarm mask ./sheet.png --crop x,y,w,h` — ONNX matting doesn't care what color the fill is, so it lifts art the chroma key can't.
79
+
66
80
  **Generation is the failure point, not the cutting.** The sheet prompt is auto-appended with the important instruction — *every item fully separated by clear plate-colored background, nothing touching or overlapping, wide margins, no text, no shadows, one consistent style* — because **touching items segment as one sticker**. If a pack comes back merged, re-run the generation asking for more spacing before you fight the `--gap` knob.
67
81
 
68
82
  ### Stickers are not necessarily small — and they usually move
@@ -179,15 +193,15 @@ Everything here is **local and free** — crop, matte/key, and trim are all `ffm
179
193
 
180
194
  1. **List the cast.** Name every illustration the explainer needs, in the house style (flat vector, 2–3 flat colors, no baked-in text).
181
195
  2. **Hand the director this prompt**, filled in:
182
- > A flat vector illustration sheet on a solid pure green background (#00FF00), no gradient, no shadow, no text, no labels. Arranged in a 3×2 grid with generous even spacing, each object fully separated and not touching any other object or the image edge: (1) `<object one>`, (2) `<object two>`, (3) `<object three>`, … Simple flat shapes, 2–3 flat colors, minimal detail, consistent style and line weight across all objects, front-facing, centered in its own cell. Square image, high resolution.
196
+ > A flat vector illustration sheet on a solid pure green background (#00FF00), no gradient, no shadow, no text, no labels. Arranged in a 3×2 grid with generous even spacing, each object fully separated and not touching any other object or the image edge: (1) `<object one>`, (2) `<object two>`, (3) `<object three>`, … Simple flat shapes, 2–3 flat colors, minimal detail, consistent style and line weight across all objects, front-facing, centered in its own cell. **Every object is a closed, solidly filled shape — outlines must enclose an opaque fill of a different color; no outline-only or hollow objects, and no object's interior left as bare green. Nothing on any object may be green or any near-shade of green; no transparent, translucent, glassy or glowing materials; no soft glows or drop shadows fading into the background.** Square image, high resolution.
183
197
 
184
- **No text in the image, ever** — rendered text never keys or scales cleanly; add copy as a caption or HTML layer instead.
198
+ **No text in the image, ever** — rendered text never keys or scales cleanly; add copy as a caption or HTML layer instead. **And don't drop the bolded fill sentence** — the green gets deleted, so anything green *on* the art gets deleted with it; hollow outline art is the single most common way a hand-generated sheet comes back broken. (`vidfarm handoff image --theme … --items …` mints this whole brief with the plate and the fill clause already filled in — prefer it over typing the prompt yourself.)
185
199
  3. **Bring the sheet in:** `vidfarm put-file ./sheet.png` (or drag into **My Files** in the web app).
186
200
  4. **Split it into stickers — one command, all free:**
187
201
  ```
188
202
  vidfarm sticker-pack ./sheet.png --items "<object one>,<object two>,…" --out-dir ./stickers
189
203
  ```
190
- It keys the flat green, finds each object in the alpha channel, and writes one trimmed transparent sticker per object (named from `--items`) plus `stickers.json`. Run it with `--dry-run` first to check the boxes. Two objects merged into one? The generator drew them too close — lower `--gap`, or grab that pair by hand:
204
+ It keys the flat green, finds each object in the alpha channel, and writes one trimmed transparent sticker per object (named from `--items`) plus `stickers.json`. Run it with `--dry-run` first to check the boxes. Any item flagged **`⚠ N% hollow`** came back as an outline around a transparent hole — unless it's genuinely a ring/frame, re-ask the director for that sheet with the fill sentence, or lift that one item with `vidfarm mask ./sheet.png --crop x,y,w,h` (ONNX matting doesn't care what the fill color is). Two objects merged into one? The generator drew them too close — lower `--gap`, or grab that pair by hand:
191
205
  ```
192
206
  vidfarm mask ./sheet.png --crop 4%,4%,29%,45% --flat "#00FF00" --out prop-a.png
193
207
  ```
@@ -15,6 +15,23 @@ Both are async primitive jobs — poll `GET /api/v1/primitives/jobs/:jobId` unti
15
15
 
16
16
  **Don't reach for this when the real goal is clips.** If the user wants short clips mined out of a long video, `POST /raws/scan` with `source_url` already downloads the source for you as part of the hunt (below) — a separate download call is wasted spend.
17
17
 
18
+ ## Recycle a Reddit or X source into content ("tweet to TikTok", "Reddit to TikTok")
19
+
20
+ When the source isn't a video file but a **conversation** — a Reddit thread and its comments, a subreddit's front page, an X thread and its replies, an account's timeline — the download routes are the wrong tool. Use `vidfarm recycle <source>` (devcli) or `POST /api/v1/primitives/social/recycle` (alias `/recycle-social`), which **decomposes** the source into JSON you can remix:
21
+
22
+ | Paste this | You get back |
23
+ | --- | --- |
24
+ | `reddit.com/r/<sub>/comments/<id>/...` | the post + its whole comment tree |
25
+ | `reddit.com/r/<sub>` or `r/<sub>` | that subreddit's threads |
26
+ | `x.com/<user>/status/<id>` | the tweet + self-thread + replies |
27
+ | `x.com/<user>` or `@<user>` | that account's posts, pinned first |
28
+
29
+ Every item carries text, author + `avatar_url`, timestamps, permalink, engagement stats, and **media URLs** (X video resolves to the top-bitrate MP4). It never summarizes or ranks — you read `items[]` and choose the hook, the punchline comment, the stat worth a card. `max_records` is your spend ceiling; `nextCursor` resumes. Paid plans only.
30
+
31
+ Under the hood it brokers the **reddit-lead-gen** and **x-lead-gen** OfficeX apps rather than holding Reddit/X keys itself — so it's a job wrapping a job, and the primitive waits out the upstream job for you. Full payload/response reference in `references/primitives.md` → *Primitive: social/recycle*.
32
+
33
+ Pipe it forward: any `media[].url` goes straight into a layer or through `videos/download`; `profile.avatar_url` makes the "who said this" badge. **Recycled text is source material, not a script** — rewrite it to the hook/loop/payoff standard instead of pasting a comment thread on screen.
34
+
18
35
  ## Public raws catalog — browse BY CATEGORY (check here first)
19
36
 
20
37
  Before you hunt footage and long before you generate it, check what the platform **already has**. The public raws catalog is a curated, pre-tagged, free-to-use pool of short clips, organised into **categories** (shelves) — and browsing it by category is one of the highest-leverage moves available to you, for two reasons:
@@ -214,9 +214,9 @@ The licensed harness also carries the **generative build workflow** guidance (ch
214
214
  | `vidfarm inpaint <image> --mask <png> --prompt "…" [--region "label=…"] [--ref …] [--out <f>]` | `POST /api/v1/primitives/images/inpaint` (polls job) | masked image EDIT — replace ONLY the transparent-mask region, keep everything else (devcli twin of the /inpaint page) |
215
215
  | `vidfarm create-overlay "<subject>" [--key-color #00FF00] [--aspect-ratio 1:1] [--place <dir>] [--out <f>]` | `POST /api/v1/primitives/images/create-overlay` (polls job) | **Vox-style** transparent OVERLAY — AI image on a forced key-color background, chroma-keyed out in one job → ready-to-composite transparent PNG |
216
216
  | `vidfarm remove-greenscreen <image\|video> [--preset green\|blue\|white\|black\|digital-green\|magenta] [--key-color #00FF00] [--tolerance 0.3] [--local] [--gif] [--out <f>]` | `POST /api/v1/primitives/remove-greenscreen` (polls job) | chroma-key a FLAT solid background → transparent PNG/WebP (image) or WebM/VP9-alpha (video); auto-detects media kind. `--local` runs it FREE in-process (sharp/ffmpeg, no wallet); default cloud is billed at real compute × 1.2. **`--gif` writes a transparent GIF instead** (ANIMATED for a clip; `--gif-fps`/`--gif-width`/`--gif-alpha`) — local-only, 1-bit alpha, for GIF-only sticker surfaces; prefer PNG/WebP/WebM for compositing. Aliases: `greenscreen`, `remove-background-greenscreen`. |
217
- | `vidfarm cutout <image\|url> [--generate "<prompt>"] [--preset green] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--out <f>]` | **local, free, ffmpeg-only** (no job) — key + `alphaextract`/`cropdetect` trim | **The transparent explainer-STICKER maker.** Keys out the flat plate **and then shrinks the canvas to the cutout's true min width/height** (a 1024² mostly-empty plate → a snug sticker whose pixel size IS the subject) so you can scale/position it precisely. `--generate` AI-generates the graphic first on a matching chroma plate (that step is the billed image primitive), then keys+trims in one shot; without it, keys+trims a file/url you already have. **IMAGE-only** (a moving subject has no single bounding box — key a clip with `remove-greenscreen`). Prefer this over `create-overlay` locally: same idea, but free and auto-trimmed. `--pad` keeps transparent breathing room; `--json` reports final `width`/`height`/`area_reduced_pct`. Alias: `sticker`. See recipe `cutout-graphics-for-explainers.md`. |
217
+ | `vidfarm cutout <image\|url> [--generate "<prompt>"] [--preset green] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--out <f>]` | **local, free, ffmpeg-only** (no job) — key + `alphaextract`/`cropdetect` trim | **The transparent explainer-STICKER maker.** Keys out the flat plate **and then shrinks the canvas to the cutout's true min width/height** (a 1024² mostly-empty plate → a snug sticker whose pixel size IS the subject) so you can scale/position it precisely. `--generate` AI-generates the graphic first on a matching chroma plate (that step is the billed image primitive), then keys+trims in one shot; without it, keys+trims a file/url you already have. **IMAGE-only** (a moving subject has no single bounding box — key a clip with `remove-greenscreen`). Prefer this over `create-overlay` locally: same idea, but free and auto-trimmed. `--pad` keeps transparent breathing room; `--json` reports final `width`/`height`/`area_reduced_pct`, plus `hole_pct`/`hollow` — the "the key ate the fill" check (outline-only art keys into a rim around a transparent hole; `--generate` prompts against it automatically, and the console prints a `Hollow:` warning with the fix). Alias: `sticker`. See recipe `cutout-graphics-for-explainers.md`. |
218
218
  | `vidfarm mask <image\|url> [--crop x,y,w,h] [--flat <hex>] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--keep-region <f>] [--out <f>]` | **local, free** (no job) — ffmpeg crop + ONNX matting (or ffmpeg chroma-key) + `cropdetect` trim | **Lift an illustration OUT of an image you already have** (infographic / poster / marketing graphic / brand sheet / screenshot) → snug transparent PNG, the same reusable explainer sticker `cutout` makes but with **$0 and zero AI generation** — the cost-saving move whenever source art exists. `--crop x,y,w,h` (pixels **or** %) isolates ONE element from a multi-illustration source before masking (re-run with different rects to grab each). Background removed by **local ONNX matting** (any/busy background) by default, or **`--flat <hexcolor>`** chroma-keys a solid fill for crisper edges (an infographic's cream/white paper); then trims to the subject's true min width/height. **IMAGE-only** (matte a clip with `remove-background`). Aliases: `isolate`, `extract`. See recipe `cutout-graphics-for-explainers.md` → "Mask from an image you already have". |
219
- | `vidfarm sticker-pack [sheet\|url] [--generate "<theme>"] [--items "a,b,c"] [--count <n>] [--dry-run] [--gap <pct>] [--min-area <pct>] [--output-format png\|webp\|gif] [--out-dir <d>]` | **local, free, ffmpeg-only** (no job; only `--generate` bills, ONCE for the whole set) — key + alpha-channel segmentation + per-item trim | **The STICKER-PACK maker — the answer whenever a director asks for "a sticker pack" / prop set / icon set.** A pack is ONE greenscreen sheet holding every item, keyed once and then masked apart: 1/N the cost of N `cutout` calls, and the only way a cast stays on-style. Finds each item **automatically** by segmenting the keyed sheet's alpha into connected islands — no hand-measured `--crop` rects — and writes one snug transparent file per item (named from `--items`, reading order) plus a `stickers.json` manifest. `--dry-run` prints the detected boxes first; `--gap` merges (lower) or splits (raise) items that came out joined/broken; items have **no maximum size** — a full-frame landscape/backdrop is as valid a sticker as a 3% icon. **Plate color is chosen for you:** when generating it reads the subject and moves the plate off any hue the art uses (green → magenta → blue → black → white — a pack of leaves/frogs/money on GREEN would key holes through the art), and when splitting an existing sheet it DETECTS the plate from the sheet's four corners, so a red/purple sheet handed back from a web tool just works. Pin it with `--key-color`/`--preset`, or `--no-auto-key` for plain green. `--output-format gif` emits 1-bit-alpha GIFs for GIF-only surfaces. IMAGE-only. Aliases: `stickers`, `sticker-sheet`. See recipe `cutout-graphics-for-explainers.md` → "A sticker pack". |
219
+ | `vidfarm sticker-pack [sheet\|url] [--generate "<theme>"] [--items "a,b,c"] [--count <n>] [--dry-run] [--gap <pct>] [--min-area <pct>] [--output-format png\|webp\|gif] [--out-dir <d>]` | **local, free, ffmpeg-only** (no job; only `--generate` bills, ONCE for the whole set) — key + alpha-channel segmentation + per-item trim | **The STICKER-PACK maker — the answer whenever a director asks for "a sticker pack" / prop set / icon set.** A pack is ONE greenscreen sheet holding every item, keyed once and then masked apart: 1/N the cost of N `cutout` calls, and the only way a cast stays on-style. Finds each item **automatically** by segmenting the keyed sheet's alpha into connected islands — no hand-measured `--crop` rects — and writes one snug transparent file per item (named from `--items`, reading order) plus a `stickers.json` manifest. `--dry-run` prints the detected boxes first; `--gap` merges (lower) or splits (raise) items that came out joined/broken; items have **no maximum size** — a full-frame landscape/backdrop is as valid a sticker as a 3% icon. **Plate color is chosen for you:** when generating it reads the subject and moves the plate off any hue the art uses (green → magenta → blue → black → white — a pack of leaves/frogs/money on GREEN would key holes through the art), and when splitting an existing sheet it DETECTS the plate from the sheet's four corners, so a red/purple sheet handed back from a web tool just works. Pin it with `--key-color`/`--preset`, or `--no-auto-key` for plain green. **The ART is made key-safe too:** the generation prompt is auto-appended with "closed, solidly filled shapes, no outline-only/hollow art, nothing in the plate hue or a near-shade, fully opaque, no glow/translucency" — the fix for stickers that come back as a rim around a transparent hole — and after keying each item reports `holes`/`hole_pct`/`hollow` (console `⚠ N% hollow` at ≥20%, plus `--json` and `stickers.json`). It **warns, never blocks** (a ring/frame/donut reads identically); re-generate with the fill clause, or lift that one item with `vidfarm mask --crop …`. `--output-format gif` emits 1-bit-alpha GIFs for GIF-only surfaces. IMAGE-only. Aliases: `stickers`, `sticker-sheet`. See recipe `cutout-graphics-for-explainers.md` → "A sticker pack". |
220
220
  | `vidfarm tts "…" [--style "…"] [--voice <v>] [--out <file>]` | (LOCAL-FIRST: your own OPENAI/GEMINI/OPENROUTER_API_KEY → audio file on disk; `--cloud` = `POST /api/v1/primitives/audio/speech` + poll, ElevenLabs on the platform key by default, `--own-key` for yours) | text → narration audio; `--cloud --voice <voice_id>` picks an ElevenLabs voice |
221
221
  | `vidfarm music "<prompt>" [--length <sec>] [--out <f>] [--own-key]` | `POST /api/v1/primitives/music/generate` (polls job) | prompt → music track (ElevenLabs; platform key + wallet by default, `--own-key` for yours) |
222
222
  | `vidfarm voices [--own-key] [--limit N]` | `GET /api/v1/primitives/audio/voices` | list ElevenLabs voices (voice_id/name/labels) for `tts --voice`; default a voice + tell the user they can choose |
@@ -245,7 +245,7 @@ The licensed harness also carries the **generative build workflow** guidance (ch
245
245
  | `vidfarm whoami` | `GET /api/v1/user/me` | who am I (also prints the active cost mode) |
246
246
  | `vidfarm cost-mode [minimize\|hybrid\|rich-ai\|pure-videogen]` | (local `~/.vidfarm/cost-mode.json`) | show/set the money-saving preference every billed command respects; no arg = show + explain each; `--clear` forgets it |
247
247
  | `vidfarm mode [interactive\|autonomous]` | local (`~/.vidfarm/interaction-mode.json`) | **The SECOND preference axis — ask it alongside cost mode.** Is the human in the loop? `interactive` = at checkpoints you hand them a copy-paste brief (an image prompt for a FREE web generator; a keyword list for sourcing clips) and they hand files back — usually BETTER output, and in `minimize` the only way to get custom art at all. `autonomous` = you finish solo within the budget. Independent of cost mode: every cost mode runs either way. No arg prints the pair + the explainer to relay; `--clear` forgets; per-run `--interactive`/`--autonomous`. Aliases: `interaction-mode`, `interaction`. |
248
- | `vidfarm handoff image --theme "<what>" [--items "a,b,c"] [--style …] [--single]` | local (pure text) | **The interactive-mode IMAGE brief.** Prints the exact prompt, the numbered steps, the free tools (meta.ai / ChatGPT / Gemini / HF Spaces) and the follow-up command. Defaults to a **sticker pack**: ONE sheet holding every item on a chroma plate → `vidfarm sticker-pack` splits it for $0. Picks a plate the art won't collide with (green art → magenta plate) and carries that `--key-color` into the follow-up. `--single` for one subject. |
248
+ | `vidfarm handoff image --theme "<what>" [--items "a,b,c"] [--style …] [--single]` | local (pure text) | **The interactive-mode IMAGE brief.** Prints the exact prompt, the numbered steps, the free tools (meta.ai / ChatGPT / Gemini / HF Spaces) and the follow-up command. Defaults to a **sticker pack**: ONE sheet holding every item on a chroma plate → `vidfarm sticker-pack` splits it for $0. Picks a plate the art won't collide with (green art → magenta plate), spells out the **key-safe fill rules** (closed solidly-filled shapes, no outline-only/hollow art, nothing in a near-plate shade, fully opaque) so the sheet doesn't come back as rims around holes, and carries that `--key-color` into the follow-up. `--single` for one subject. |
249
249
  | `vidfarm handoff raws --keywords "a,b" [--platforms tiktok,youtube] [--count N] [--purpose "…"]` | local (pure text) | **The interactive-mode CLIP-SOURCING brief** — the bottom rung of the sourcing ladder (browser control → `clipper`/`raws scan --cloud` → public raws → the human). Prints what to search, how to download (a Google *search* for a downloader, never a link that rots), and the import command for when the folder is ready. |
250
250
  | `vidfarm storyboard [dir] [--init] [--frames "Title\|scene,…"] [--json]` | local (`STORYBOARD.md` / `SCRIPT.md`) | **The plan pass, and a core part of the composition format.** Scaffolds/reads the project's `STORYBOARD.md`: ordered frames with `duration`/`status`/`src`/`scene`/`voiceover`. The Vidfarm editor renders this file in its **Storyboard** view (contact sheet + per-frame comments + `outline → built → animated` progress), so it's the cheapest place to get a director's approval before building. Not to be confused with `vidfarm sequence` (which GENERATES storyboard images for the pure-videogen pipeline). Alias: `plan`. |
251
251
  | `vidfarm wallet [--job <id>\|--tracer <t>] [--limit <n>]` | `GET /api/v1/user/me/wallet` | cost log: balance + lifetime spend + recent charges. `--job <renderJobId>` prints **what that one video cost** (sums its charges); `--tracer <t>` sums a tracer. Cloud-only; readable on the free plan too (shows $0.00). Aliases: `spend`, `costs` |
@@ -254,6 +254,7 @@ The licensed harness also carries the **generative build workflow** guidance (ch
254
254
  | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
255
255
  | `vidfarm download-video <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | **download a video FROM A WEBSITE** (YouTube/TikTok/IG/X/other supported posts) into durable Vidfarm media; photo/carousel posts return an ordered slideshow. **PAID PLAN** (wallet-billed resolver; free plans get 402). Aliases: `download-post`, `download-url` |
256
256
  | `vidfarm download-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download just the AUDIO track (music/voice) off a website post into a durable audio file. **PAID PLAN**. Alias: `download-post-audio` |
257
+ | `vidfarm recycle <source>` | `POST /api/v1/primitives/social/recycle` + poll | RECYCLE a Reddit/X source into reusable JSON ("tweet to tiktok", "reddit to tiktok"): thread + comments, subreddit threads, X thread + replies, or an account's posts — text, author + avatar, stats, media URLs. `--out` saves the full payload. **PAID PLAN**. Aliases: `recycle-social`, `tweet-to-video`, `reddit-to-video` |
257
258
 
258
259
  > **Free plan / `cost-mode minimize`:** both download commands are gated by `guardBilled` and will refuse or warn. The free path is **Chrome**: open the URL in a real browser, save the file off the page (right-click → *Save video as*, downloads pane, or a Googled `"<platform> video downloader"` site), then `vidfarm put-file ./the-file.mp4 --folder <folder>` to bring it in for $0. The CLI prints these steps on a 402. Never tell a free user the video simply can't be downloaded.
259
260
  | `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
@@ -365,6 +366,8 @@ vidfarm doctor # ✓/⚠/✗ for node, ffmpeg, hy
365
366
  - **`ffmpeg`** — the media engine the devcli drives for every local media op: `remove-greenscreen --local` (chroma-key → transparent PNG/WebP/WebM, ProRes fallback where the local libvpx can't do WebM alpha), STT/caption demux, `raws scan` clip cutting, `stills`, audio trims. **Hard requirement** — `vidfarm doctor` exits non-zero without it.
366
367
  - **`hyperframes`** — **Vidfarm's own render/animation engine, open-source under a whitelabel.** *Vidfarm renders ARE hyperframes renders.* The devcli bundles it and re-exposes everything it does locally as **`vidfarm hf <subcommand>`** — `vidfarm hf render <dir> -o out.mp4`, `capture`, `validate`, `preview`, `doctor`, plus the ONNX `remove-background` matting, Kokoro TTS, and Whisper STT engines the `tts`/`stt`/`remove-background` commands ride. Installing it standalone makes these run **natively** instead of a slower `npx -y hyperframes` fallback (which `vidfarm doctor` will warn about).
367
368
 
369
+ **The local studio UI is whitelabeled automatically.** `hyperframes preview` (and `vidfarm hf preview`) serves a studio SPA whose stock shell paints an upstream "HeyGen · HyperFrames" header logo and a "HyperFrames Studio" tab title. Every devcli hyperframes spawn — and `vidfarm doctor` — idempotently patches that shell in place (`src/devcli/studio-brand.ts`) so the header reads **VidFarm**, the tab title is **VidFarm Studio**, and the product name in the studio's own copy (lint panel, feedback prompt, storyboard/agent prompts, clipboard writes) is rewritten. `.hyperframes/` paths and `data-hyperframes-*` DOM hooks are preserved verbatim — they name real files and hooks — and CLI verbs map to their twins (`hyperframes render` → `vidfarm render`). A fresh `npm install` restores the stock shell; the next devcli command (or `vidfarm doctor`) re-applies it. If a director ever screenshots upstream branding, run `vidfarm doctor`.
370
+
368
371
  **Treat `hyperframes` as Vidfarm** (it is — just whitelabeled): never point it at an external HeyGen/vendor account, publish service, or telemetry endpoint. Keep `HYPERFRAMES_SKIP_SKILLS=1` + `HYPERFRAMES_NO_TELEMETRY=1` set (the devcli sets these for its children); do not run `hyperframes auth`/`cloud`/`publish`/`play`/`feedback` — Vidfarm's own render/share/telemetry surfaces replace all of them. Everything else `hyperframes` can do locally is fair game and encouraged.
369
372
 
370
373
  ## Local dev loop (`vidfarm serve`)
@@ -595,7 +595,7 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
595
595
 
596
596
  **Make the sticker (`vidfarm cutout`).** Local, free, ffmpeg-only, image-only. `vidfarm cutout --generate "<subject>"` AI-generates on a matching plate then keys+trims (only the generation is billed); `vidfarm cutout <file|url>` keys+trims a graphic you already have for $0. Because the output is trimmed tight, its on-frame **width % maps directly to how big the subject reads** — no invisible padding skewing the layout. Knobs: `--preset`/`--key-color` (match the plate), `--pad <px>` (transparent breathing room), `--alpha-threshold <0..255>` (trim faint haloed edges), `--output-format webp`, `--no-trim`. A *moving* subject has no single bounding box → key it as a clip with `vidfarm remove-greenscreen <video>` (→ transparent WebM) instead.
597
597
 
598
- **Make a whole PACK at once (`vidfarm sticker-pack`).** When the scene needs a *set* — props, icons, a cast, plus any backdrops — don't run `cutout` N times. Generate ONE greenscreen sheet holding every item and split it locally: `vidfarm sticker-pack --generate "<theme>" --items "a,b,c" --out-dir ./stickers` (one billed image job for the set), or `vidfarm sticker-pack ./sheet.png` for $0 on a sheet you already have. It keys the plate once, **segments the sheet's alpha channel to find each item automatically** (no hand-measured crop rects), trims each to its true box, and writes `stickers.json`. `--dry-run` to inspect the boxes; `--gap` to fix items that merged (lower) or split (raise). **Item size is unbounded** — a full-frame landscape/skyline/backdrop is as legitimate a sticker as a small icon, and belongs in the same sheet so it shares the cast's style. Recipe: `recipes/cutout-graphics-for-explainers.md` → "A sticker pack".
598
+ **Make a whole PACK at once (`vidfarm sticker-pack`).** When the scene needs a *set* — props, icons, a cast, plus any backdrops — don't run `cutout` N times. Generate ONE greenscreen sheet holding every item and split it locally: `vidfarm sticker-pack --generate "<theme>" --items "a,b,c" --out-dir ./stickers` (one billed image job for the set), or `vidfarm sticker-pack ./sheet.png` for $0 on a sheet you already have. It keys the plate once, **segments the sheet's alpha channel to find each item automatically** (no hand-measured crop rects), trims each to its true box, and writes `stickers.json`. `--dry-run` to inspect the boxes; `--gap` to fix items that merged (lower) or split (raise). **Ask for solidly filled art** — outline-only/hollow shapes (or fills in a near-plate shade) get their interiors keyed away and composite as rims around transparent holes; the generation prompt says so automatically, and each item reports `⚠ N% hollow` / `hole_pct` after the key so you catch it before placing. **Item size is unbounded** — a full-frame landscape/skyline/backdrop is as legitimate a sticker as a small icon, and belongs in the same sheet so it shares the cast's style. Recipe: `recipes/cutout-graphics-for-explainers.md` → "A sticker pack".
599
599
 
600
600
  **Sticker formats.** Still sticker → **PNG** (or `--output-format webp` for smaller files). Sticker whose own artwork moves → **transparent WebM** via `vidfarm remove-greenscreen <video>` (ProRes `.mov` is the local fallback; `--cloud` always returns WebM). Sticker destined for a **GIF-only surface** (chat, forum, Notion, a shareable pack) → `--output-format gif` on `sticker-pack`, or `vidfarm remove-greenscreen <video> --gif` for an animated one; GIF alpha is 1-bit, so edges harden and soft shadows vanish — never the choice for compositing on a timeline.
601
601
 
@@ -35,7 +35,7 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/images/remove-background" \
35
35
 
36
36
  - `POST /api/v1/primitives/images/create-overlay` (flat alias: `POST /api/v1/primitives/create-media-overlay`)
37
37
  - Body: `{ "tracer": "...", "payload": { "prompt": "a cartoon rocket ship, flat vector illustration", "provider"?: "...", "model"?: "...", "prompt_attachments"?: ["https://..."], "aspect_ratio"?: "1:1", "image_size"?: "1K"|"2K"|"4K", "key_color"?: "#00FF00", "tolerance"?: 0.3, "softness"?: 0.1, "despill"?: true, "output_format"?: "png"|"webp" }, "webhook_url"?: "..." }`
38
- - Just describe the **subject** in `prompt` — do NOT describe the background; the primitive appends the flat key-color background requirement for you.
38
+ - Just describe the **subject** in `prompt` — do NOT describe the background; the primitive appends the flat key-color background requirement for you, **and** the key-safe *artwork* requirement (closed solidly-filled shapes, no outline-only/hollow art, nothing on the subject in the key hue or a near-shade, fully opaque, no glow/translucency). That second clause is what stops the classic failure: outline art whose interior is bare plate keys into a **rim around a transparent hole**. If you build the prompt yourself for `images/generate` instead of using this primitive, write that clause in by hand.
39
39
  - `key_color` lets you key against something other than green if your subject is itself green-heavy (e.g. key against `#FF00FF` magenta for a green frog).
40
40
  - The chroma-key params (`tolerance`/`softness`/`despill`) behave exactly as in `image_remove_background_greenscreen`.
41
41
  - Response: `primary_file_url` is the finished transparent overlay; `greenscreen_source_url` is the raw pre-key frame (kept so you can re-key at a different tolerance without paying for another generation).
@@ -136,6 +136,85 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/audio/download" \
136
136
  -d '{"tracer": "demo-download-audio", "payload": {"source_url": "https://www.youtube.com/watch?v=example"}}'
137
137
  ```
138
138
 
139
+ ## Primitive: social/recycle
140
+
141
+ **"Tweet to TikTok" and "Reddit to TikTok" live here.** The text-and-thread sibling of `videos/download`: point it at a Reddit or X/Twitter source and it **decomposes** that source into reusable JSON — the post text, the comment/reply thread, every image/video media URL, the author's profile pic and follower count, and the engagement stats — so YOU decide what to recycle into a new video. It deliberately does **not** summarize, rank, score, or filter. It dumps the data back raw.
142
+
143
+ **Vidfarm does not hold Reddit/X vendor keys.** This primitive is a thin broker over the two OfficeX apps that already own that integration — **reddit-lead-gen** and **x-lead-gen** — called through their public REST job API. They own the vendor keys, retries, rate-limit budget, auto-pagination, and 90-day result storage; vidfarm owns target resolution, normalization, and the wallet charge. Both are async job APIs, so a recycle is **a job wrapping a job** — but the nesting is invisible: this primitive polls upstream to completion server-side and only resolves when the data is in hand, exactly like the GhostCut leg of `videos/remove-captions`. You poll `GET /api/v1/primitives/jobs/:jobId` like any other primitive.
144
+
145
+ - `POST /api/v1/primitives/social/recycle` (alias: `POST /api/v1/primitives/recycle-social`)
146
+ - Body: `{ "tracer": "...", "payload": { "source_url": "https://..." }, "webhook_url"?: "..." }`
147
+
148
+ ### The four shapes
149
+
150
+ | You pass | Mode (auto-detected) | You get |
151
+ | --- | --- | --- |
152
+ | `https://reddit.com/r/<sub>/comments/<id>/...` | `reddit_thread` | the post + its comment tree, flattened in reading order |
153
+ | `https://reddit.com/r/<sub>` (or `r/<sub>`) | `reddit_channel` | that subreddit's threads, paginated |
154
+ | `https://x.com/<user>/status/<id>` | `twitter_thread` | the tweet + the author's self-thread + its replies, paginated |
155
+ | `https://x.com/<user>` (or `@<user>`) | `twitter_channel` | that account's tweets (pinned first), paginated |
156
+
157
+ A fifth mode, `reddit_user` (`https://reddit.com/user/<name>` or `u/<name>`), returns a redditor's posts and comments. Pass `mode` explicitly to override auto-detection, or `handle` + `network` instead of a URL.
158
+
159
+ ### Payload
160
+
161
+ | Field | Default | Notes |
162
+ | --- | --- | --- |
163
+ | `source_url` | — | Reddit/X URL. Required unless you pass `handle` + `network`. Aliases: `url`, `thread_url`, `profile_url` |
164
+ | `handle` + `network` | — | `{ "handle": "wallstreetbets", "network": "reddit" }`. Aliases for handle: `username`, `screenname`, `subreddit` |
165
+ | `mode` | auto | Override the detected shape |
166
+ | `max_records` | `100` | Cap on records (1–1000). The lead-gen apps auto-paginate to reach it and reserve credits for `ceil(max_records / 25)` pages — **this is your spend ceiling** |
167
+ | `cursor` | — | Resume token from a previous run's `nextCursor` |
168
+ | `prompt_filter` | — | Hand the lead-gen app a Gemini scoring prompt; adds `match_score` + `ai_notes` per item and costs slightly more per task upstream. We surface it and never act on it |
169
+ | `query` | — | Restrict a channel pull to matching posts (Reddit search / X `from:<user> <query>`) |
170
+ | `filter` / `sort` / `time_filter` | — | Reddit listing controls (`hot`, `new`, `top`, …) |
171
+ | `search_type` | — | X search mode (`Top`, `Latest`, `Photos`, `Videos`) |
172
+ | `include_profile` | `true` | One extra upstream job for the subreddit/account avatar, banner, bio, follower count |
173
+ | `include_comments` | `true` | Thread modes only — set `false` to skip the replies leg (and its cost) |
174
+ | `save_raw` | `true` | Persist the untouched upstream pages as `raw-pages.json` |
175
+
176
+ ### Result
177
+
178
+ Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`, then read:
179
+
180
+ - `items[]` — the normalized view. Each entry: `{ network, kind: "post" | "comment", id, title, text, created_at, permalink, link_url, author: { handle, name, avatar_url, banner_url, followers, profile_url, bio, verified }, stats: { … }, media: [{ type: "image"|"video"|"gif", url, thumbnail_url, width, height, duration_ms }] }`. X video media resolves to the **highest-bitrate MP4** variant, not the HLS manifest.
181
+ - `profile` — the subreddit/account itself (avatar, banner, bio, subscriber/follower count).
182
+ - `mediaUrls[]` — every media URL flattened, ready to drop into layers or feed to `videos/download`.
183
+ - `nextCursor` — pass it back as `payload.cursor` to page further. `truncated: true` means there IS more.
184
+ - `counts` — `{ items, posts, comments, media, pages, upstream_jobs, credits_charged }`.
185
+ - `upstreamJobs[]` — one entry per lead-gen job we ran: `{ network, endpoint, job_id, status, tasks, credits_charged, view_url }`. **`view_url` opens a NocoDB spreadsheet of that pull — hand it to the human when they want to browse results visually.**
186
+ - `recycled.json` (also `primary_file_url`) — the **complete** normalized payload. `items` in the job result is trimmed when it would blow the job-record size limit; `itemsTruncatedInline: true` tells you to read the artifact instead.
187
+ - `raw-pages.json` — every untouched upstream page, so you can reach fields this primitive doesn't model.
188
+
189
+ - Billing: wallet, metered on the **OfficeX credits the lead-gen apps actually consumed** (`officex_social_recycle`), converted at `OFFICEX_CREDIT_USD`. A one-task thread costs a fraction of a 10-page timeline. Paid plans only — free plans get `402`, same as the download routes. If the *upstream* app runs out of OfficeX credits you get a `402` naming that app, which is an operator problem, not a customer one.
190
+ - devcli wrapper: `vidfarm recycle <source>` (aliases: `recycle-social`, `tweet-to-video`, `reddit-to-video`). Flags: `--max-records N`, `--cursor`, `--query`, `--filter hot|new|top`, `--mode`, `--network`, `--no-comments`, `--no-profile`, `--out ./recycled.json` (writes the FULL normalized payload to disk), `--no-wait`. It prints the counts, the upstream NocoDB `view_url`s, and the resume cursor.
191
+ - Legs: a recycle runs 1–3 upstream jobs (profile, feed, replies) **sequentially**, so a failure on the first never pays for the second. The optional profile leg degrades rather than sinking the pull.
192
+ - Timeouts: upstream polling gives up after `SOCIAL_RECYCLE_POLL_TIMEOUT_MS` (default 5 min); the upstream job may still finish and can be browsed in that app.
193
+
194
+ ### Turning a recycle into a video
195
+
196
+ 1. Recycle the source with a small `max_records` (25–50 is usually plenty for one video).
197
+ 2. Read `items[]` yourself and pick the beat: the post's hook line, the two funniest comments, the stat everyone reacted to.
198
+ 3. Pull any `media[].url` you want as real footage through `videos/download` or straight into a layer; use `profile.avatar_url` for the "who said this" badge.
199
+ 4. Script and build as normal. **The recycled text is source material, not a script** — rewrite it to the hook/loop/payoff standard rather than pasting a comment thread on screen.
200
+
201
+ Example:
202
+
203
+ ```bash
204
+ curl -X POST "$VIDFARM_BASE/api/v1/primitives/social/recycle" \
205
+ -H "vidfarm-api-key: $VIDFARM_API_KEY" \
206
+ -H "content-type: application/json" \
207
+ -d '{"tracer": "demo-recycle", "payload": {"source_url": "https://www.reddit.com/r/wallstreetbets/comments/p0esdp/slug/", "max_records": 50}}'
208
+ ```
209
+
210
+ ```bash
211
+ # An X account's recent posts, no replies, cheapest possible pull
212
+ curl -X POST "$VIDFARM_BASE/api/v1/primitives/recycle-social" \
213
+ -H "vidfarm-api-key: $VIDFARM_API_KEY" \
214
+ -H "content-type: application/json" \
215
+ -d '{"tracer": "demo-recycle-x", "payload": {"source_url": "https://x.com/elonmusk", "max_records": 40, "include_profile": true}}'
216
+ ```
217
+
139
218
  ## Primitive: video_remove_captions
140
219
 
141
220
  Remove burned-in captions/subtitles/on-screen text from any video URL (GhostCut-powered — the same pipeline auto-decompose uses for its caption-free mirror, exposed as a standalone reusable primitive). Result is a durable caption-free MP4 stored at a Vidfarm URL.