sonilo-cli 0.7.0__tar.gz → 0.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/PKG-INFO +18 -2
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/README.md +16 -0
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/pyproject.toml +2 -2
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/src/sonilo_cli/__init__.py +1 -1
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/src/sonilo_cli/__main__.py +36 -4
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/.gitignore +0 -0
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/LICENSE +0 -0
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/tests/__init__.py +0 -0
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/tests/test_cli.py +0 -0
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/tests/test_context7.py +0 -0
- {sonilo_cli-0.7.0 → sonilo_cli-0.8.1}/tests/test_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sonilo-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Command-line interface for the Sonilo API: generate music and sound effects from text or video
|
|
5
5
|
Project-URL: Repository, https://github.com/sonilo-ai/sonilo-python
|
|
6
6
|
Author: Sonilo AI
|
|
@@ -8,7 +8,7 @@ License-Expression: MIT
|
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: ai,cli,music,sfx,sonilo,text-to-music,video-to-music
|
|
10
10
|
Requires-Python: >=3.9
|
|
11
|
-
Requires-Dist: sonilo<0.
|
|
11
|
+
Requires-Dist: sonilo<0.12,>=0.11.0
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
14
14
|
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
@@ -197,3 +197,19 @@ Because the summary is on stderr, `sonilo account | jq .trial` still sees clean
|
|
|
197
197
|
Once an endpoint's free runs are used up, calls to it bill at the normal rate — or, if the account
|
|
198
198
|
has never been funded, fail with `HTTP 402: ... (trial_exhausted)` until a payment method is added.
|
|
199
199
|
That is the one 402 a retry can never fix.
|
|
200
|
+
|
|
201
|
+
## Rate limits
|
|
202
|
+
|
|
203
|
+
Two separate limits return `HTTP 429`, and they want opposite handling. The CLI prints the API's
|
|
204
|
+
own sentence, so the wording says which one you hit:
|
|
205
|
+
|
|
206
|
+
sonilo: HTTP 429: Rate limit exceeded: your account allows 60 requests per minute. Rejected requests count toward the limit too, so wait for the next minute window (up to 60 sec) rather than retrying right away. To raise your limit, contact info@sonilo.com. (rate_limit_exceeded)
|
|
207
|
+
sonilo: HTTP 429: Too many concurrent generations: 5 of 5 in progress. Wait for one to finish before starting another. To raise your limit, contact info@sonilo.com. (rate_limit_exceeded)
|
|
208
|
+
|
|
209
|
+
The first means calls are going out too fast. The counter runs on a fixed 60-second window and
|
|
210
|
+
rejected calls count toward it too, so wait the window out instead of retrying inside it. The
|
|
211
|
+
second means every generation slot is busy — waiting alone frees nothing, a running generation has
|
|
212
|
+
to finish first.
|
|
213
|
+
|
|
214
|
+
`sonilo account` prints the account's own `rpm_limit` and `concurrency_limit`; the numbers above
|
|
215
|
+
are the standard-tier defaults. Email info@sonilo.com to raise either.
|
|
@@ -181,3 +181,19 @@ Because the summary is on stderr, `sonilo account | jq .trial` still sees clean
|
|
|
181
181
|
Once an endpoint's free runs are used up, calls to it bill at the normal rate — or, if the account
|
|
182
182
|
has never been funded, fail with `HTTP 402: ... (trial_exhausted)` until a payment method is added.
|
|
183
183
|
That is the one 402 a retry can never fix.
|
|
184
|
+
|
|
185
|
+
## Rate limits
|
|
186
|
+
|
|
187
|
+
Two separate limits return `HTTP 429`, and they want opposite handling. The CLI prints the API's
|
|
188
|
+
own sentence, so the wording says which one you hit:
|
|
189
|
+
|
|
190
|
+
sonilo: HTTP 429: Rate limit exceeded: your account allows 60 requests per minute. Rejected requests count toward the limit too, so wait for the next minute window (up to 60 sec) rather than retrying right away. To raise your limit, contact info@sonilo.com. (rate_limit_exceeded)
|
|
191
|
+
sonilo: HTTP 429: Too many concurrent generations: 5 of 5 in progress. Wait for one to finish before starting another. To raise your limit, contact info@sonilo.com. (rate_limit_exceeded)
|
|
192
|
+
|
|
193
|
+
The first means calls are going out too fast. The counter runs on a fixed 60-second window and
|
|
194
|
+
rejected calls count toward it too, so wait the window out instead of retrying inside it. The
|
|
195
|
+
second means every generation slot is busy — waiting alone frees nothing, a running generation has
|
|
196
|
+
to finish first.
|
|
197
|
+
|
|
198
|
+
`sonilo account` prints the account's own `rpm_limit` and `concurrency_limit`; the numbers above
|
|
199
|
+
are the standard-tier defaults. Email info@sonilo.com to raise either.
|
|
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sonilo-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.8.1"
|
|
8
8
|
description = "Command-line interface for the Sonilo API: generate music and sound effects from text or video"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.9"
|
|
12
12
|
authors = [{ name = "Sonilo AI" }]
|
|
13
|
-
dependencies = ["sonilo>=0.
|
|
13
|
+
dependencies = ["sonilo>=0.11.0,<0.12"]
|
|
14
14
|
keywords = ["sonilo", "cli", "music", "sfx", "text-to-music", "video-to-music", "ai"]
|
|
15
15
|
|
|
16
16
|
[project.urls]
|
|
@@ -331,6 +331,13 @@ def _stem_path(out: str, stem: str, media: Any) -> str:
|
|
|
331
331
|
|
|
332
332
|
def _run_sound(client: Sonilo, args: argparse.Namespace, resource: Any, default_ext: str) -> None:
|
|
333
333
|
out = args.output if args.output is not None else f"output.{default_ext}"
|
|
334
|
+
# video-to-video-sound only. Its parser is the only one that defines the
|
|
335
|
+
# flag, and video_to_sound.generate() does not accept the keyword at all,
|
|
336
|
+
# so it has to be omitted here rather than forwarded as None — the same
|
|
337
|
+
# split the SDK enforces by never passing it from the audio resource.
|
|
338
|
+
extra: Dict[str, Any] = {}
|
|
339
|
+
if getattr(args, "keep_original_sound", False):
|
|
340
|
+
extra["keep_original_sound"] = True
|
|
334
341
|
result = resource.generate(
|
|
335
342
|
video=args.video,
|
|
336
343
|
video_url=args.video_url,
|
|
@@ -340,6 +347,7 @@ def _run_sound(client: Sonilo, args: argparse.Namespace, resource: Any, default_
|
|
|
340
347
|
preserve_speech=True if args.preserve_speech else None,
|
|
341
348
|
ducking=False if args.no_ducking else None,
|
|
342
349
|
variants_num=args.variants,
|
|
350
|
+
**extra,
|
|
343
351
|
)
|
|
344
352
|
multi = args.variants is not None and args.variants > 1 and len(result.outputs) > 1
|
|
345
353
|
if not multi:
|
|
@@ -395,7 +403,12 @@ def cmd_video_to_video_music(client: Sonilo, args: argparse.Namespace) -> None:
|
|
|
395
403
|
client.video_to_video_music,
|
|
396
404
|
prompt=args.prompt,
|
|
397
405
|
# Unset flags forward None, not False, so the server default stands —
|
|
398
|
-
# same reasoning as --no-ducking on the sound commands.
|
|
406
|
+
# same reasoning as --no-ducking on the sound commands. The two
|
|
407
|
+
# defaults run opposite ways: ducking is default-ON server-side and
|
|
408
|
+
# keep_original_sound default-OFF, so each is only ever sent to change
|
|
409
|
+
# the default, never to restate it.
|
|
410
|
+
keep_original_sound=True if args.keep_original_sound else None,
|
|
411
|
+
ducking=False if args.no_ducking else None,
|
|
399
412
|
preserve_speech=True if args.preserve_speech else None,
|
|
400
413
|
isolate_vocals=True if args.isolate_vocals else None,
|
|
401
414
|
variants_num=args.variants,
|
|
@@ -612,8 +625,18 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
612
625
|
_add_global(p_v2vm)
|
|
613
626
|
_add_video_source(p_v2vm)
|
|
614
627
|
p_v2vm.add_argument("--prompt", default=None, help="Optional creative direction.")
|
|
628
|
+
p_v2vm.add_argument("--keep-original-sound", dest="keep_original_sound",
|
|
629
|
+
action="store_true",
|
|
630
|
+
help="Keep the source video's whole original audio, with the "
|
|
631
|
+
"generated music under it. Off by default, so by default "
|
|
632
|
+
"the result's audio is the generated music alone. "
|
|
633
|
+
"Supersedes --preserve-speech.")
|
|
634
|
+
p_v2vm.add_argument("--no-ducking", dest="no_ducking", action="store_true",
|
|
635
|
+
help="Combine the voice and the music as a static mix instead "
|
|
636
|
+
"of a dynamic duck. No effect without "
|
|
637
|
+
"--keep-original-sound or --preserve-speech.")
|
|
615
638
|
p_v2vm.add_argument("--preserve-speech", dest="preserve_speech", action="store_true",
|
|
616
|
-
help="Keep source speech in the mix.")
|
|
639
|
+
help="Keep only the source's isolated speech in the mix.")
|
|
617
640
|
# Same aliasing as video-to-music, and here the endpoint collapses the two
|
|
618
641
|
# into a single boolean before it reaches the model (video_to_video.py:
|
|
619
642
|
# `keep_speech = bool(preserve_speech) or bool(isolate_vocals)`), with no
|
|
@@ -644,10 +667,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
644
667
|
p_v2vsd.add_argument("--sfx-prompt", dest="sfx_prompt", default=None,
|
|
645
668
|
help="Optional creative direction for the sound effects.")
|
|
646
669
|
_add_segments(p_v2vsd, SFX_SHAPE)
|
|
670
|
+
p_v2vsd.add_argument("--keep-original-sound", dest="keep_original_sound",
|
|
671
|
+
action="store_true",
|
|
672
|
+
help="Keep the source video's whole original audio, with the "
|
|
673
|
+
"generated music and effects under it. Off by default, so "
|
|
674
|
+
"by default the result's audio is the generated audio "
|
|
675
|
+
"alone and there is no music_processed stem. Supersedes "
|
|
676
|
+
"--preserve-speech. This command only.")
|
|
647
677
|
p_v2vsd.add_argument("--preserve-speech", dest="preserve_speech", action="store_true",
|
|
648
|
-
help="Keep source speech in the mix.")
|
|
678
|
+
help="Keep only the source's isolated speech in the mix.")
|
|
649
679
|
p_v2vsd.add_argument("--no-ducking", dest="no_ducking", action="store_true",
|
|
650
|
-
help="
|
|
680
|
+
help="Combine the voice and the generated bed as a static mix "
|
|
681
|
+
"instead of a dynamic duck. No effect without "
|
|
682
|
+
"--keep-original-sound or --preserve-speech.")
|
|
651
683
|
p_v2vsd.add_argument("--stem", dest="stems", action="append", choices=_SOUND_STEMS,
|
|
652
684
|
default=None, help="Also save an individual stem. Repeatable.")
|
|
653
685
|
p_v2vsd.add_argument("--output", default=None, help="Where to save the combined video.")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|