Skip to content

feat: keep_original_sound on video_to_video_music and video_to_video_sound (0.11.0) - #23

Merged
spencer-zqian merged 2 commits into
mainfrom
feat/keep-original-sound
Aug 1, 2026
Merged

feat: keep_original_sound on video_to_video_music and video_to_video_sound (0.11.0)#23
spencer-zqian merged 2 commits into
mainfrom
feat/keep-original-sound

Conversation

@spencer-zqian

Copy link
Copy Markdown
Contributor

Follows the API change deployed today (sonilo-api-dashboard #198, live on
backend-public rev 77 / backend-mcp rev 33).

Why this matters even without upgrading

The server flipped both endpoints' defaults. A request that does not set
keep_original_sound now returns the generated audio alone, where it previously
returned the source video's speech with the generated music ducked underneath.
video_to_video_sound's default result therefore carries no music_processed
stem
, since with no voice source there is no processed track.

That is already true in production for current users of this SDK. This release
is what lets them opt back in.

The contract

Two independent knobs:

  • keep_original_sound picks the voice source — the whole original track,
    or preserve_speech for the isolated speech only, or neither.
  • ducking picks how that voice is combined with the generated audio —
    the dynamic duck (default), or ducking=False for a static voice-forward mix.
Request Audio in the returned video
neither set generated audio only
keep_original_sound=True full original sound + audio ducked under it
keep_original_sound=True, ducking=False full original sound + static mix
preserve_speech=True isolated vocals + audio ducked under them
preserve_speech=True, ducking=False static vocal-forward mix

keep_original_sound supersedes preserve_speech (the voice source is a single
choice). No combination is rejected, so nothing here raises.

Video-only, enforced the same way output_format already is

build_v2s_parts is shared by both sound endpoints, and it already had one
field they do not share — output_format, audio-only, keyword-only with a
None default, which VideoToVideoSound simply never passes.
keep_original_sound is the mirror image, so it follows the identical pattern
and VideoToSound never passes it. No type gymnastics needed.

tests/test_keep_original_sound.py asserts that on the public signatures of
all four classes (sync and async, submit and generate), so adding the field
to VideoToSound by reflex fails the suite rather than silently shipping a
parameter the server drops.

Verification

  • 235 tests pass.
  • New tests cover: omitted when unset on both endpoints (so an unset value
    becomes neither "true" nor "false" — the default belongs to the server),
    an explicit False still being sent (a real request, distinct from unset),
    the keep_original_sound + ducking=False static-mix row, both flags sent
    together with precedence deliberately left to the server, and the
    signature-level guard in both directions.

Stale copy corrected

The README asserted the old default ("By default the returned video keeps the
source's speech with the music ducked under it") and described ducking as if
it decided whether speech was kept rather than how it is mixed. The
SoundOutput / SoundResult docstrings said music_processed is present "when
preserve_speech or ducking altered the music bed", which no longer holds. All
four are rewritten.

…sound (0.11.0)

The server flipped both endpoints' defaults, so a request that does not set
keep_original_sound now returns the generated audio alone. This adds the
parameter that opts back in.

keep_original_sound picks the voice source and supersedes preserve_speech;
ducking independently picks how that voice is combined with the generated
audio. Both are emitted only when explicitly passed, so each server default
stands on its own — and they run in opposite directions: ducking is default-ON,
keep_original_sound default-OFF.

The field is video-only, following the pattern already used for output_format
in reverse: keyword-only with a None default on the shared build_v2s_parts, and
the resource that must not send it simply never passes it. Tests assert that on
the public signatures, so adding it to VideoToSound by reflex fails.

Also corrects two README claims and two SoundOutput/SoundResult docstrings that
described the old default and the old music_processed condition.
@sapient-app

sapient-app Bot commented Aug 1, 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: 83fceaf
Status: waiting for staging docs URL

… to it

CI caught that this repo ships three packages, not one: sonilo-cli pinned
sonilo>=0.10.0,<0.11, so bumping the core to 0.11.0 made the editable install
unresolvable. Widened to >=0.11.0,<0.12 and bumped sonilo-cli to 0.8.0.
sonilo-video-kit's >=0.3,<1.0 already covers it.

That also surfaced that the Python CLI needed the same treatment as the JS one:
--keep-original-sound on both video-to-video-music and video-to-video-sound,
plus --no-ducking on video-to-video-music, which the SDK supported but the CLI
never exposed -- without it the static-mix combination was unreachable.

_run_sound is shared by video-to-sound and video-to-video-sound, so the flag is
forwarded only when the parser actually defined it: video_to_sound.generate()
does not accept the keyword at all, so it must be omitted rather than passed as
None.
@spencer-zqian
spencer-zqian merged commit 4b872fc into main Aug 1, 2026
2 checks passed
@spencer-zqian
spencer-zqian deleted the feat/keep-original-sound branch August 1, 2026 11:41
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