Skip to content

paperfoot/seedance-cli

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Seedance CLI — ByteDance Seedance 2.0 from Your Terminal

Generate AI video with text, images, and reference footage. One Rust binary. No MCP server required.


Star this repo    Follow @longevityboris


Crates.io   CI   License: MIT   PRs Welcome


Pay-per-second video generation from ByteDance, wrapped in a tool agents can actually drive. JSON on pipe, human-readable on TTY, semantic exit codes, one static binary.

Install · API Key Setup · Quick Start · Reference Inputs · Companion Tools · Known Quirks · Commands


Why this exists

Seedance 2.0 produces strong 480p and 720p output at a low per-second price. The ModelArk API that serves it wasn't built for scripts — auth is awkward, responses are verbose, and there is no single step from prompt to mp4 on disk. This CLI gives you that step, and keeps the output shape stable whether you're piping into jq or watching a progress bar.

Install

# Cargo (works everywhere Rust does)
cargo install seedance

# Homebrew (macOS + Linux)
brew install 199-biotechnologies/tap/seedance

# Prebuilt binary
# https://github.com/paperfoot/seedance-cli/releases/latest

API Key Setup

BytePlus is ByteDance's official international cloud. The model platform is ModelArk. Three minutes, start to finish:

  1. Go to console.byteplus.com/ark and sign in (free account, credit card needed for paid generation).
  2. Open the API Keys page from the left sidebar.
  3. Click Create API Key — copy the ark-… string it shows you (it won't show again).
  4. Hand it to the CLI in one of three ways, pick whatever fits:
# (a) Environment variable — best for CI
export SEEDANCE_API_KEY=ark-xxxxxxxx
# ARK_API_KEY is also accepted

# (b) Config file — stored locally at chmod 600, never echoed back
seedance config set api-key ark-xxxxxxxx

# (c) Per-command flag — overrides everything else
seedance generate --prompt "" --api-key ark-xxxxxxxx

Verify:

seedance doctor

If anything's wrong, doctor tells you which step to revisit.

Quick Start

seedance generate --prompt "A cat yawns at the camera" --wait
# -> ~/Documents/seedance/20260420T023015Z-abc12345.mp4
# -> ~/Documents/seedance/20260420T023015Z-abc12345.seedance.json   # sidecar manifest

Default output is ~/Documents/seedance/. Override with -o /path/to/file.mp4 or -o /some/dir/, or use --label / --project to drive the default naming.

seedance generate --project cafe-scene --label alice-walk \
  --image ~/Documents/seedance/cafe-scene/alice-sheet.png \
  --prompt "..." --wait
# -> ~/Documents/seedance/cafe-scene/20260420T023015Z-alice-walk-abc12345.mp4
# -> ~/Documents/seedance/cafe-scene/20260420T023015Z-alice-walk-abc12345.seedance.json

Sidecars written by generate --wait carry the full request — prompt, references, model, seed, duration, task id, timestamps — with "source": "generate". Sidecars written by the bare seedance download command carry task/download metadata only (the BytePlus GetTask response doesn't echo the original request back) with "source": "download". Agents should key on .source before trusting either:

jq 'select(.source == "generate") | {file: .downloaded_to, label, prompt}' \
  ~/Documents/seedance/**/*.seedance.json

How it works

 prompt + refs          ModelArk queue            mp4
     |                        |                    ^
     v                        v                    |
  seedance gen   ->    task id returned   ->   seedance download
                              |
                              v
                       seedance status <id>

One task id from start to finish. Fire and forget, or pass --wait to block until the file lands on disk.

Reference Inputs

Seedance 2.0 accepts a free mix of references in a single content array:

Flag Kind Limit Notes
--image / -i Image 0–9 Path (base64'd inline) or URL. Role: reference_image
--first-frame Image 1 Role: first_frame
--last-frame Image 1 Role: last_frame (requires --first-frame)
--video / -v Video 0–3 URL only (API restriction). Total ≤ 15s
--audio / -a Audio 0–3 wav/mp3. Path (base64'd) or URL. Total ≤ 15s. Needs an image or video alongside.

Address references inside the prompt:

[Image 1] the boy waves; [Video 1] camera style; [Audio 1] background music

Use time codes for multi-shot: [0-4s]: wide establishing shot; [4-8s]: push in.

Supported resolutions: 480p, 720p. Supported ratios: 16:9, 4:3, 1:1, 3:4, 9:16, 21:9, adaptive. Duration: 4–15s or -1 for auto.

Examples

# Text-to-video, wait and download
seedance generate \
  --prompt "A kitten yawns and blinks at the camera, cozy warm light" \
  --duration 6 --resolution 720p --ratio 16:9 \
  --wait --output kitten.mp4

# Multimodal reference-to-video with the fast tier
seedance generate \
  --prompt "[Image 1] the boy smiles; [Image 2] the corgi jumps in; [Video 1] camera motion" \
  --image boy.png --image corgi.png \
  --video https://my-cdn.example/style.mp4 \
  --fast --wait -o out.mp4

# Fire and forget, poll later
TASK=$(seedance gen --prompt "..." | jq -r '.data.id')
seedance status "$TASK"
seedance download "$TASK" -o final.mp4

Companion Tools

Four helpers ship in the same binary, built to work around specific Seedance limits. Each one chains cleanly into generate.

Tool What it does
seedance character-sheet --character N Builds a 9-angle (or 4-angle) sheet from a single photo via Nano Banana Pro. Pass the resulting PNG to --image to keep one person consistent across shots. For multi-character scenes, run this once per person with distinct --character names and pass every sheet as a separate --image.
seedance audio-to-video Wraps an audio file in a silent mp4 (ffmpeg under the hood) so you can pass it as --video instead of --audio. Preserves lyrics exactly. --upload hosts the file on tmpfiles.org and prints the URL.
seedance prep-face Applies an empirically verified grain + desaturation recipe so a real portrait clears ModelArk's face filter. --bw swaps to the pure grayscale variant.
seedance upload <file> Uploads a local file to tmpfiles.org and prints a direct-download URL, ready to paste into --video.

Multi-character scenes

Cap scenes at two characters. Three or more in the same shot breaks identity lock — Seedance starts morphing faces across frames. For three-person scenes, split into two shots and intercut in post.

# One sheet per character, shared project folder
seedance character-sheet alice.jpg --character alice --project cafe-scene
seedance character-sheet bob.jpg   --character bob   --project cafe-scene

# Generate with both sheets; each gets an explicit job in the prompt
seedance generate --project cafe-scene --label cafe-opening \
  --image ~/Documents/seedance/cafe-scene/alice-sheet.png \
  --image ~/Documents/seedance/cafe-scene/bob-sheet.png \
  --prompt "[Image 1] is Alice's 9-angle reference. Her face and hair match [Image 1] \
            exactly. [Image 2] is Bob's 9-angle reference. His face and beard match \
            [Image 2] exactly. [0-4s]: wide, Alice and Bob at a corner table in a \
            sunlit Parisian cafe. [4-14s]: medium, Alice picks up her espresso cup." \
  --duration 14 --ratio 16:9 --wait

Prompting principles

Seedance reads prompts literally. Do not rely on logic or genre shorthand — describe everything you need to see.

  • Every reference must have an explicit job in the prompt. Unnamed refs are silently dropped.
  • One verb per shot. Split multi-action scenes with time codes: [0-4s]: ...; [4-9s]: ....
  • Negative prompts do not work. Phrase positively: "eyes natural, soft eye contact, blinks occasionally" beats "no weird eyes".
  • Early tokens dominate. Put camera language, subject, and style in the first half of the prompt.
  • Prompt length: 30–200 words. Too short underspecifies; too long causes detail dropout.
  • Describe wardrobe, lighting, and setting concretely even when it's visible in a reference — these drift more than faces do.

For deeper prompt recipes (UGC vs marketing vs cinematic templates, chaining scenes into longer films, voice cloning workflows), see the seedance-prompting skill.

Known Quirks

Hard-won notes from real users, saved so you don't hit the same walls:

  • Audio upload mutates lyrics. Reported by @MrDavids1 and @simeonnz: uploading audio directly alters the song. Workaround — seedance audio-to-video song.mp3 --upload, then pass the printed URL as --video. The API trusts reference videos for audio content but post-processes raw audio.
  • No real human faces in references. ModelArk blocks direct upload of real human portraits. Either run the photo through seedance prep-face first, or build a character-sheet from one photo and use that.
  • Videos must be URLs. Host the file first (S3, Cloudinary, or seedance upload → tmpfiles.org) and pass the URL. Local files are rejected by the API.
  • No 1080p. Seedance 2.0 tops out at 720p. Use --resolution 720p1080p returns an error.

Commands

Command Purpose
seedance generate / gen Create a video generation task
seedance status <id> / get Poll a task
seedance download <id> Download the mp4 for a completed task
seedance cancel <id> / rm Cancel a queued task
seedance character-sheet <photo> Build a consistent-character reference grid
seedance audio-to-video <file> Wrap audio in silent mp4 (lyrics workaround)
seedance prep-face <photo> Prepare a portrait to pass the face filter
seedance upload <file> Host a local file and print a public URL
seedance models / ls List available model ids
seedance doctor Check API key, base URL, and deps
seedance agent-info / info Machine-readable capability manifest
seedance skill install Deploy SKILL.md to Claude, Codex, or Gemini
seedance config show / path / set / unset Manage the TOML config
seedance update [--check] Self-update from GitHub Releases

Global flags: --json (force JSON), --quiet (suppress info), --help, --version.

Exit codes

Code Meaning
0 Success
1 Transient (network, API, IO, update) — retry
2 Config (missing API key, bad base URL) — fix setup
3 Invalid input — fix arguments
4 Rate limited — wait and retry

Every error also prints a machine-readable error_code and a literal recovery suggestion when you're in --json mode.

Built with

Contributing

PRs and issues welcome. See CONTRIBUTING.md for the short version.

License

MIT — see LICENSE.


Built by Boris Djordjevic at Paperfoot AI


If this saved you time:

Star this repo    Follow @longevityboris