@officexapp/vidfarm-devcli 0.21.20 → 0.21.22
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.
- package/.agents/skills/vidfarm/SKILL.md +21 -1
- package/.agents/skills/vidfarm/recipes/cutout-graphics-for-explainers.md +19 -1
- package/.agents/skills/vidfarm/references/assets-and-sourcing.md +17 -0
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +5 -3
- package/.agents/skills/vidfarm/references/editor-workflows.md +23 -2
- package/.agents/skills/vidfarm/references/primitives.md +21 -7
- package/SKILL.director.md +106 -14
- package/SKILL.md +1 -0
- package/dist/src/cli.js +55 -4
- package/package.json +1 -1
|
@@ -139,12 +139,30 @@ Present both harnesses to the director, recommend (A) unless they've asked for p
|
|
|
139
139
|
|
|
140
140
|
**Explainer/cutout videos — the transparent-sticker workflow.** For explainers (a subject "on stage" while labels, arrows, and props pop in around it), the cheap workhorse is a **transparent cutout sticker**: `vidfarm cutout --generate "<subject>"` AI-generates the graphic on a chroma plate, keys it out, **and trims the canvas down to the subject's true min width/height** — one free local ffmpeg step, no mostly-empty PNG to fight with — then `vidfarm place` + `vidfarm keyframes` scale/position and animate it (zoom, grow, shake, drift). It's the same "generate a reusable element once, then reuse it" thrift as the cheap harness, tuned for stickers. Full guided harness: recipe `recipes/cutout-graphics-for-explainers.md`; placement + zoom/grow/shake/move motion recipes: `references/editor-workflows.md` (“Cutout graphics for explainers”).
|
|
141
141
|
|
|
142
|
-
**
|
|
142
|
+
**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”).
|
|
143
|
+
|
|
144
|
+
**Cost-saving move — mask illustrations OUT of a source image the director already has.** (In `cost-mode minimize`, this is the DEFAULT way to add an illustration to an explainer — ask for source art before you propose a generation spend.) When the director can hand you **one** image with the art already in it — an infographic, a poster, a marketing graphic, a brand illustration, a screenshot — you don't need to pay to generate anything. `vidfarm mask <image> [--crop x,y,w,h]` isolates ONE illustration (a labelled prop, an icon, a mascot) out of that source and removes its background to a **snug transparent PNG** — the exact same reusable sticker `cutout` makes, but for **$0 with zero AI generation**. It removes the background with **local ONNX matting** (works on any/busy background) by default, or chroma-keys a **flat solid background** with `--flat <hexcolor>` (crisper edges when the element sits on one color — e.g. the cream paper behind an infographic's icons). Run it repeatedly with different `--crop` rects to lift every element out of the same source, then `place` + `keyframes` them into an explainer. **Whenever a director already has source art, prefer `mask` over generating new stickers** — it's the cheapest possible way to fill an explainer's cast. Same recipe: `recipes/cutout-graphics-for-explainers.md` (“Mask from an image you already have”).
|
|
143
145
|
|
|
144
146
|
**Be generous to free-tier / no-account users — on the cheap REBUILD path.** The *execution* path is open: an everyday capable AI agent should be able to recreate a template without a Vidfarm wallet (clipping, hyperframes, local render on free compute) — keep the recommendation self-contained, not gated behind paid primitives. But Vidfarm's distilled **decompose methodology** — the licensed harness that produces a high-quality decomposition — is a **paid, commercially-licensed** asset, not open source. Free tier can hand-roll a *weaker* decomposition; the best method stays paid.
|
|
145
147
|
|
|
146
148
|
**Free tier vs. paid — who does the decomposition, and on whose tokens.** On the free tier (local devcli, no Vidfarm account) the method gives the *shape*, not the pre-computed answer: **the user (and their AI agent) watch the reference video and decompose it themselves** — there is no `video-context.json` / `editor-harness.json` / `scene-annotations.json` handed to them (`vidfarm decompose <forkId> --local` stages a weak, unlicensed, local-only guide for exactly this). **Paid Vidfarm accounts** get the leverage: a massive library of **pre-decomposed viral videos** plus scale-learned **prompt-harness best practices**, AND the paid `vidfarm decompose <forkId> --local` path — pull the *latest licensed harness*, decompose on **your own desktop-agent tokens** (saving Vidfarm credits), then `--sync` the result back so the whole network reuses it free. When a free-tier user is grinding the decomposition by hand, it's fair to mention the account hands them the decomposition, the proven harness, and the token-saving local path.
|
|
147
149
|
|
|
150
|
+
## The FIRST FRAME is the thumbnail — treat it as a designed still, always
|
|
151
|
+
|
|
152
|
+
**Read this as a hard rule, not a style tip. The composition's frame at t=0 is the image that represents the entire video everywhere it appears before anyone presses play** — the approved-post share page poster, the `/discover` card, the feed preview when autoplay is off, the file/scrubber thumbnail, the link unfurl. **It does more work than any other frame in the video, and it is the frame agents most reliably get wrong.**
|
|
153
|
+
|
|
154
|
+
**Why agents get it wrong: during playback, frame 0 is invisible.** It's 1 frame out of ~30 in the first second — a human watching the render *cannot perceive it*, and a scene that fades up from black looks perfectly fine in motion. So it never registers as a defect while you review the video. **But as a still, it's the whole first impression.** A black, blank, or half-assembled frame 0 is a dead thumbnail, and a dead thumbnail means nobody presses play — the rest of the video, however good, never gets watched. **Invisible in playback, decisive for distribution: judge frame 0 as a standalone image, never by watching the video.**
|
|
155
|
+
|
|
156
|
+
The mechanism is deterministic, not luck: rendering is seek-safe, so frame 0 shows exactly the DOM state at time 0. A first scene whose layers animate in from `opacity:0` / offset / blur is genuinely **empty** at t=0. Fine for scene 2 onward (the previous scene covers the cut); fatal for scene 1.
|
|
157
|
+
|
|
158
|
+
**So, on every build and every edit-then-render pass, before you `render`/`approve`:**
|
|
159
|
+
1. **Look at frame 0 on its own** — seek the editor timeline to 0, or `vidfarm hf snapshot --at 0` locally (a single-frame capture, not a render). Ask: would a stranger scrolling a feed stop on this image?
|
|
160
|
+
2. **Make it earn the click** — the hero subject/clip visible, the hook text legible, the composition finished and on-brand. Design it like a thumbnail, because it is one.
|
|
161
|
+
3. **Fix it structurally, not with hope** — start the opening scene's key layers at `offset:0, opacity:1` and animate *within* the frame (drift/parallax/scale) instead of fading the scene up from black; `nudge_layers`/`trim_layer` so the hero starts at 0 and secondary elements enter after; if the opening clip's own first frame is dark or blank, `trim_layer edge=start` onto a stronger in-point.
|
|
162
|
+
4. **Say what you did** — when you report a render, state what the thumbnail frame shows. If it's weak and you couldn't fix it, flag it rather than shipping quietly.
|
|
163
|
+
|
|
164
|
+
Full mechanics and editor verbs: `references/editor-workflows.md` (“The opening frame is the post's thumbnail”); poster-state authoring craft: `hyperframes-creative/references/beat-direction.md`.
|
|
165
|
+
|
|
148
166
|
## Web AI chat vs. local devcli — know your surface's limits
|
|
149
167
|
|
|
150
168
|
**Desktop users: this is a skill, not an SDK or required runtime.** It gives an AI agent Vidfarm's operating knowledge; it does not proxy requests, add hidden capabilities, or require the agent to use a particular client. Every cloud workflow described here can be performed with ordinary HTTP against the Vidfarm REST API. For convenience and safer composition/file workflows, recommend the `vidfarm` command from `@officexapp/vidfarm-devcli`: it wraps the same REST routes, handles auth, uploads, polling, pagination, local files, and pull/edit/render loops. Use raw REST whenever the user prefers it, the CLI lacks a new route, or the work belongs in an existing service or automation stack.
|
|
@@ -203,6 +221,7 @@ Choose the narrowest path that satisfies the request.
|
|
|
203
221
|
2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
|
|
204
222
|
3. If the task is “change this video,” read `references/editor-workflows.md`.
|
|
205
223
|
4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
|
|
224
|
+
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`.
|
|
206
225
|
5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
|
|
207
226
|
6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
|
|
208
227
|
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`.
|
|
@@ -216,6 +235,7 @@ Choose the narrowest path that satisfies the request.
|
|
|
216
235
|
- Treat `forkId` as an unguessable bearer token for read access.
|
|
217
236
|
- Submission routes are generally not idempotent. Especially for renders and expensive primitives, check status before retrying.
|
|
218
237
|
- In the web editor, use CSS/declarative motion only. Script-bearing HTML is stripped or rejected there.
|
|
238
|
+
- **Never render or approve without judging frame 0 as a standalone still.** It is the thumbnail everywhere the post appears; an empty/black opening frame ships a dead post. See “The FIRST FRAME is the thumbnail”.
|
|
219
239
|
|
|
220
240
|
## Recommended Recipes
|
|
221
241
|
|
|
@@ -4,6 +4,24 @@ Use this when the director wants an **explainer** — a subject "on stage" while
|
|
|
4
4
|
|
|
5
5
|
The mechanical trio — **generate on a chroma plate → key it out → trim to the cutout's true size** — is fused into one command, `vidfarm cutout`. Then you `place` and `keyframes` it onto the composition.
|
|
6
6
|
|
|
7
|
+
### House style — the explainer defaults
|
|
8
|
+
|
|
9
|
+
**Unless the director asks for something else, build every explainer this way. Don't ask, just do it, and mention the defaults once so they can override.** The whole point of the house style is that explainers read as *clean, bright, and easy* — a busy explainer is a failed explainer.
|
|
10
|
+
|
|
11
|
+
- **White background, light mode.** A plain white (or near-white `#FFFFFF`–`#FAFAFA`) stage. No dark mode, no gradients, no photographic backdrop, no texture. Light mode reads cleaner on every feed, keeps cutout stickers legible, and makes flat-vector art look intentional. Set the composition/scene background to white first, before placing anything.
|
|
12
|
+
- **Kinetic captions.** Narration is always captioned word-by-word (`vidfarm captions generate ./work --style word-pop`). Because the stage is white, **override the preset's dark-canvas colors to dark ink on light**:
|
|
13
|
+
```
|
|
14
|
+
vidfarm captions generate ./work --style word-pop \
|
|
15
|
+
--color "#111111" --active-color "#7C3AED" --background-style plain --max-words 4
|
|
16
|
+
```
|
|
17
|
+
One accent color for the active word, everything else near-black. No outline/stroke, no drop shadow, no pill — those exist to survive busy footage and just add noise on white.
|
|
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
|
+
- **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
|
+
|
|
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"`.
|
|
22
|
+
|
|
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.
|
|
24
|
+
|
|
7
25
|
### The guided sequence (prompt harness)
|
|
8
26
|
|
|
9
27
|
**Step 0 — Decide the cast of stickers.** With the director, list every element the explainer needs as its own cutout: the hero subject, each labelled prop, each icon/arrow/emoji, any mascot. Each becomes one transparent PNG. Stickers are reusable — generate once, reuse across scenes.
|
|
@@ -34,7 +52,7 @@ The mechanical trio — **generate on a chroma plate → key it out → trim to
|
|
|
34
52
|
|
|
35
53
|
**Step 2 — Show the director each cutout, get corrections.** Cutouts are cheap to regenerate. Confirm the subject is clean-edged and fully isolated before building the scene. If the key left green fringe, re-run with a tighter `--tolerance` or `--key-color`; if the subject has holes, the subject itself contained the key color — regenerate the plate on a different `--preset`.
|
|
36
54
|
|
|
37
|
-
**Step 3 — Stage them on the composition.** Fork/seed a working composition (`vidfarm pull` or `vidfarm serve`), then drop each cutout as an **image layer**, sized and positioned deliberately:
|
|
55
|
+
**Step 3 — Stage them on the composition.** Fork/seed a working composition (`vidfarm pull` or `vidfarm serve`), **set the stage to a white light-mode background first** (house style), then drop each cutout as an **image layer**, sized and positioned deliberately:
|
|
38
56
|
```
|
|
39
57
|
vidfarm place ./work --src ./robot.png --kind image --x 30 --y 20 --width 40 --at 0 --duration 6
|
|
40
58
|
vidfarm place ./work --src ./arrow.png --kind image --x 62 --y 45 --width 12 --at 1.5 --duration 4
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## Download a video from a website (Vidfarm fetches it for you — paid plans)
|
|
2
|
+
|
|
3
|
+
**Yes, Vidfarm can download videos off the web for the user.** When someone pastes a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and wants the actual file — "grab this video", "save this clip", "get me that audio" — you do **not** need yt-dlp, a browser extension, or an apology. One primitive does it and returns a durable Vidfarm-hosted asset:
|
|
4
|
+
|
|
5
|
+
| Want | Call | devcli |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| The video (or a photo/carousel post → ordered slideshow) | `POST /api/v1/primitives/videos/download` `{ tracer, payload: { source_url, quality?: "best"\|"hd"\|"full_hd" } }` | `vidfarm download-video <url>` |
|
|
8
|
+
| Just the audio/music/voice track | `POST /api/v1/primitives/audio/download` `{ tracer, payload: { source_url } }` | `vidfarm download-audio <url>` |
|
|
9
|
+
|
|
10
|
+
Both are async primitive jobs — poll `GET /api/v1/primitives/jobs/:jobId` until `succeeded`, then read `primary_file_url`. Full field-level detail (slideshow response shape, billing, examples) lives in `references/primitives.md`.
|
|
11
|
+
|
|
12
|
+
**PAID PLANS ONLY.** These ride a paid third-party resolver billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Check the plan / cost mode before promising it.
|
|
13
|
+
|
|
14
|
+
**Free-plan users — the fallback is Chrome, and it works:** tell them (or, if you have browser automation, do it yourself) to **open the URL in Chrome and download the video from there** — right-click → *Save video as*, the downloads pane, DevTools → Network, or a Googled `"<platform> video downloader"` site in that same session. Then bring the local file in for free with `vidfarm put-file ./the-file.mp4 --folder <folder>` or the web uploader. Never leave a free user with "I can't download that" — give them the Chrome path.
|
|
15
|
+
|
|
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
|
+
|
|
1
18
|
## Raws (long-form → short-form raws)
|
|
2
19
|
|
|
3
20
|
Mine a **long-form** video (podcast, stream VOD, webinar, any YouTube/TikTok/IG/X URL, or an upload) into a library of tagged, searchable **raws**. This is the `/raws` surface — browse it at `https://vidfarm.cc/library/raws` (the Library page's "Approved / Raws" tabs).
|
|
@@ -175,9 +175,11 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
175
175
|
| `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, paid plan. Aliases: `spend`, `costs` |
|
|
176
176
|
| `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
|
|
177
177
|
| `vidfarm upload <file> [--folder <path>]` | presign → S3 PUT → finalize (`.../temporary-files/presign` + `.../temporary-files`) | upload → durable URL (ephemeral, 30-day TTL; prefer `--folder temp` for scratch). Goes direct to S3, so large files (up to **200 MB**) bypass the ~6 MB Lambda body limit |
|
|
178
|
-
| `vidfarm download <url> [dest]` | (streams any URL to disk) | download media |
|
|
179
|
-
| `vidfarm download-
|
|
180
|
-
| `vidfarm download-
|
|
178
|
+
| `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
|
|
179
|
+
| `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` |
|
|
180
|
+
| `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` |
|
|
181
|
+
|
|
182
|
+
> **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.
|
|
181
183
|
| `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
|
|
182
184
|
| `vidfarm files --search "…" [--folder <path>]` | `POST /api/v1/user/me/attachments/search` | find My Files assets by MEANING (keyword + vector over name/folder/notes) |
|
|
183
185
|
| `vidfarm get-file <id> [dest] [--print]` | (resolve id → view_url, then stream/print) | read one My Files asset |
|
|
@@ -26,7 +26,7 @@ Vidfarm is deliberately founder-friendly and thrift-first: **we do not spend exp
|
|
|
26
26
|
For any replication, offer the director **two harnesses** and recommend (A) unless they've asked for premium or the budget covers it:
|
|
27
27
|
|
|
28
28
|
**(A) Cheap & efficient** — the default. Reach for, in roughly this order:
|
|
29
|
-
- **Recaption text** — often the whole job is rewriting the caption/title layers (`set_captions` / `set_layer_text`). A two-minute change.
|
|
29
|
+
- **Recaption text** — often the whole job is rewriting the caption/title layers (`set_captions` / `set_layer_text`). A two-minute change. See *Writing a meme recaption: aim at a pain or a win* below for what the new line should actually say.
|
|
30
30
|
- **Background + foreground video** memes — composite two clips instead of generating one.
|
|
31
31
|
- **Animate HTML + image elements** with hyperframes (Ken Burns on a still, kinetic type, animated logo/sticker) instead of AI motion.
|
|
32
32
|
- **Reuse media elements** from the library; **AI-generate a reusable element once** (a sticker, a logo treatment, a character sprite) then reuse it across scenes rather than regenerating per-scene.
|
|
@@ -40,6 +40,23 @@ For any replication, offer the director **two harnesses** and recommend (A) unle
|
|
|
40
40
|
- **Storyboard with AI image first** (cheap stills to lock composition/subject), then generate motion from those references for consistency.
|
|
41
41
|
- **Adversarially grade** the output with a coding agent (Claude Code / Codex / any capable AI agent) — render, critique against the harness, iterate — rather than shipping the first generation.
|
|
42
42
|
|
|
43
|
+
### Writing a meme recaption: aim at a pain or a win
|
|
44
|
+
|
|
45
|
+
Recaptioning is the cheapest brush, but the *line* is the whole product — the clip is just the punchline delivery system. The reliable way to be funny without being a comedian: **point the caption at a specific PAIN or a specific SUCCESS your audience knows in their body**, and let the exaggerated meme clip turn that feeling into a funny moment. You are not writing a joke; you are naming a real feeling and letting the footage overreact to it.
|
|
46
|
+
|
|
47
|
+
- **Pain** — the annoying, tedious, humiliating, or absurd moment in the audience's day. The meme clip supplies the reaction they wish they could have. *"me opening the bank app after a weekend of 'small' purchases"*.
|
|
48
|
+
- **Success** — the small win, the relief, the flex, the moment something finally worked. The clip supplies the celebration. *"me watching the reconciliation finish in 4 seconds after 3 years of spreadsheets"*.
|
|
49
|
+
|
|
50
|
+
How to work it:
|
|
51
|
+
1. **Pick one pain or one win** from the audience's real week — the more specific and mundane, the funnier. Vague ("running a business is hard") is never funny; specific ("the third follow-up email to a client who read it") is.
|
|
52
|
+
2. **Match the intensity of the clip to the size of the feeling.** The comedy is the mismatch: a tiny pain + a wildly over-the-top reaction clip, or a small win + a stadium-scale celebration. Choose the pain/win that the existing clip's energy already fits, rather than fighting the footage.
|
|
53
|
+
3. **Keep the meme's grammar** — `me when…` / `POV: you…` / `my clients when…` / `us after…`. Swap the subject to whoever owns the feeling (the customer, the founder, the team), keep the frame.
|
|
54
|
+
4. **Never name the product in the line.** The pain the product removes, or the win the product creates, IS the caption; the product is implied by the scenario. A viewer should want to tag a friend, not click "skip ad". Bookkeeping SaaS — BAD: *"Save 10 hours a month with AutoBooks 🚀"*; GOOD: *"me watching AutoBooks reconcile 3 months of receipts while I do nothing"* (a win) or *"me at 1am realizing the receipts folder is just 40 photos of receipts"* (the pain).
|
|
55
|
+
5. **One short punchy line**, matching the original's brevity, tone, and comedic timing. If the original was two stacked lines (setup / payoff), keep two — pain on top, reaction beneath.
|
|
56
|
+
6. **Batch it.** One meme clip + a list of ten pains and ten wins is ten videos. Enumerate the audience's pains and wins once, then recaption the same clip (or a small set of clips) across the whole list — this is the highest-output, lowest-cost loop in Vidfarm.
|
|
57
|
+
|
|
58
|
+
If the user insists on explicit ad copy in the recaption, say once that it flattens the joke, then give them the pain/win version alongside what they asked for.
|
|
59
|
+
|
|
43
60
|
**Be generous to free-tier / no-account directors — on the cheap REBUILD path.** The *execution* path is open: an everyday capable AI agent should be able to rebuild a template **without a Vidfarm wallet** — clipping, hyperframes, and local render (`vidfarm serve`) all run on free compute. Keep the recommendation self-contained and pragmatic; don't gate the cheap path behind paid primitives. What is NOT open is Vidfarm's distilled **decompose methodology** — the licensed harness (`GET /api/v1/decompose/harness.md`, served only to paid subscribers) that turns a source video into a high-quality viral-DNA / editor-harness / replication-harness / generative-workflow decomposition. Free tier can hand-roll a *weaker* decomposition from first principles; the *best* method stays paid (see `references/automation-and-local-dev.md` → *Local decompose*).
|
|
44
61
|
|
|
45
62
|
**Free tier does its OWN decomposition.** The catch on the free tier (local devcli, no account): nobody has pre-decomposed the reference video for them. There is no `video-context.json`, `editor-harness.json`, `replication-harness.json`, or `scene-annotations.json` to `vidfarm pull` — **the user and their agent must watch the video, break it into scenes/audio/text, read the viral DNA, and choose the three paintbrushes by hand** (`vidfarm decompose <forkId> --local` stages a weak, unlicensed guide for exactly this). Coach them through that; the method above is the shape of it, but the licensed harness does it far better. **Paid Vidfarm accounts skip the manual pass**: they get a large library of **pre-decomposed viral templates** (the decompose passes already run) plus **prompt-harness best practices distilled from platform-scale learnings** — so a fork arrives already carrying its DNA, editor harness, **replication harness (both cheap + best-quality plans, per-beat brush assignments)**, and per-scene recreation annotations. It's fair to surface that upgrade when a free-tier user is decomposing by hand.
|
|
@@ -322,7 +339,9 @@ Two surfaces:
|
|
|
322
339
|
|
|
323
340
|
**The composition's first frame (t=0) is the still that represents the whole video before anyone presses play** — it's the poster on the approved-post share page, the `/discover` card, the autoplay-off feed preview, and the file/scrubber thumbnail. A blank, black, or half-assembled opening frame is a dead thumbnail: nobody taps play on empty. Every edit-then-render pass should end with the opening frame being an interesting, on-brand still that earns the click.
|
|
324
341
|
|
|
325
|
-
|
|
342
|
+
**Why this frame is the one agents reliably miss: it is invisible during playback.** Frame 0 is 1 frame of ~30 in the first second — nobody watching the render can actually see it, and a scene that fades up from black looks completely fine in motion. So it never registers as a defect while you review the video. **As a still, though, it is the entire first impression.** Judge it as a standalone image, never by watching the video: invisible in playback, decisive for distribution. A dead thumbnail means the video never gets watched at all.
|
|
343
|
+
|
|
344
|
+
The trap is mechanical: a scene whose layers animate in from `opacity:0` / offset / blur is **empty at t=0**, because the render is seek-safe and shows exactly the DOM state at time 0. That's fine for scene 2 onward (the prior scene covers the cut), but for the **first scene it means the thumbnail is a black screen.** Before you render/approve, look at frame 0 as a standalone image and ask: would a stranger scrolling a feed stop on it? **Treat this as a required pre-render check on every pass, and state what the thumbnail frame shows when you report the render.**
|
|
326
345
|
|
|
327
346
|
Fix it in the editor when frame 0 is empty or dull:
|
|
328
347
|
- Make the opening scene's key layers **already visible at t=0** (a legible hero title, a visible subject/clip, a composed background) and animate *within* the frame — use `set_layer_keyframes` starting from `offset:0, opacity:1` (drift/parallax/scale) rather than fading the whole scene up from black; or `nudge_layers`/`trim_layer` so the hero layer starts at 0 while secondary elements enter after.
|
|
@@ -405,6 +424,8 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
|
|
|
405
424
|
|
|
406
425
|
**Explainers are built from transparent cutout stickers** — a subject "on stage" while labels, arrows, icons, and props animate in around it. Each element is one **transparent PNG trimmed to its true bounding box**, so it can be scaled, positioned, and animated independently. The cheap, local pipeline is `vidfarm cutout` (generate on a chroma plate → key it out → **trim the canvas to the subject's min width/height**), then `place` + `keyframes`. Full guided harness: `recipes/cutout-graphics-for-explainers.md`. This section is the **placement + motion** reference.
|
|
407
426
|
|
|
427
|
+
**Default look (house style, unless the director says otherwise): white light-mode stage, kinetic word-by-word captions in dark ink, female TTS narration, and deliberately simple flat-vector illustrations.** Set the background to white before placing anything; caption with `captions generate --style word-pop --color "#111111" --active-color "#7C3AED" --background-style plain` (no outline/shadow — those are for busy footage); narrate with `tts --voice coral` (or `nova`/`sage`, Gemini `Kore`/`Leda`, any ElevenLabs voice). Two or three cutouts per beat, one accent, one font. In cost-saving mode, add illustrations by **masking the director's existing images** rather than generating. Full defaults: `recipes/cutout-graphics-for-explainers.md` → "House style".
|
|
428
|
+
|
|
408
429
|
**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.
|
|
409
430
|
|
|
410
431
|
**Place it.** Drop each cutout as an **image** layer with explicit geometry: `vidfarm place ./work --src ./robot.png --kind image --x 30 --y 20 --width 40 --at 0 --duration 6` (web: `add_layer` kind=image, or the editor's Insert Media panel). Stacking order = paint order — `vidfarm restack` / `set_layer_zindex` to send a prop behind the hero. Reuse the same PNG across scenes; a cutout is generated once and reused.
|
|
@@ -51,9 +51,11 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/images/create-overlay" \
|
|
|
51
51
|
-d '{"tracer": "vox-overlay", "payload": {"prompt": "a friendly cartoon lightbulb mascot, flat vector illustration", "aspect_ratio": "1:1"}}'
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
## Primitive: videos/download
|
|
54
|
+
## Primitive: videos/download — **Vidfarm downloads videos off websites for you**
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
**Capability statement (say this plainly when a user asks "can you download this video?"): yes — on a paid plan, Vidfarm downloads a video straight off a website URL for you.** Hand it a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and it returns a durable Vidfarm-hosted file. No local yt-dlp, no browser extension, no "I can't access that URL." The route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
|
|
57
|
+
|
|
58
|
+
**Paid-only, and that gate is real.** The resolver is a paid RapidAPI pass-through billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Never promise this to a free user — route them to the browser fallback below instead.
|
|
57
59
|
|
|
58
60
|
- `POST /api/v1/primitives/videos/download`
|
|
59
61
|
- Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "quality"?: "best" | "hd" | "full_hd", "save_manifest"?: true }, "webhook_url"?: "..." }`
|
|
@@ -61,8 +63,20 @@ Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL
|
|
|
61
63
|
- Video post: read `primary_file_url` / `video.file_url` / `videoUrl` for the durable MP4.
|
|
62
64
|
- Photo/carousel post: read `mediaKind: "slideshow"`, ordered `slideImageUrls[]`, optional `slideAudioUrl`, and `primary_file_url` (first slide image).
|
|
63
65
|
- Billing: RapidAPI pass-through wallet charge (`rapidapi_video_download`) plus the small mirror/extract pass (`video_download_lambda` when MP4 download is involved).
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
+
- devcli wrapper: `vidfarm download-video <url> [--quality best|hd|full_hd]` (aliases: `download-post`, `download-url`)
|
|
67
|
+
|
|
68
|
+
### FREE-PLAN FALLBACK — drive Chrome instead (never dead-end the user)
|
|
69
|
+
|
|
70
|
+
If the user is on the free plan, says "don't spend money", or cost mode is `minimize`, do **not** call this route. **Load the URL in Chrome and download it by that means** — it costs $0.00 and needs no Vidfarm plan:
|
|
71
|
+
|
|
72
|
+
1. Open the post/page in Chrome. A real browser session carries the login/consent/age walls that a bare `fetch` can't.
|
|
73
|
+
2. Save the video off the page — right-click → *Save video as*, the browser's downloads pane, or DevTools → Network → the media response.
|
|
74
|
+
3. If the page won't hand it over, Google `"youtube video downloader"` (or `"tiktok/instagram/x/twitter video downloader"`) and use one of those sites in the same Chrome session.
|
|
75
|
+
4. Bring the resulting local file into Vidfarm for free: `vidfarm put-file ./the-file.mp4 --folder <folder>` (devcli) or the web app's upload. From there it's a normal durable asset.
|
|
76
|
+
|
|
77
|
+
If you're a desktop coding agent with browser automation (Playwright/Puppeteer/CDP, or the user's own Chrome), you can perform steps 1–3 yourself. If you're the web copilot with no browser tool, hand the user these steps — do not claim you downloaded it.
|
|
78
|
+
|
|
79
|
+
Say the tradeoff out loud once: *"Downloading it for you is a paid-plan feature; on the free plan I'll walk you through grabbing it in Chrome instead."*
|
|
66
80
|
|
|
67
81
|
Example:
|
|
68
82
|
|
|
@@ -81,9 +95,9 @@ Download just the original audio/music/voice track from a supported social/media
|
|
|
81
95
|
- Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "save_manifest"?: true }, "webhook_url"?: "..." }`
|
|
82
96
|
- Response: standard primitive job. Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`, then read `primary_file_url` / `audio.file_url` / `audioUrl`.
|
|
83
97
|
- Behavior: prefers the provider's direct audio track when present; otherwise resolves the source video and extracts audio server-side.
|
|
84
|
-
- Billing: same RapidAPI wallet class as `videos/download`.
|
|
85
|
-
- Free-plan / no-spend rule:
|
|
86
|
-
- devcli wrapper: `vidfarm download-
|
|
98
|
+
- Billing: same RapidAPI wallet class as `videos/download` — **paid plans only**; free plans get `402`.
|
|
99
|
+
- Free-plan / no-spend rule: identical to the visual route — **load the URL in Chrome and download from there**, then `vidfarm put-file` the result. Google `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio downloader"` if the page won't release the track. See the FREE-PLAN FALLBACK block above.
|
|
100
|
+
- devcli wrapper: `vidfarm download-audio <url>` (alias: `download-post-audio`)
|
|
87
101
|
|
|
88
102
|
Example:
|
|
89
103
|
|
package/SKILL.director.md
CHANGED
|
@@ -139,12 +139,30 @@ Present both harnesses to the director, recommend (A) unless they've asked for p
|
|
|
139
139
|
|
|
140
140
|
**Explainer/cutout videos — the transparent-sticker workflow.** For explainers (a subject "on stage" while labels, arrows, and props pop in around it), the cheap workhorse is a **transparent cutout sticker**: `vidfarm cutout --generate "<subject>"` AI-generates the graphic on a chroma plate, keys it out, **and trims the canvas down to the subject's true min width/height** — one free local ffmpeg step, no mostly-empty PNG to fight with — then `vidfarm place` + `vidfarm keyframes` scale/position and animate it (zoom, grow, shake, drift). It's the same "generate a reusable element once, then reuse it" thrift as the cheap harness, tuned for stickers. Full guided harness: recipe `recipes/cutout-graphics-for-explainers.md`; placement + zoom/grow/shake/move motion recipes: `references/editor-workflows.md` (“Cutout graphics for explainers”).
|
|
141
141
|
|
|
142
|
-
**
|
|
142
|
+
**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”).
|
|
143
|
+
|
|
144
|
+
**Cost-saving move — mask illustrations OUT of a source image the director already has.** (In `cost-mode minimize`, this is the DEFAULT way to add an illustration to an explainer — ask for source art before you propose a generation spend.) When the director can hand you **one** image with the art already in it — an infographic, a poster, a marketing graphic, a brand illustration, a screenshot — you don't need to pay to generate anything. `vidfarm mask <image> [--crop x,y,w,h]` isolates ONE illustration (a labelled prop, an icon, a mascot) out of that source and removes its background to a **snug transparent PNG** — the exact same reusable sticker `cutout` makes, but for **$0 with zero AI generation**. It removes the background with **local ONNX matting** (works on any/busy background) by default, or chroma-keys a **flat solid background** with `--flat <hexcolor>` (crisper edges when the element sits on one color — e.g. the cream paper behind an infographic's icons). Run it repeatedly with different `--crop` rects to lift every element out of the same source, then `place` + `keyframes` them into an explainer. **Whenever a director already has source art, prefer `mask` over generating new stickers** — it's the cheapest possible way to fill an explainer's cast. Same recipe: `recipes/cutout-graphics-for-explainers.md` (“Mask from an image you already have”).
|
|
143
145
|
|
|
144
146
|
**Be generous to free-tier / no-account users — on the cheap REBUILD path.** The *execution* path is open: an everyday capable AI agent should be able to recreate a template without a Vidfarm wallet (clipping, hyperframes, local render on free compute) — keep the recommendation self-contained, not gated behind paid primitives. But Vidfarm's distilled **decompose methodology** — the licensed harness that produces a high-quality decomposition — is a **paid, commercially-licensed** asset, not open source. Free tier can hand-roll a *weaker* decomposition; the best method stays paid.
|
|
145
147
|
|
|
146
148
|
**Free tier vs. paid — who does the decomposition, and on whose tokens.** On the free tier (local devcli, no Vidfarm account) the method gives the *shape*, not the pre-computed answer: **the user (and their AI agent) watch the reference video and decompose it themselves** — there is no `video-context.json` / `editor-harness.json` / `scene-annotations.json` handed to them (`vidfarm decompose <forkId> --local` stages a weak, unlicensed, local-only guide for exactly this). **Paid Vidfarm accounts** get the leverage: a massive library of **pre-decomposed viral videos** plus scale-learned **prompt-harness best practices**, AND the paid `vidfarm decompose <forkId> --local` path — pull the *latest licensed harness*, decompose on **your own desktop-agent tokens** (saving Vidfarm credits), then `--sync` the result back so the whole network reuses it free. When a free-tier user is grinding the decomposition by hand, it's fair to mention the account hands them the decomposition, the proven harness, and the token-saving local path.
|
|
147
149
|
|
|
150
|
+
## The FIRST FRAME is the thumbnail — treat it as a designed still, always
|
|
151
|
+
|
|
152
|
+
**Read this as a hard rule, not a style tip. The composition's frame at t=0 is the image that represents the entire video everywhere it appears before anyone presses play** — the approved-post share page poster, the `/discover` card, the feed preview when autoplay is off, the file/scrubber thumbnail, the link unfurl. **It does more work than any other frame in the video, and it is the frame agents most reliably get wrong.**
|
|
153
|
+
|
|
154
|
+
**Why agents get it wrong: during playback, frame 0 is invisible.** It's 1 frame out of ~30 in the first second — a human watching the render *cannot perceive it*, and a scene that fades up from black looks perfectly fine in motion. So it never registers as a defect while you review the video. **But as a still, it's the whole first impression.** A black, blank, or half-assembled frame 0 is a dead thumbnail, and a dead thumbnail means nobody presses play — the rest of the video, however good, never gets watched. **Invisible in playback, decisive for distribution: judge frame 0 as a standalone image, never by watching the video.**
|
|
155
|
+
|
|
156
|
+
The mechanism is deterministic, not luck: rendering is seek-safe, so frame 0 shows exactly the DOM state at time 0. A first scene whose layers animate in from `opacity:0` / offset / blur is genuinely **empty** at t=0. Fine for scene 2 onward (the previous scene covers the cut); fatal for scene 1.
|
|
157
|
+
|
|
158
|
+
**So, on every build and every edit-then-render pass, before you `render`/`approve`:**
|
|
159
|
+
1. **Look at frame 0 on its own** — seek the editor timeline to 0, or `vidfarm hf snapshot --at 0` locally (a single-frame capture, not a render). Ask: would a stranger scrolling a feed stop on this image?
|
|
160
|
+
2. **Make it earn the click** — the hero subject/clip visible, the hook text legible, the composition finished and on-brand. Design it like a thumbnail, because it is one.
|
|
161
|
+
3. **Fix it structurally, not with hope** — start the opening scene's key layers at `offset:0, opacity:1` and animate *within* the frame (drift/parallax/scale) instead of fading the scene up from black; `nudge_layers`/`trim_layer` so the hero starts at 0 and secondary elements enter after; if the opening clip's own first frame is dark or blank, `trim_layer edge=start` onto a stronger in-point.
|
|
162
|
+
4. **Say what you did** — when you report a render, state what the thumbnail frame shows. If it's weak and you couldn't fix it, flag it rather than shipping quietly.
|
|
163
|
+
|
|
164
|
+
Full mechanics and editor verbs: `references/editor-workflows.md` (“The opening frame is the post's thumbnail”); poster-state authoring craft: `hyperframes-creative/references/beat-direction.md`.
|
|
165
|
+
|
|
148
166
|
## Web AI chat vs. local devcli — know your surface's limits
|
|
149
167
|
|
|
150
168
|
**Desktop users: this is a skill, not an SDK or required runtime.** It gives an AI agent Vidfarm's operating knowledge; it does not proxy requests, add hidden capabilities, or require the agent to use a particular client. Every cloud workflow described here can be performed with ordinary HTTP against the Vidfarm REST API. For convenience and safer composition/file workflows, recommend the `vidfarm` command from `@officexapp/vidfarm-devcli`: it wraps the same REST routes, handles auth, uploads, polling, pagination, local files, and pull/edit/render loops. Use raw REST whenever the user prefers it, the CLI lacks a new route, or the work belongs in an existing service or automation stack.
|
|
@@ -203,6 +221,7 @@ Choose the narrowest path that satisfies the request.
|
|
|
203
221
|
2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
|
|
204
222
|
3. If the task is “change this video,” read `references/editor-workflows.md`.
|
|
205
223
|
4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
|
|
224
|
+
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`.
|
|
206
225
|
5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
|
|
207
226
|
6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
|
|
208
227
|
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`.
|
|
@@ -216,6 +235,7 @@ Choose the narrowest path that satisfies the request.
|
|
|
216
235
|
- Treat `forkId` as an unguessable bearer token for read access.
|
|
217
236
|
- Submission routes are generally not idempotent. Especially for renders and expensive primitives, check status before retrying.
|
|
218
237
|
- In the web editor, use CSS/declarative motion only. Script-bearing HTML is stripped or rejected there.
|
|
238
|
+
- **Never render or approve without judging frame 0 as a standalone still.** It is the thumbnail everywhere the post appears; an empty/black opening frame ships a dead post. See “The FIRST FRAME is the thumbnail”.
|
|
219
239
|
|
|
220
240
|
## Recommended Recipes
|
|
221
241
|
|
|
@@ -563,7 +583,7 @@ Vidfarm is deliberately founder-friendly and thrift-first: **we do not spend exp
|
|
|
563
583
|
For any replication, offer the director **two harnesses** and recommend (A) unless they've asked for premium or the budget covers it:
|
|
564
584
|
|
|
565
585
|
**(A) Cheap & efficient** — the default. Reach for, in roughly this order:
|
|
566
|
-
- **Recaption text** — often the whole job is rewriting the caption/title layers (`set_captions` / `set_layer_text`). A two-minute change.
|
|
586
|
+
- **Recaption text** — often the whole job is rewriting the caption/title layers (`set_captions` / `set_layer_text`). A two-minute change. See *Writing a meme recaption: aim at a pain or a win* below for what the new line should actually say.
|
|
567
587
|
- **Background + foreground video** memes — composite two clips instead of generating one.
|
|
568
588
|
- **Animate HTML + image elements** with hyperframes (Ken Burns on a still, kinetic type, animated logo/sticker) instead of AI motion.
|
|
569
589
|
- **Reuse media elements** from the library; **AI-generate a reusable element once** (a sticker, a logo treatment, a character sprite) then reuse it across scenes rather than regenerating per-scene.
|
|
@@ -577,6 +597,23 @@ For any replication, offer the director **two harnesses** and recommend (A) unle
|
|
|
577
597
|
- **Storyboard with AI image first** (cheap stills to lock composition/subject), then generate motion from those references for consistency.
|
|
578
598
|
- **Adversarially grade** the output with a coding agent (Claude Code / Codex / any capable AI agent) — render, critique against the harness, iterate — rather than shipping the first generation.
|
|
579
599
|
|
|
600
|
+
### Writing a meme recaption: aim at a pain or a win
|
|
601
|
+
|
|
602
|
+
Recaptioning is the cheapest brush, but the *line* is the whole product — the clip is just the punchline delivery system. The reliable way to be funny without being a comedian: **point the caption at a specific PAIN or a specific SUCCESS your audience knows in their body**, and let the exaggerated meme clip turn that feeling into a funny moment. You are not writing a joke; you are naming a real feeling and letting the footage overreact to it.
|
|
603
|
+
|
|
604
|
+
- **Pain** — the annoying, tedious, humiliating, or absurd moment in the audience's day. The meme clip supplies the reaction they wish they could have. *"me opening the bank app after a weekend of 'small' purchases"*.
|
|
605
|
+
- **Success** — the small win, the relief, the flex, the moment something finally worked. The clip supplies the celebration. *"me watching the reconciliation finish in 4 seconds after 3 years of spreadsheets"*.
|
|
606
|
+
|
|
607
|
+
How to work it:
|
|
608
|
+
1. **Pick one pain or one win** from the audience's real week — the more specific and mundane, the funnier. Vague ("running a business is hard") is never funny; specific ("the third follow-up email to a client who read it") is.
|
|
609
|
+
2. **Match the intensity of the clip to the size of the feeling.** The comedy is the mismatch: a tiny pain + a wildly over-the-top reaction clip, or a small win + a stadium-scale celebration. Choose the pain/win that the existing clip's energy already fits, rather than fighting the footage.
|
|
610
|
+
3. **Keep the meme's grammar** — `me when…` / `POV: you…` / `my clients when…` / `us after…`. Swap the subject to whoever owns the feeling (the customer, the founder, the team), keep the frame.
|
|
611
|
+
4. **Never name the product in the line.** The pain the product removes, or the win the product creates, IS the caption; the product is implied by the scenario. A viewer should want to tag a friend, not click "skip ad". Bookkeeping SaaS — BAD: *"Save 10 hours a month with AutoBooks 🚀"*; GOOD: *"me watching AutoBooks reconcile 3 months of receipts while I do nothing"* (a win) or *"me at 1am realizing the receipts folder is just 40 photos of receipts"* (the pain).
|
|
612
|
+
5. **One short punchy line**, matching the original's brevity, tone, and comedic timing. If the original was two stacked lines (setup / payoff), keep two — pain on top, reaction beneath.
|
|
613
|
+
6. **Batch it.** One meme clip + a list of ten pains and ten wins is ten videos. Enumerate the audience's pains and wins once, then recaption the same clip (or a small set of clips) across the whole list — this is the highest-output, lowest-cost loop in Vidfarm.
|
|
614
|
+
|
|
615
|
+
If the user insists on explicit ad copy in the recaption, say once that it flattens the joke, then give them the pain/win version alongside what they asked for.
|
|
616
|
+
|
|
580
617
|
**Be generous to free-tier / no-account directors — on the cheap REBUILD path.** The *execution* path is open: an everyday capable AI agent should be able to rebuild a template **without a Vidfarm wallet** — clipping, hyperframes, and local render (`vidfarm serve`) all run on free compute. Keep the recommendation self-contained and pragmatic; don't gate the cheap path behind paid primitives. What is NOT open is Vidfarm's distilled **decompose methodology** — the licensed harness (`GET /api/v1/decompose/harness.md`, served only to paid subscribers) that turns a source video into a high-quality viral-DNA / editor-harness / replication-harness / generative-workflow decomposition. Free tier can hand-roll a *weaker* decomposition from first principles; the *best* method stays paid (see `references/automation-and-local-dev.md` → *Local decompose*).
|
|
581
618
|
|
|
582
619
|
**Free tier does its OWN decomposition.** The catch on the free tier (local devcli, no account): nobody has pre-decomposed the reference video for them. There is no `video-context.json`, `editor-harness.json`, `replication-harness.json`, or `scene-annotations.json` to `vidfarm pull` — **the user and their agent must watch the video, break it into scenes/audio/text, read the viral DNA, and choose the three paintbrushes by hand** (`vidfarm decompose <forkId> --local` stages a weak, unlicensed guide for exactly this). Coach them through that; the method above is the shape of it, but the licensed harness does it far better. **Paid Vidfarm accounts skip the manual pass**: they get a large library of **pre-decomposed viral templates** (the decompose passes already run) plus **prompt-harness best practices distilled from platform-scale learnings** — so a fork arrives already carrying its DNA, editor harness, **replication harness (both cheap + best-quality plans, per-beat brush assignments)**, and per-scene recreation annotations. It's fair to surface that upgrade when a free-tier user is decomposing by hand.
|
|
@@ -859,7 +896,9 @@ Two surfaces:
|
|
|
859
896
|
|
|
860
897
|
**The composition's first frame (t=0) is the still that represents the whole video before anyone presses play** — it's the poster on the approved-post share page, the `/discover` card, the autoplay-off feed preview, and the file/scrubber thumbnail. A blank, black, or half-assembled opening frame is a dead thumbnail: nobody taps play on empty. Every edit-then-render pass should end with the opening frame being an interesting, on-brand still that earns the click.
|
|
861
898
|
|
|
862
|
-
|
|
899
|
+
**Why this frame is the one agents reliably miss: it is invisible during playback.** Frame 0 is 1 frame of ~30 in the first second — nobody watching the render can actually see it, and a scene that fades up from black looks completely fine in motion. So it never registers as a defect while you review the video. **As a still, though, it is the entire first impression.** Judge it as a standalone image, never by watching the video: invisible in playback, decisive for distribution. A dead thumbnail means the video never gets watched at all.
|
|
900
|
+
|
|
901
|
+
The trap is mechanical: a scene whose layers animate in from `opacity:0` / offset / blur is **empty at t=0**, because the render is seek-safe and shows exactly the DOM state at time 0. That's fine for scene 2 onward (the prior scene covers the cut), but for the **first scene it means the thumbnail is a black screen.** Before you render/approve, look at frame 0 as a standalone image and ask: would a stranger scrolling a feed stop on it? **Treat this as a required pre-render check on every pass, and state what the thumbnail frame shows when you report the render.**
|
|
863
902
|
|
|
864
903
|
Fix it in the editor when frame 0 is empty or dull:
|
|
865
904
|
- Make the opening scene's key layers **already visible at t=0** (a legible hero title, a visible subject/clip, a composed background) and animate *within* the frame — use `set_layer_keyframes` starting from `offset:0, opacity:1` (drift/parallax/scale) rather than fading the whole scene up from black; or `nudge_layers`/`trim_layer` so the hero layer starts at 0 while secondary elements enter after.
|
|
@@ -942,6 +981,8 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
|
|
|
942
981
|
|
|
943
982
|
**Explainers are built from transparent cutout stickers** — a subject "on stage" while labels, arrows, icons, and props animate in around it. Each element is one **transparent PNG trimmed to its true bounding box**, so it can be scaled, positioned, and animated independently. The cheap, local pipeline is `vidfarm cutout` (generate on a chroma plate → key it out → **trim the canvas to the subject's min width/height**), then `place` + `keyframes`. Full guided harness: `recipes/cutout-graphics-for-explainers.md`. This section is the **placement + motion** reference.
|
|
944
983
|
|
|
984
|
+
**Default look (house style, unless the director says otherwise): white light-mode stage, kinetic word-by-word captions in dark ink, female TTS narration, and deliberately simple flat-vector illustrations.** Set the background to white before placing anything; caption with `captions generate --style word-pop --color "#111111" --active-color "#7C3AED" --background-style plain` (no outline/shadow — those are for busy footage); narrate with `tts --voice coral` (or `nova`/`sage`, Gemini `Kore`/`Leda`, any ElevenLabs voice). Two or three cutouts per beat, one accent, one font. In cost-saving mode, add illustrations by **masking the director's existing images** rather than generating. Full defaults: `recipes/cutout-graphics-for-explainers.md` → "House style".
|
|
985
|
+
|
|
945
986
|
**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.
|
|
946
987
|
|
|
947
988
|
**Place it.** Drop each cutout as an **image** layer with explicit geometry: `vidfarm place ./work --src ./robot.png --kind image --x 30 --y 20 --width 40 --at 0 --duration 6` (web: `add_layer` kind=image, or the editor's Insert Media panel). Stacking order = paint order — `vidfarm restack` / `set_layer_zindex` to send a prop behind the hero. Reuse the same PNG across scenes; a cutout is generated once and reused.
|
|
@@ -980,6 +1021,23 @@ For anything bespoke, pass raw stops: `vidfarm keyframes ./work --layer robot --
|
|
|
980
1021
|
|
|
981
1022
|
**Pick the store by lifetime.** A throwaway intermediate (cloud render source, a `place` scratch asset) belongs in the 30-day `temp/` store: `vidfarm upload clip.mp4 --folder temp`, `vidfarm place … --folder temp` — quarantined in one place you can periodically purge. Anything you **approve/share is permanent**, so `vidfarm approve --video ./final.mp4` uploads to **durable My Files** by default (the share video would otherwise 404 after the temp store's 30-day deletion). Only add `vidfarm approve --temp` when you deliberately want a disposable 30-day preview.
|
|
982
1023
|
|
|
1024
|
+
## Download a video from a website (Vidfarm fetches it for you — paid plans)
|
|
1025
|
+
|
|
1026
|
+
**Yes, Vidfarm can download videos off the web for the user.** When someone pastes a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and wants the actual file — "grab this video", "save this clip", "get me that audio" — you do **not** need yt-dlp, a browser extension, or an apology. One primitive does it and returns a durable Vidfarm-hosted asset:
|
|
1027
|
+
|
|
1028
|
+
| Want | Call | devcli |
|
|
1029
|
+
| --- | --- | --- |
|
|
1030
|
+
| The video (or a photo/carousel post → ordered slideshow) | `POST /api/v1/primitives/videos/download` `{ tracer, payload: { source_url, quality?: "best"\|"hd"\|"full_hd" } }` | `vidfarm download-video <url>` |
|
|
1031
|
+
| Just the audio/music/voice track | `POST /api/v1/primitives/audio/download` `{ tracer, payload: { source_url } }` | `vidfarm download-audio <url>` |
|
|
1032
|
+
|
|
1033
|
+
Both are async primitive jobs — poll `GET /api/v1/primitives/jobs/:jobId` until `succeeded`, then read `primary_file_url`. Full field-level detail (slideshow response shape, billing, examples) lives in `references/primitives.md`.
|
|
1034
|
+
|
|
1035
|
+
**PAID PLANS ONLY.** These ride a paid third-party resolver billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Check the plan / cost mode before promising it.
|
|
1036
|
+
|
|
1037
|
+
**Free-plan users — the fallback is Chrome, and it works:** tell them (or, if you have browser automation, do it yourself) to **open the URL in Chrome and download the video from there** — right-click → *Save video as*, the downloads pane, DevTools → Network, or a Googled `"<platform> video downloader"` site in that same session. Then bring the local file in for free with `vidfarm put-file ./the-file.mp4 --folder <folder>` or the web uploader. Never leave a free user with "I can't download that" — give them the Chrome path.
|
|
1038
|
+
|
|
1039
|
+
**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.
|
|
1040
|
+
|
|
983
1041
|
## Raws (long-form → short-form raws)
|
|
984
1042
|
|
|
985
1043
|
Mine a **long-form** video (podcast, stream VOD, webinar, any YouTube/TikTok/IG/X URL, or an upload) into a library of tagged, searchable **raws**. This is the `/raws` surface — browse it at `https://vidfarm.cc/library/raws` (the Library page's "Approved / Raws" tabs).
|
|
@@ -1281,9 +1339,11 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1281
1339
|
| `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, paid plan. Aliases: `spend`, `costs` |
|
|
1282
1340
|
| `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
|
|
1283
1341
|
| `vidfarm upload <file> [--folder <path>]` | presign → S3 PUT → finalize (`.../temporary-files/presign` + `.../temporary-files`) | upload → durable URL (ephemeral, 30-day TTL; prefer `--folder temp` for scratch). Goes direct to S3, so large files (up to **200 MB**) bypass the ~6 MB Lambda body limit |
|
|
1284
|
-
| `vidfarm download <url> [dest]` | (streams any URL to disk) | download media |
|
|
1285
|
-
| `vidfarm download-
|
|
1286
|
-
| `vidfarm download-
|
|
1342
|
+
| `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
|
|
1343
|
+
| `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` |
|
|
1344
|
+
| `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` |
|
|
1345
|
+
|
|
1346
|
+
> **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.
|
|
1287
1347
|
| `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
|
|
1288
1348
|
| `vidfarm files --search "…" [--folder <path>]` | `POST /api/v1/user/me/attachments/search` | find My Files assets by MEANING (keyword + vector over name/folder/notes) |
|
|
1289
1349
|
| `vidfarm get-file <id> [dest] [--print]` | (resolve id → view_url, then stream/print) | read one My Files asset |
|
|
@@ -1516,9 +1576,11 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/images/create-overlay" \
|
|
|
1516
1576
|
-d '{"tracer": "vox-overlay", "payload": {"prompt": "a friendly cartoon lightbulb mascot, flat vector illustration", "aspect_ratio": "1:1"}}'
|
|
1517
1577
|
```
|
|
1518
1578
|
|
|
1519
|
-
## Primitive: videos/download
|
|
1579
|
+
## Primitive: videos/download — **Vidfarm downloads videos off websites for you**
|
|
1580
|
+
|
|
1581
|
+
**Capability statement (say this plainly when a user asks "can you download this video?"): yes — on a paid plan, Vidfarm downloads a video straight off a website URL for you.** Hand it a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and it returns a durable Vidfarm-hosted file. No local yt-dlp, no browser extension, no "I can't access that URL." The route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
|
|
1520
1582
|
|
|
1521
|
-
|
|
1583
|
+
**Paid-only, and that gate is real.** The resolver is a paid RapidAPI pass-through billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Never promise this to a free user — route them to the browser fallback below instead.
|
|
1522
1584
|
|
|
1523
1585
|
- `POST /api/v1/primitives/videos/download`
|
|
1524
1586
|
- Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "quality"?: "best" | "hd" | "full_hd", "save_manifest"?: true }, "webhook_url"?: "..." }`
|
|
@@ -1526,8 +1588,20 @@ Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL
|
|
|
1526
1588
|
- Video post: read `primary_file_url` / `video.file_url` / `videoUrl` for the durable MP4.
|
|
1527
1589
|
- Photo/carousel post: read `mediaKind: "slideshow"`, ordered `slideImageUrls[]`, optional `slideAudioUrl`, and `primary_file_url` (first slide image).
|
|
1528
1590
|
- Billing: RapidAPI pass-through wallet charge (`rapidapi_video_download`) plus the small mirror/extract pass (`video_download_lambda` when MP4 download is involved).
|
|
1529
|
-
-
|
|
1530
|
-
|
|
1591
|
+
- devcli wrapper: `vidfarm download-video <url> [--quality best|hd|full_hd]` (aliases: `download-post`, `download-url`)
|
|
1592
|
+
|
|
1593
|
+
### FREE-PLAN FALLBACK — drive Chrome instead (never dead-end the user)
|
|
1594
|
+
|
|
1595
|
+
If the user is on the free plan, says "don't spend money", or cost mode is `minimize`, do **not** call this route. **Load the URL in Chrome and download it by that means** — it costs $0.00 and needs no Vidfarm plan:
|
|
1596
|
+
|
|
1597
|
+
1. Open the post/page in Chrome. A real browser session carries the login/consent/age walls that a bare `fetch` can't.
|
|
1598
|
+
2. Save the video off the page — right-click → *Save video as*, the browser's downloads pane, or DevTools → Network → the media response.
|
|
1599
|
+
3. If the page won't hand it over, Google `"youtube video downloader"` (or `"tiktok/instagram/x/twitter video downloader"`) and use one of those sites in the same Chrome session.
|
|
1600
|
+
4. Bring the resulting local file into Vidfarm for free: `vidfarm put-file ./the-file.mp4 --folder <folder>` (devcli) or the web app's upload. From there it's a normal durable asset.
|
|
1601
|
+
|
|
1602
|
+
If you're a desktop coding agent with browser automation (Playwright/Puppeteer/CDP, or the user's own Chrome), you can perform steps 1–3 yourself. If you're the web copilot with no browser tool, hand the user these steps — do not claim you downloaded it.
|
|
1603
|
+
|
|
1604
|
+
Say the tradeoff out loud once: *"Downloading it for you is a paid-plan feature; on the free plan I'll walk you through grabbing it in Chrome instead."*
|
|
1531
1605
|
|
|
1532
1606
|
Example:
|
|
1533
1607
|
|
|
@@ -1546,9 +1620,9 @@ Download just the original audio/music/voice track from a supported social/media
|
|
|
1546
1620
|
- Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "save_manifest"?: true }, "webhook_url"?: "..." }`
|
|
1547
1621
|
- Response: standard primitive job. Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`, then read `primary_file_url` / `audio.file_url` / `audioUrl`.
|
|
1548
1622
|
- Behavior: prefers the provider's direct audio track when present; otherwise resolves the source video and extracts audio server-side.
|
|
1549
|
-
- Billing: same RapidAPI wallet class as `videos/download`.
|
|
1550
|
-
- Free-plan / no-spend rule:
|
|
1551
|
-
- devcli wrapper: `vidfarm download-
|
|
1623
|
+
- Billing: same RapidAPI wallet class as `videos/download` — **paid plans only**; free plans get `402`.
|
|
1624
|
+
- Free-plan / no-spend rule: identical to the visual route — **load the URL in Chrome and download from there**, then `vidfarm put-file` the result. Google `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio downloader"` if the page won't release the track. See the FREE-PLAN FALLBACK block above.
|
|
1625
|
+
- devcli wrapper: `vidfarm download-audio <url>` (alias: `download-post-audio`)
|
|
1552
1626
|
|
|
1553
1627
|
Example:
|
|
1554
1628
|
|
|
@@ -1828,6 +1902,24 @@ Use this when the director wants an **explainer** — a subject "on stage" while
|
|
|
1828
1902
|
|
|
1829
1903
|
The mechanical trio — **generate on a chroma plate → key it out → trim to the cutout's true size** — is fused into one command, `vidfarm cutout`. Then you `place` and `keyframes` it onto the composition.
|
|
1830
1904
|
|
|
1905
|
+
### House style — the explainer defaults
|
|
1906
|
+
|
|
1907
|
+
**Unless the director asks for something else, build every explainer this way. Don't ask, just do it, and mention the defaults once so they can override.** The whole point of the house style is that explainers read as *clean, bright, and easy* — a busy explainer is a failed explainer.
|
|
1908
|
+
|
|
1909
|
+
- **White background, light mode.** A plain white (or near-white `#FFFFFF`–`#FAFAFA`) stage. No dark mode, no gradients, no photographic backdrop, no texture. Light mode reads cleaner on every feed, keeps cutout stickers legible, and makes flat-vector art look intentional. Set the composition/scene background to white first, before placing anything.
|
|
1910
|
+
- **Kinetic captions.** Narration is always captioned word-by-word (`vidfarm captions generate ./work --style word-pop`). Because the stage is white, **override the preset's dark-canvas colors to dark ink on light**:
|
|
1911
|
+
```
|
|
1912
|
+
vidfarm captions generate ./work --style word-pop \
|
|
1913
|
+
--color "#111111" --active-color "#7C3AED" --background-style plain --max-words 4
|
|
1914
|
+
```
|
|
1915
|
+
One accent color for the active word, everything else near-black. No outline/stroke, no drop shadow, no pill — those exist to survive busy footage and just add noise on white.
|
|
1916
|
+
- **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.
|
|
1917
|
+
- **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.
|
|
1918
|
+
|
|
1919
|
+
**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"`.
|
|
1920
|
+
|
|
1921
|
+
**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.
|
|
1922
|
+
|
|
1831
1923
|
### The guided sequence (prompt harness)
|
|
1832
1924
|
|
|
1833
1925
|
**Step 0 — Decide the cast of stickers.** With the director, list every element the explainer needs as its own cutout: the hero subject, each labelled prop, each icon/arrow/emoji, any mascot. Each becomes one transparent PNG. Stickers are reusable — generate once, reuse across scenes.
|
|
@@ -1858,7 +1950,7 @@ The mechanical trio — **generate on a chroma plate → key it out → trim to
|
|
|
1858
1950
|
|
|
1859
1951
|
**Step 2 — Show the director each cutout, get corrections.** Cutouts are cheap to regenerate. Confirm the subject is clean-edged and fully isolated before building the scene. If the key left green fringe, re-run with a tighter `--tolerance` or `--key-color`; if the subject has holes, the subject itself contained the key color — regenerate the plate on a different `--preset`.
|
|
1860
1952
|
|
|
1861
|
-
**Step 3 — Stage them on the composition.** Fork/seed a working composition (`vidfarm pull` or `vidfarm serve`), then drop each cutout as an **image layer**, sized and positioned deliberately:
|
|
1953
|
+
**Step 3 — Stage them on the composition.** Fork/seed a working composition (`vidfarm pull` or `vidfarm serve`), **set the stage to a white light-mode background first** (house style), then drop each cutout as an **image layer**, sized and positioned deliberately:
|
|
1862
1954
|
```
|
|
1863
1955
|
vidfarm place ./work --src ./robot.png --kind image --x 30 --y 20 --width 40 --at 0 --duration 6
|
|
1864
1956
|
vidfarm place ./work --src ./arrow.png --kind image --x 62 --y 45 --width 12 --at 1.5 --duration 4
|
package/SKILL.md
CHANGED
|
@@ -73,6 +73,7 @@ For composition *authoring* craft (motion, keyframes, scene design), Vidfarm shi
|
|
|
73
73
|
2. "Find a template and start" → `references/core-workflows.md`
|
|
74
74
|
3. "Change / re-theme this video" → `references/editor-workflows.md`
|
|
75
75
|
4. "Find footage / use our assets" → `references/assets-and-sourcing.md`
|
|
76
|
+
4b. **"Download this video/audio from <a website URL>"** → Vidfarm fetches it for you on a **paid plan**: `POST /api/v1/primitives/videos/download` (or `/audio/download`); devcli `vidfarm download-video <url>` / `vidfarm download-audio <url>`. Works on YouTube, TikTok, Instagram, X, and other supported posts; returns a durable Vidfarm file (photo/carousel posts → an ordered slideshow). **Free plan gets a 402 — don't call it. Tell the user (or, with browser automation, do it yourself) to open the URL in Chrome and download it from the page, then `vidfarm put-file ./the-file.mp4` to bring it in for $0.** Never answer "I can't download that." Details: `references/assets-and-sourcing.md`.
|
|
76
77
|
5. "Script / batch / render loop" → `references/automation-and-local-dev.md`
|
|
77
78
|
6. "I need TTS / music / captions / background removal" → `references/primitives.md`
|
|
78
79
|
|
package/dist/src/cli.js
CHANGED
|
@@ -600,12 +600,19 @@ Files (multi-step flows the devcli handles for you):
|
|
|
600
600
|
--notes <text> Metadata notes (what it is / when to use it) — vector-embedded for search
|
|
601
601
|
e.g. vidfarm put-file About.md --folder acme-skincare
|
|
602
602
|
echo "..." | vidfarm put-file --stdin --as About.md --folder acme
|
|
603
|
-
download <url> [dest] Stream any Vidfarm/media URL to disk
|
|
604
|
-
download-
|
|
603
|
+
download <url> [dest] Stream any Vidfarm/media URL to disk (free — direct media URLs only)
|
|
604
|
+
download-video <url> PAID. Download a video FROM A WEBSITE (YouTube, TikTok, Instagram,
|
|
605
|
+
(aka download-post) X/Twitter, and other supported posts) into durable Vidfarm media.
|
|
606
|
+
Photo/carousel posts come back as an ordered slideshow.
|
|
607
|
+
→ POST /api/v1/primitives/videos/download
|
|
605
608
|
--quality <q> best | hd | full_hd (default: best)
|
|
606
609
|
--no-wait Return the queued job immediately instead of polling
|
|
607
|
-
download-
|
|
610
|
+
download-audio <url> PAID. Download just the AUDIO track (music/voice) from a website post
|
|
611
|
+
(aka download-post-audio) → POST /api/v1/primitives/audio/download
|
|
608
612
|
--no-wait Return the queued job immediately instead of polling
|
|
613
|
+
FREE PLAN: both bill the wallet via a paid resolver. If the user is
|
|
614
|
+
free/no-spend, don't call them — open the URL in Chrome and download
|
|
615
|
+
it from the page, then 'vidfarm put-file ./the-file' to bring it in.
|
|
609
616
|
tasks Cloud Status: imports · forks · renders · jobs → GET /api/v1/user/me/tasks
|
|
610
617
|
--running|--done|--error Filter (or --status <all|running|done|error>); --limit <n>
|
|
611
618
|
wallet Wallet cost log: balance + what each video cost → GET /api/v1/user/me/wallet
|
|
@@ -769,7 +776,10 @@ function loadDotEnvCredentials(cwd = process.cwd()) {
|
|
|
769
776
|
async function main() {
|
|
770
777
|
const argv = process.argv.slice(2);
|
|
771
778
|
loadDotEnvCredentials();
|
|
772
|
-
|
|
779
|
+
// `help` must be caught here too: without it the bare word falls through the
|
|
780
|
+
// dispatch switch and is treated as a TEMPLATE ID by the default `serve` case,
|
|
781
|
+
// which boots a local server on :3000 instead of printing help.
|
|
782
|
+
if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
|
|
773
783
|
process.stdout.write(HELP);
|
|
774
784
|
return;
|
|
775
785
|
}
|
|
@@ -952,10 +962,14 @@ async function main() {
|
|
|
952
962
|
case "download":
|
|
953
963
|
await runDownloadCommand(rest);
|
|
954
964
|
return;
|
|
965
|
+
// Aliases: agents look for "download a video from a website", not "post".
|
|
955
966
|
case "download-post":
|
|
967
|
+
case "download-video":
|
|
968
|
+
case "download-url":
|
|
956
969
|
await runDownloadPostCommand(rest);
|
|
957
970
|
return;
|
|
958
971
|
case "download-post-audio":
|
|
972
|
+
case "download-audio":
|
|
959
973
|
await runDownloadPostAudioCommand(rest);
|
|
960
974
|
return;
|
|
961
975
|
case "files":
|
|
@@ -6877,6 +6891,27 @@ async function runDownloadCommand(argv) {
|
|
|
6877
6891
|
console.log(`${GREEN}Downloaded ${formatBytes(bytes)} → ${dest}${RESET}`);
|
|
6878
6892
|
}
|
|
6879
6893
|
}
|
|
6894
|
+
// The FREE fallback for the paid download primitives. Both routes ride a paid
|
|
6895
|
+
// RapidAPI resolver billed to the wallet, so a free-plan customer gets a 402.
|
|
6896
|
+
// Rather than dead-ending them, tell the agent to drive the user's own browser:
|
|
6897
|
+
// open the URL in Chrome and download it from there (no Vidfarm spend).
|
|
6898
|
+
// NOTE: a hoisted function, NOT a module const — `void main()` runs synchronously
|
|
6899
|
+
// until its first await, so a const declared this far down the file is still in
|
|
6900
|
+
// its TDZ when a command handler reads it (same gotcha as the *_HELP consts).
|
|
6901
|
+
function downloadFreeAlternative() {
|
|
6902
|
+
return "open the URL in Chrome and download it from the page yourself ($0.00 — no wallet spend)";
|
|
6903
|
+
}
|
|
6904
|
+
function printDownloadFreePathHint(ctx, kind) {
|
|
6905
|
+
if (ctx.json)
|
|
6906
|
+
return;
|
|
6907
|
+
const noun = kind === "video" ? "video" : "audio";
|
|
6908
|
+
console.log("");
|
|
6909
|
+
console.log(`${YELLOW}Free path (no Vidfarm spend):${RESET} load the URL in Chrome and grab the ${noun} from there.`);
|
|
6910
|
+
console.log(`${DIM} 1. Open the post/page in Chrome (a real browser session handles the login/consent walls).${RESET}`);
|
|
6911
|
+
console.log(`${DIM} 2. Save the ${noun} from the page — right-click → Save, the browser's downloads, or a downloader site.${RESET}`);
|
|
6912
|
+
console.log(`${DIM} 3. Google "youtube ${noun} downloader" or "tiktok/instagram/x ${noun} downloader" if the page won't give it up.${RESET}`);
|
|
6913
|
+
console.log(`${DIM} 4. Then bring the local file into Vidfarm for free: vidfarm put-file ./the-file --folder <folder>${RESET}`);
|
|
6914
|
+
}
|
|
6880
6915
|
async function runDownloadPostCommand(argv) {
|
|
6881
6916
|
const parsed = parseArgs({
|
|
6882
6917
|
args: argv,
|
|
@@ -6891,6 +6926,11 @@ async function runDownloadPostCommand(argv) {
|
|
|
6891
6926
|
throw new Error("download-post --quality must be one of: best, hd, full_hd.");
|
|
6892
6927
|
}
|
|
6893
6928
|
const ctx = commonContext(parsed.values);
|
|
6929
|
+
guardBilled(ctx, {
|
|
6930
|
+
label: "download a video from a website (paid plan — RapidAPI resolver)",
|
|
6931
|
+
estimate: "small per-download wallet charge",
|
|
6932
|
+
freeAlternative: downloadFreeAlternative()
|
|
6933
|
+
});
|
|
6894
6934
|
const tracer = String(parsed.values.tracer ?? `devcli-download-post-${Date.now().toString(36)}`);
|
|
6895
6935
|
const submit = await apiRequest({
|
|
6896
6936
|
method: "POST",
|
|
@@ -6899,6 +6939,9 @@ async function runDownloadPostCommand(argv) {
|
|
|
6899
6939
|
auth: ctx.auth,
|
|
6900
6940
|
body: { tracer, payload: { source_url: url, quality } }
|
|
6901
6941
|
});
|
|
6942
|
+
if (!submit.ok && (submit.status === 402 || submit.status === 401 || submit.status === 403)) {
|
|
6943
|
+
printDownloadFreePathHint(ctx, "video");
|
|
6944
|
+
}
|
|
6902
6945
|
assertApiOk(submit, "download-post");
|
|
6903
6946
|
const jobId = submit.json?.job_id;
|
|
6904
6947
|
if (!jobId || parsed.values["no-wait"]) {
|
|
@@ -6920,6 +6963,11 @@ async function runDownloadPostAudioCommand(argv) {
|
|
|
6920
6963
|
if (!url)
|
|
6921
6964
|
throw new Error("download-post-audio requires a source URL.");
|
|
6922
6965
|
const ctx = commonContext(parsed.values);
|
|
6966
|
+
guardBilled(ctx, {
|
|
6967
|
+
label: "download audio from a website (paid plan — RapidAPI resolver)",
|
|
6968
|
+
estimate: "small per-download wallet charge",
|
|
6969
|
+
freeAlternative: downloadFreeAlternative()
|
|
6970
|
+
});
|
|
6923
6971
|
const tracer = String(parsed.values.tracer ?? `devcli-download-post-audio-${Date.now().toString(36)}`);
|
|
6924
6972
|
const submit = await apiRequest({
|
|
6925
6973
|
method: "POST",
|
|
@@ -6928,6 +6976,9 @@ async function runDownloadPostAudioCommand(argv) {
|
|
|
6928
6976
|
auth: ctx.auth,
|
|
6929
6977
|
body: { tracer, payload: { source_url: url } }
|
|
6930
6978
|
});
|
|
6979
|
+
if (!submit.ok && (submit.status === 402 || submit.status === 401 || submit.status === 403)) {
|
|
6980
|
+
printDownloadFreePathHint(ctx, "audio");
|
|
6981
|
+
}
|
|
6931
6982
|
assertApiOk(submit, "download-post-audio");
|
|
6932
6983
|
const jobId = submit.json?.job_id;
|
|
6933
6984
|
if (!jobId || parsed.values["no-wait"]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@officexapp/vidfarm-devcli",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.22",
|
|
4
4
|
"description": "Local bridge for the Vidfarm Trackpad Editor. `vidfarm serve <template_id>` boots the FULL editor on localhost (disk-backed records/storage, free in-process render); edit composition.html on disk (Claude Code, Codex, etc.) and the browser live-morphs it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|