A zsh function: submit a video prompt, keep your terminal free

A 19-line .zshrc function sends a prompt to Sume Wan 3.0 (2 s, 480p, $0.125), returns at once and saves the MP4 later. Do not name a variable status.

4 min readSume
All posts

Most shell recipes for video APIs block the terminal until the file arrives, and a clip can take from 30 seconds to several minutes. If you work in zsh, you can do better with one function in ~/.zshrc. video "steam rising from a coffee cup" submits the job, prints the polling URL and gives you the prompt back. The wait happens in the background, and a line appears when the file is saved.

The function uses Wan 3.0 at 2 seconds and 480p, which Sume prices at $0.0625 a second, so $0.125 a run. It needs curl and jq, and a SUME_API_KEY in the environment.

The function

Source the file and call video "prompt" out.mp4, with the file name optional. The submit step runs in the foreground so a bad key or a 402 shows up straight away. The poll loop sits inside a brace group ended with &!, which backgrounds the group and disowns it, so the shell prints no job-control lines. I ran it against a local stand-in server with a short delay: the prompt came back first, and the saved line and file followed.

video() {
  local base=${SUME_API_BASE:-https://api.sume.com} out=${2:-clip-$(date +%H%M%S).mp4}
  local -a auth=(-H "Authorization: Bearer ${SUME_API_KEY:?set SUME_API_KEY}")
  local poll
  poll=$(curl -sS -X POST $base/v1/videos $auth -H 'Content-Type: application/json' \
    -d "$(jq -nc --arg p "$1" '{model:"wan-3.0",prompt:$p,duration:2,resolution:"480p"}')" \
    | jq -er .polling_url) || { print -u2 "submit failed"; return 1 }
  print "submitted: $poll"
  {
    local job state
    while sleep ${POLL_SECONDS:-30}; do
      job=$(curl -sS $poll $auth); state=$(jq -r .status <<<$job)
      [[ $state == (failed|cancelled) ]] && { print -u2 "video: $state"; return 1 }
      [[ $state == completed ]] && break
    done
    curl -sSL $(jq -r '.unsigned_urls[0]' <<<$job) $auth -o $out
    print "video: saved $out, cost $(jq .usage.cost <<<$job)"
  } &!
}

The zsh trap

My first draft named the poll variable status, the obvious name for a job's status field, and the background block died with read-only variable: status. In zsh, status is a special parameter that holds the exit code of the last command, the same role $? has, so assigning to it fails. Bash has no such rule, which is why a script that works in bash can break when it is pasted into a zsh function. The function calls it state.

Two smaller zsh points. auth is an array, so $auth expands to the two words of the header option without the splitting quirks of bash. And [[ $state == (failed|cancelled) ]] is a pattern match, so no regex is needed.

What the job returns

The submit response is a 202 with id, polling_url, status and model. The function polls that URL as given, treats failed and cancelled as the end, and on completed follows unsigned_urls[0], the content URL, with curl -L. Finished jobs report usage.cost, the Sume billable amount, which the function prints.

If you want to retry safely after a flaky connection, add an Idempotency-Key header to the submit call; Sume's guide says a replay with the same key returns the original job. Without it, running video twice with the same prompt starts two jobs and charges for two.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume