Skip to content

feat(cli): add video-to-video-music and video-to-video-sfx - #18

Merged
spencer-zqian merged 2 commits into
mainfrom
feat/cli-video-to-video
Jul 25, 2026
Merged

feat(cli): add video-to-video-music and video-to-video-sfx#18
spencer-zqian merged 2 commits into
mainfrom
feat/cli-video-to-video

Conversation

@spencer-zqian

Copy link
Copy Markdown
Contributor

The API ships 11 generation endpoints; the CLI covered 9. video-to-video-music and video-to-video-sfx had SDK resources and public documentation but were never wired up as CLI subcommands. This closes that gap, so the CLI now covers every generation endpoint.

The two commands

Both are async-only and write a video, modelled on the existing video-to-video-sound:

  • video-to-video-music — exactly one of --video / --video-url, plus optional --prompt, --preserve-speech, --isolate-vocals, --output (defaults to an .mp4)
  • video-to-video-sfx — the same, with --segments taking SFX-shaped {start, end, prompt} items, reusing the existing segments reader and shape rather than a second copy

video-to-video-music deliberately has no --segments: the request builder cannot send segments to that endpoint at all, and a test pins that.

Cross-CLI parity

The Node and Python CLIs were implemented against one shared contract and their help output was compared directly afterwards. The flag surfaces match exactly.

A pre-existing bug fixed along the way

That comparison surfaced a long-standing error on the existing video-to-music command in both CLIs. Its help presented --isolate-vocals and --preserve-speech as two independent options with different descriptions, but the backend ORs them into a single behaviour — backend/app/routers/v1/video_to_music.py normalises the two under a comment naming preserve_speech the current public name and isolate_vocals the legacy field name. One feature was being documented as two.

Both CLIs now describe --isolate-vocals as a legacy alias, with --preserve-speech listed first, and a test pins the wording so it cannot silently regress. The endpoint does produce vocals and mux tracks, but neither CLI can retrieve them — there is no flag to fetch anything but the main audio — so advertising a stem would trade one misleading line for another.

Tests

83 tests pass in sonilo-cli (15 new for the two commands, plus a parametrised guard on the --isolate-vocals wording). Root suite 192 pass and sonilo-video-kit 53 pass, both untouched.

The result is saved through the SDK's own VideoResult.save() rather than reaching into the result's fields, so the CLI cannot drift from how the SDK models the artifact.

Release

sonilo-cli goes 0.4.0 → 0.5.0 in both pyproject.toml and __init__.py — the latter feeds the x-sonilo-client-version header, so the two move together. The core sonilo package is not bumped.

The API has 11 generation endpoints and the CLI covered 9: both
video-to-video endpoints had SDK resources and public docs, but no
`sonilo` subcommand. Both are async-only and return the source picture
with the generated audio muxed in, so they follow video-to-video-sound:
one file out, default `output.mp4`, no --format.

video-to-video-music takes --prompt, --preserve-speech and
--isolate-vocals; unset switches forward None rather than false so the
server default stands. It takes no --segments, matching the resource.

video-to-video-sfx takes --prompt and --segments in the SFX shape
{start, end, prompt}, reusing the existing _add_segments helper and the
shared shape-checking reader, so a music-shaped array is rejected before
any request is made.

Bumps sonilo-cli to 0.5.0 in pyproject.toml and __init__.py together,
since the latter feeds the x-sonilo-client-version header.
Both endpoints OR the two fields into a single behaviour server-side —
video_to_video.py does `keep_speech = bool(preserve_speech) or
bool(isolate_vocals)` and video_to_music.py does the same normalization
onto its own local — so --isolate-vocals is the legacy name for
--preserve-speech, not a second feature.

The help text on both commands claimed it split out a vocals-only stem.
On video-to-video-music that describes something the endpoint cannot do
at all: the result is one muxed video with no stems. On video-to-music
the flag was pre-existing and equally misleading, since it presented two
names for one feature as two independent options.

Also corrects the two matching claims in the CLI README and adds a test
so neither help string can drift back.
@sapient-app

sapient-app Bot commented Jul 25, 2026

Copy link
Copy Markdown

Sapient docs evals

Waiting for the staging docs URL before running evals.

Sapient will start the selected PR evals automatically when GitHub reports a successful docs deployment for this PR. This usually happens within 15 minutes.

Commit: 74d04a5
Status: waiting for staging docs URL

@spencer-zqian
spencer-zqian merged commit 7c08831 into main Jul 25, 2026
2 checks passed
@spencer-zqian
spencer-zqian deleted the feat/cli-video-to-video branch July 25, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant