veo-generator

Render a scripted video project into mp4 clips with the Google Veo 3.1 API. Reads a project's shot package (shots/*.md), one clip per camera cut, reusing each shot's prompt and sketch — world anchor o

SKILL.md

GitHub

Veo Generator

Turns an already-scripted shot package into actual video. The prompting is done by the veo-showreel-production-kit skill (STYLE BIBLE → per-shot shots/*.md); this skill is the render step that calls the Veo 3.1 API and downloads mp4s.

It expects the layout the production kit emits:

<Project>/
  .env                      <- (optional) per-project API key lives here
  video_production/
    shots/shot_*.md         <- source of truth: prompt, negative, seed, aspect, refs
    storyboard/*.png        <- first-frame sketches + 00_world_anchor.png
    renders/                <- mp4 output (created here)

Each shots/shot_NN.md already contains everything the renderer needs: the ▶ Full Veo prompt block, the ⛔ Negative prompt block, and a Reproduction & consistency line with the seed, aspect ratio, resolution, reference image(s) and first-frame image. The renderer parses those — it never re-invents the prompt.

Prerequisites

  • The pi-veo CLI (this package). Runs as TypeScript via pi's jiti loader — no build step.
  • ffmpeg — only needed for --chain (seamless A→B last-frame handoff). Declared in pi.tools and resolved via the tool registry; absent → non-chain videos still generate.
  • An API key (see below). Get one at https://aistudio.google.com/apikey. Declared as an env probe (GEMINI_API_KEY) in pi.tools. The registry recognizes GEMINI_API_KEY only — the CLI's VEO_API_KEY / GOOGLE_API_KEY aliases still work at run time but are NOT tracked in Settings → Tools.

API key — per project or global

Resolution order, first non-empty wins:

# Source Use
1 --api-key <KEY> flag one-off
2 VEO_API_KEY / GEMINI_API_KEY / GOOGLE_API_KEY env var machine-wide
3 <Project>/.env or …/video_production/.env per-project key
4 this package's .env global fallback

Copy .env.example and set VEO_API_KEY=…. Never commit real keys.

Procedure

  1. Dry-run / validate the package (no key spent):

    pi-veo parse <Project>
    

    Confirms every shot has a parseable prompt, shows seed/aspect/resolution, the first-frame sketch, the reference image and seamless→next chaining. Add --json for machine output, --shots 01 03A for a subset.

  2. Plan the render (resolves key + model + outputs, still no API call):

    pi-veo plan <Project>
    
  3. Render (calls Veo, polls, downloads to video_production/renders/):

    pi-veo render <Project>
    
    • Test a single shot first: --shots 01
    • Cheaper/faster preview pass: --model fast --resolution 720p
    • Seamless A→B handoff (sequential, uses ffmpeg last frame): --chain
    • Render several independent shots at once: --parallel 4
    • Also attach the world-anchor as a reference image: --with-reference
    • Re-render existing clips: --force Already-rendered shot_NN.mp4 files are skipped unless --force, so the command is safe to re-run / resume.
  4. (Optional) regenerate storyboard sketches (the first-frame images) via nano-banana, if they're missing or you changed sketch_prompts.json:

    pi-veo storyboard <Project>
    
  5. Assemble in post — cut the renders/*.mp4 in timecode order, lay the official voiceover + music, bake captions, drop the logo into the reserved space of the final shot. (Veo only makes ambient SFX — speech is forbidden in the AUDIO LOCK on purpose.) With sidecars (film.json + timeline.json) automate it: pi-veo export timeline <Project> --clips video_production/renders → call video_compose on the printed spec → pi-veo mux <Project> --picture <final_video.mp4> → video_production/master/master.mp4.

Optional: other providers via pi-video-gen

Needs sidecars (film.json, shots/shot_NN.json) — see the production kit. @amaster.ai/pi-video-gen is a user-level pi extension, never a dependency of this package: pi install npm:@amaster.ai/pi-video-gen (verified 0.1.18; 0.1.x formats may change). Coupling is file-format only.

  1. Run every pi-veo export from the pi session cwd, without cd — pi-video-gen resolves frames and its outputDir (default .video-gen) against that cwd. If outputDir is configured differently, pass --out <that dir>.
  2. Call video_capabilities, copy the model's duration range and aspect ratios into --durations <min>-<max> / --aspect 16:9,9:16 (not parsed automatically).
  3. pi-veo export render <Project> [--durations 4-15] [--aspect 16:9] → call video_render with the printed render-input.json. SEAMLESS shots become lastFramePath = next shot's sketch; --no-last-frame for models without first+last frame (e.g. kling-3.0-turbo).
  4. Resume = call video_render again on the SAME spec. Never re-export — a new job re-bills every shot. Export refuses while a previous spec exists (.pi-veo/exports.json); --new-job only for a genuinely revised film.
  5. Picture edit: pi-veo export timeline <Project> --clips .video-gen/<job>/shots (or video_production/renders) → video_compose → pi-veo mux.

Trade-offs: no seed, no local reference images, resolution = the model default (OpenRouter Veo is 720p / 16:9) — keep pi-veo render (Veo direct) for 4K/seed quality passes; use pi-video-gen for Seedance / Kling / MiniMax. Run /video-gen doctor and one cheap clip first. pi-veo mux needs ffmpeg + ffprobe; --burn needs ffmpeg's subtitles filter (libass), soft captions are the default.

Notes & pitfalls

  • Veo clips are ≤8s. The shot package is already split into ≤8s units; don't merge them. Long beats are A/B pairs.
  • Model IDs: standard → veo-3.1-generate-preview, fast → veo-3.1-fast-generate-preview. Pass a full id to --model to use another.
  • Resolution 4k is Veo 3.1 preview only and is slower + pricier; use --resolution 1080p (or 720p for cheap previews) to override the shot files.
  • Parallel rendering: by default shots render one-at-a-time. --parallel N keeps up to N Veo operations in flight; the real ceiling is your Veo API quota. --chain forces sequential (B needs A's rendered last frame).
  • first-frame vs reference: by default the shot's own storyboard sketch is the image-to-video first frame. --with-reference additionally sends the world-anchor as an asset reference; if the model rejects that combo the renderer automatically retries without it.
  • Cost: every render call spends credits. Always parse → plan → render one shot → then the batch.
  • Gemini Developer API quirks (AI Studio key): the Developer API rejects seed and enhance_prompt. Use --no-seed (and leave enhance_prompt off — it's opt-in via --enhance-prompt, Vertex only). Reproducibility then relies on the first-frame sketches + STYLE LOCK, not the seed.
  • Vertex env trap: if GOOGLE_GENAI_USE_VERTEXAI=true / GOOGLE_CLOUD_PROJECT are exported, the client routes to Vertex and fails. Unset them to force the Developer API.
  • Logs: each run appends to renders/render_log.jsonl (status, seed, model, resolution, output path, seconds) for auditing.