From 5e25450aa82ec0d663fed971f18b95706a77c435 Mon Sep 17 00:00:00 2001 From: Spencer Qian Date: Sat, 18 Jul 2026 17:17:05 -0700 Subject: [PATCH 1/3] docs: document video-to-video endpoints and v2m ducking/preserve_speech/output_format --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index 42891d0..7865799 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,50 @@ result = client.tasks.wait( `isolate_vocals=True` with an explicit non-async `mode` raises `SoniloError` locally before any request is sent. +### Ducking, speech & output format (async video-to-music) + +`submit()` / `generate_async()` also accept: + +- `preserve_speech` — keep the source speech/vocals in the result. This is the + current name for `isolate_vocals`, which is still accepted as an alias. +- `ducking` — duck the generated music under the source voice. It is **on by + default** in async mode; pass `ducking=False` to opt out. When it runs, the + result gains a `ducked` list alongside `audio`. +- `output_format` — `"m4a"` (default) or `"wav"` (requires async mode). + +```python +result = client.video_to_music.generate_async( + video="my_video.mp4", + preserve_speech=True, + output_format="wav", + # ducking defaults on in async — pass ducking=False to disable +) +result.save("track.wav") +if result.ducked: + result.save("ducked.wav", which="ducked") +``` + +## Video to video + +Generate music or sound effects and get back a **re-hosted video** with the +audio muxed in — not just an audio file. Both endpoints are async; `generate()` +submits and polls to a `VideoResult`: + +```python +music = client.video_to_video_music.generate( + video="my_video.mp4", # path, bytes, open file, or use video_url= + prompt="cinematic orchestral swell", + preserve_speech=True, +) +music.save("scored.mp4") + +sfx = client.video_to_video_sfx.generate( + video="my_video.mp4", + segments=[{"start": 0, "end": 2, "prompt": "footsteps on gravel"}], +) +sfx.save("with_sfx.mp4") +``` + ## Streaming ```python From 7d9674060211135a56a4549d6d8643e0d53bdde2 Mon Sep 17 00:00:00 2001 From: Spencer Qian Date: Sat, 18 Jul 2026 17:20:39 -0700 Subject: [PATCH 2/3] docs: add Authentication section (where to get and set the API key) --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 7865799..0d05e93 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,24 @@ Python ≥ 3.9. Sync and async clients included. pip install sonilo ``` +## Authentication + +Create an API key in your [Sonilo dashboard](https://platform.sonilo.com/dashboard/api-keys), +then give it to the client either as an environment variable (recommended) or +inline: + +```bash +export SONILO_API_KEY=sk_... +``` + +```python +client = Sonilo() # reads SONILO_API_KEY +client = Sonilo(api_key="sk_...") # or pass it directly +``` + +Keep your key secret — use it only server-side, never commit it, and prefer the +environment variable over hardcoding it. + ## Quickstart ```python From 2ebc27dc411add9fa1405869c13119da3e92467b Mon Sep 17 00:00:00 2001 From: Spencer Qian Date: Sat, 18 Jul 2026 17:26:28 -0700 Subject: [PATCH 3/3] docs: rename Vocal isolation section to Preserve speech; drop isolate_vocals from docs --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0d05e93..f02b55d 100644 --- a/README.md +++ b/README.md @@ -50,21 +50,23 @@ track = client.video_to_music.generate(video="my_video.mp4", prompt="upbeat") track = client.video_to_music.generate(video_url="https://example.com/clip.mp4") ``` -### Vocal isolation (async) +### Preserve speech (async) -Pass `isolate_vocals=True` to also get an isolated vocal stem and a muxed -video, alongside the scored audio. This requires async processing — submit -returns a `task_id` immediately, and `generate_async()` wraps submit + poll: +Pass `preserve_speech=True` to keep the source speech/vocals in the result. +You also get a separate speech stem (`vocals`) and a mux (the generated music +mixed with the preserved speech) alongside the scored audio. This requires async +processing — submit returns a `task_id` immediately, and `generate_async()` +wraps submit + poll: ```python result = client.video_to_music.generate_async( video="my_video.mp4", prompt="upbeat", - isolate_vocals=True, # implies mode="async"; omit mode to let it auto-select + preserve_speech=True, # implies mode="async"; omit mode to let it auto-select ) result.save("mix.m4a") # result.audio[0] — the full mix result.save("vocals.m4a", which="vocals") -result.save("video.mp4", which="mux") # video muxed with the generated audio +result.save("video.mp4", which="mux") # generated music muxed with the preserved speech print(result.title.title if result.title else None) ``` @@ -73,22 +75,22 @@ Or control submission and polling yourself: ```python from sonilo.resources.tasks import parse_music_result -task = client.video_to_music.submit(video_url="https://example.com/clip.mp4", isolate_vocals=True) +task = client.video_to_music.submit(video_url="https://example.com/clip.mp4", preserve_speech=True) result = client.tasks.wait( task.task_id, parser=parse_music_result, # required: tasks.wait()/get() default to the SFX parser ) ``` -`isolate_vocals=True` with an explicit non-async `mode` raises `SoniloError` +`preserve_speech=True` with an explicit non-async `mode` raises `SoniloError` locally before any request is sent. ### Ducking, speech & output format (async video-to-music) `submit()` / `generate_async()` also accept: -- `preserve_speech` — keep the source speech/vocals in the result. This is the - current name for `isolate_vocals`, which is still accepted as an alias. +- `preserve_speech` — keep the source speech/vocals in the result (see + [Preserve speech](#preserve-speech-async) above). - `ducking` — duck the generated music under the source voice. It is **on by default** in async mode; pass `ducking=False` to opt out. When it runs, the result gains a `ducked` list alongside `audio`.