sonilo-cli 0.1.1__tar.gz → 0.4.0__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.4.0/PKG-INFO +153 -0
- sonilo_cli-0.4.0/README.md +137 -0
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/pyproject.toml +2 -2
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/src/sonilo_cli/__init__.py +1 -1
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/src/sonilo_cli/__main__.py +255 -6
- sonilo_cli-0.4.0/tests/test_cli.py +787 -0
- sonilo_cli-0.4.0/tests/test_context7.py +51 -0
- sonilo_cli-0.1.1/PKG-INFO +0 -87
- sonilo_cli-0.1.1/README.md +0 -71
- sonilo_cli-0.1.1/tests/test_cli.py +0 -384
- sonilo_cli-0.1.1/tests/test_context7.py +0 -12
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/.gitignore +0 -0
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/LICENSE +0 -0
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/tests/__init__.py +0 -0
- {sonilo_cli-0.1.1 → sonilo_cli-0.4.0}/tests/test_smoke.py +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sonilo-cli
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Command-line interface for the Sonilo API: generate music and sound effects from text or video
|
|
5
|
+
Project-URL: Repository, https://github.com/sonilo-ai/sonilo-python
|
|
6
|
+
Author: Sonilo AI
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: ai,cli,music,sfx,sonilo,text-to-music,video-to-music
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Requires-Dist: sonilo<0.8,>=0.7.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
14
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# sonilo-cli
|
|
18
|
+
|
|
19
|
+
Command-line interface for the [Sonilo API](https://github.com/sonilo-ai/sonilo-python) — generate music and sound effects from text or video.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
pip install sonilo-cli
|
|
24
|
+
|
|
25
|
+
## Auth
|
|
26
|
+
|
|
27
|
+
Set your API key once:
|
|
28
|
+
|
|
29
|
+
export SONILO_API_KEY=sk-...
|
|
30
|
+
|
|
31
|
+
or pass `--api-key sk-...` on any command.
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
sonilo account # plan limits and available services
|
|
36
|
+
sonilo usage --days 7 # usage summary
|
|
37
|
+
sonilo text-to-music --prompt "warm lo-fi piano, rain" --duration 30
|
|
38
|
+
sonilo video-to-music --video clip.mp4 --prompt "tense synths" --format wav
|
|
39
|
+
sonilo text-to-sfx --prompt "glass shattering on concrete" --duration 3
|
|
40
|
+
sonilo video-to-sfx --video clip.mp4 --output whoosh.wav
|
|
41
|
+
sonilo video-to-sfx --video clip.mp4 --segments @segments.json
|
|
42
|
+
sonilo video-to-sound --video clip.mp4 \
|
|
43
|
+
--music-prompt "uplifting orchestral score" --sfx-prompt "match the on-screen action"
|
|
44
|
+
sonilo video-to-video-sound --video clip.mp4 --music-prompt "tense synths"
|
|
45
|
+
sonilo dubbing --video-url https://example.com/clip.mp4 --languages es,fr --output dubbed.mp4
|
|
46
|
+
# writes dubbed.es.mp4 and dubbed.fr.mp4
|
|
47
|
+
sonilo tasks get <task-id>
|
|
48
|
+
sonilo tasks wait <task-id> --poll-interval 2 --timeout 600
|
|
49
|
+
|
|
50
|
+
### Notes
|
|
51
|
+
|
|
52
|
+
- `text-to-music` / `video-to-music` stream a short `.m4a` by default. `--format wav`,
|
|
53
|
+
`--isolate-vocals`, and `--preserve-speech` each switch to the async submit-and-poll path.
|
|
54
|
+
- `text-to-sfx` / `video-to-sfx` are always async; `--format` accepts `wav|mp3|aac|flac`.
|
|
55
|
+
- Output defaults to `./output.<ext>`; override with `--output`.
|
|
56
|
+
|
|
57
|
+
### Segments
|
|
58
|
+
|
|
59
|
+
`--segments` scores a timeline instead of one whole-clip prompt. It takes a JSON array, in one of
|
|
60
|
+
three forms — inline, from a file, or from stdin:
|
|
61
|
+
|
|
62
|
+
sonilo text-to-music --prompt "warm lo-fi piano" --duration 30 \
|
|
63
|
+
--segments '[{"start":0,"label":"intro","prompt":"airy pads"}]'
|
|
64
|
+
sonilo video-to-sfx --video clip.mp4 --segments @segments.json
|
|
65
|
+
jq -c '.cues' storyboard.json | sonilo video-to-sfx --video clip.mp4 --segments @-
|
|
66
|
+
|
|
67
|
+
A value starting with `@` names a source to read the JSON from, and `@-` reads standard input — the
|
|
68
|
+
same convention as `curl`, `gh` and `aws`. Anything else is parsed as JSON directly.
|
|
69
|
+
|
|
70
|
+
The two segment shapes are **not** interchangeable:
|
|
71
|
+
|
|
72
|
+
| Shape | Commands | Fields |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| Music | `text-to-music`, `video-to-music` | `{start, prompt, label?}` |
|
|
75
|
+
| SFX | `video-to-sfx`, `video-to-sound`, `video-to-video-sound` | `{start, end, prompt}` |
|
|
76
|
+
|
|
77
|
+
- `start` / `end` are seconds from the start of the track or clip.
|
|
78
|
+
- Passing one shape to a command that takes the other is rejected before any request is made, with
|
|
79
|
+
a message naming the shape that command expects.
|
|
80
|
+
- Only the shape is checked locally. Timing rules — the first segment starting at 0, minimum
|
|
81
|
+
spacing between segments, the `label` vocabulary, how many segments are allowed — are enforced by
|
|
82
|
+
the API, which answers with a `422` describing what it rejected.
|
|
83
|
+
- Keys the CLI does not recognise are forwarded as-is, so a newly added API field works without
|
|
84
|
+
upgrading the CLI.
|
|
85
|
+
- `text-to-sfx` takes no segments (its output is a single effect, not a timeline).
|
|
86
|
+
|
|
87
|
+
### Combined soundtracks
|
|
88
|
+
|
|
89
|
+
`video-to-sound` and `video-to-video-sound` score a clip with a music bed *and* sound effects in one
|
|
90
|
+
call (one charge, instead of chaining two requests). Both are async-only and take the same options —
|
|
91
|
+
they differ only in what comes back: `video-to-sound` writes the mixed **audio** (default
|
|
92
|
+
`output.wav`), `video-to-video-sound` writes the **source video with that audio muxed in** (default
|
|
93
|
+
`output.mp4`).
|
|
94
|
+
|
|
95
|
+
sonilo video-to-sound --video clip.mp4 \
|
|
96
|
+
--music-prompt "uplifting orchestral score" \
|
|
97
|
+
--sfx-prompt "match the on-screen action" \
|
|
98
|
+
--output soundtrack.wav --stem music --stem sfx
|
|
99
|
+
|
|
100
|
+
- `--music-prompt` / `--sfx-prompt` steer the two layers separately; both are optional.
|
|
101
|
+
- `--segments` places individual effects on the timeline, in the SFX shape `{start, end, prompt}` —
|
|
102
|
+
see [Segments](#segments).
|
|
103
|
+
- `--preserve-speech` keeps speech from the source video in the mix.
|
|
104
|
+
- **Ducking is on by default** (music dips under speech). Pass `--no-ducking` to opt out — omitting
|
|
105
|
+
the flag leaves the server default untouched.
|
|
106
|
+
- `--stem` is repeatable (`music`, `music_processed`, `sfx`) and saves the individual layers next to
|
|
107
|
+
the combined output, so you can re-balance the mix yourself. With `--output soundtrack.wav`, the
|
|
108
|
+
music stem lands at `soundtrack.music.m4a`. `music_processed` exists only when `--preserve-speech`
|
|
109
|
+
or ducking altered the music bed.
|
|
110
|
+
|
|
111
|
+
### Dubbing
|
|
112
|
+
|
|
113
|
+
`dubbing` dubs a video into one or more target languages in a single async call:
|
|
114
|
+
|
|
115
|
+
sonilo dubbing --video-url https://example.com/clip.mp4 --languages es,fr --output dubbed.mp4
|
|
116
|
+
# writes dubbed.es.mp4 and dubbed.fr.mp4
|
|
117
|
+
|
|
118
|
+
- `--languages` is comma-separated; omit it to use the server default `zh_cn,es,fr`. Supported
|
|
119
|
+
codes: `en, zh_cn, ja, ko, pt, es, de, fr, it, ru`.
|
|
120
|
+
- Source videos may be at most 180 seconds long.
|
|
121
|
+
- `--output` is a filename template, not a single destination: a dubbing task returns one video
|
|
122
|
+
per language, so `--output clip.mp4` writes `clip.es.mp4`, `clip.fr.mp4`, etc.
|
|
123
|
+
- Billing is per language, and dubbing has **no free trial runs** — see [Free trial](#free-trial)
|
|
124
|
+
below.
|
|
125
|
+
- `--timeout` defaults to 7200 seconds, matching the backend's own ceiling for a dubbing job
|
|
126
|
+
(far longer than other commands' default, since dubbing can run well past the usual
|
|
127
|
+
`tasks wait --timeout 600`). If the wait still times out, the task keeps running
|
|
128
|
+
server-side — resume watching it with `sonilo tasks wait <task-id>`.
|
|
129
|
+
|
|
130
|
+
## Free trial
|
|
131
|
+
|
|
132
|
+
Accounts created through self-serve signup start with free runs on most endpoints — no card
|
|
133
|
+
required:
|
|
134
|
+
|
|
135
|
+
| Free runs | Endpoints |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| 2 each | text-to-music, text-to-sfx, audio-ducking |
|
|
138
|
+
| 1 each | video-to-music, video-to-sfx, video-to-video-music, video-to-video-sfx, video-to-sound, video-to-video-sound |
|
|
139
|
+
| 0 | dubbing |
|
|
140
|
+
|
|
141
|
+
Dubbing bills `video duration × number of languages`, so a free run on it would be worth far more
|
|
142
|
+
than a free run on any other endpoint — it has no free allowance and bills from the first call.
|
|
143
|
+
|
|
144
|
+
The table above is the current default. `sonilo account` prints the live numbers: the account JSON
|
|
145
|
+
goes to stdout, and when the account has a free-trial allowance one summary line goes to stderr:
|
|
146
|
+
|
|
147
|
+
Free trial: text-to-music 1/2 left, video-to-music 0/1 left
|
|
148
|
+
|
|
149
|
+
Because the summary is on stderr, `sonilo account | jq .trial` still sees clean JSON.
|
|
150
|
+
|
|
151
|
+
Once an endpoint's free runs are used up, calls to it bill at the normal rate — or, if the account
|
|
152
|
+
has never been funded, fail with `HTTP 402: ... (trial_exhausted)` until a payment method is added.
|
|
153
|
+
That is the one 402 a retry can never fix.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# sonilo-cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for the [Sonilo API](https://github.com/sonilo-ai/sonilo-python) — generate music and sound effects from text or video.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
pip install sonilo-cli
|
|
8
|
+
|
|
9
|
+
## Auth
|
|
10
|
+
|
|
11
|
+
Set your API key once:
|
|
12
|
+
|
|
13
|
+
export SONILO_API_KEY=sk-...
|
|
14
|
+
|
|
15
|
+
or pass `--api-key sk-...` on any command.
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
sonilo account # plan limits and available services
|
|
20
|
+
sonilo usage --days 7 # usage summary
|
|
21
|
+
sonilo text-to-music --prompt "warm lo-fi piano, rain" --duration 30
|
|
22
|
+
sonilo video-to-music --video clip.mp4 --prompt "tense synths" --format wav
|
|
23
|
+
sonilo text-to-sfx --prompt "glass shattering on concrete" --duration 3
|
|
24
|
+
sonilo video-to-sfx --video clip.mp4 --output whoosh.wav
|
|
25
|
+
sonilo video-to-sfx --video clip.mp4 --segments @segments.json
|
|
26
|
+
sonilo video-to-sound --video clip.mp4 \
|
|
27
|
+
--music-prompt "uplifting orchestral score" --sfx-prompt "match the on-screen action"
|
|
28
|
+
sonilo video-to-video-sound --video clip.mp4 --music-prompt "tense synths"
|
|
29
|
+
sonilo dubbing --video-url https://example.com/clip.mp4 --languages es,fr --output dubbed.mp4
|
|
30
|
+
# writes dubbed.es.mp4 and dubbed.fr.mp4
|
|
31
|
+
sonilo tasks get <task-id>
|
|
32
|
+
sonilo tasks wait <task-id> --poll-interval 2 --timeout 600
|
|
33
|
+
|
|
34
|
+
### Notes
|
|
35
|
+
|
|
36
|
+
- `text-to-music` / `video-to-music` stream a short `.m4a` by default. `--format wav`,
|
|
37
|
+
`--isolate-vocals`, and `--preserve-speech` each switch to the async submit-and-poll path.
|
|
38
|
+
- `text-to-sfx` / `video-to-sfx` are always async; `--format` accepts `wav|mp3|aac|flac`.
|
|
39
|
+
- Output defaults to `./output.<ext>`; override with `--output`.
|
|
40
|
+
|
|
41
|
+
### Segments
|
|
42
|
+
|
|
43
|
+
`--segments` scores a timeline instead of one whole-clip prompt. It takes a JSON array, in one of
|
|
44
|
+
three forms — inline, from a file, or from stdin:
|
|
45
|
+
|
|
46
|
+
sonilo text-to-music --prompt "warm lo-fi piano" --duration 30 \
|
|
47
|
+
--segments '[{"start":0,"label":"intro","prompt":"airy pads"}]'
|
|
48
|
+
sonilo video-to-sfx --video clip.mp4 --segments @segments.json
|
|
49
|
+
jq -c '.cues' storyboard.json | sonilo video-to-sfx --video clip.mp4 --segments @-
|
|
50
|
+
|
|
51
|
+
A value starting with `@` names a source to read the JSON from, and `@-` reads standard input — the
|
|
52
|
+
same convention as `curl`, `gh` and `aws`. Anything else is parsed as JSON directly.
|
|
53
|
+
|
|
54
|
+
The two segment shapes are **not** interchangeable:
|
|
55
|
+
|
|
56
|
+
| Shape | Commands | Fields |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| Music | `text-to-music`, `video-to-music` | `{start, prompt, label?}` |
|
|
59
|
+
| SFX | `video-to-sfx`, `video-to-sound`, `video-to-video-sound` | `{start, end, prompt}` |
|
|
60
|
+
|
|
61
|
+
- `start` / `end` are seconds from the start of the track or clip.
|
|
62
|
+
- Passing one shape to a command that takes the other is rejected before any request is made, with
|
|
63
|
+
a message naming the shape that command expects.
|
|
64
|
+
- Only the shape is checked locally. Timing rules — the first segment starting at 0, minimum
|
|
65
|
+
spacing between segments, the `label` vocabulary, how many segments are allowed — are enforced by
|
|
66
|
+
the API, which answers with a `422` describing what it rejected.
|
|
67
|
+
- Keys the CLI does not recognise are forwarded as-is, so a newly added API field works without
|
|
68
|
+
upgrading the CLI.
|
|
69
|
+
- `text-to-sfx` takes no segments (its output is a single effect, not a timeline).
|
|
70
|
+
|
|
71
|
+
### Combined soundtracks
|
|
72
|
+
|
|
73
|
+
`video-to-sound` and `video-to-video-sound` score a clip with a music bed *and* sound effects in one
|
|
74
|
+
call (one charge, instead of chaining two requests). Both are async-only and take the same options —
|
|
75
|
+
they differ only in what comes back: `video-to-sound` writes the mixed **audio** (default
|
|
76
|
+
`output.wav`), `video-to-video-sound` writes the **source video with that audio muxed in** (default
|
|
77
|
+
`output.mp4`).
|
|
78
|
+
|
|
79
|
+
sonilo video-to-sound --video clip.mp4 \
|
|
80
|
+
--music-prompt "uplifting orchestral score" \
|
|
81
|
+
--sfx-prompt "match the on-screen action" \
|
|
82
|
+
--output soundtrack.wav --stem music --stem sfx
|
|
83
|
+
|
|
84
|
+
- `--music-prompt` / `--sfx-prompt` steer the two layers separately; both are optional.
|
|
85
|
+
- `--segments` places individual effects on the timeline, in the SFX shape `{start, end, prompt}` —
|
|
86
|
+
see [Segments](#segments).
|
|
87
|
+
- `--preserve-speech` keeps speech from the source video in the mix.
|
|
88
|
+
- **Ducking is on by default** (music dips under speech). Pass `--no-ducking` to opt out — omitting
|
|
89
|
+
the flag leaves the server default untouched.
|
|
90
|
+
- `--stem` is repeatable (`music`, `music_processed`, `sfx`) and saves the individual layers next to
|
|
91
|
+
the combined output, so you can re-balance the mix yourself. With `--output soundtrack.wav`, the
|
|
92
|
+
music stem lands at `soundtrack.music.m4a`. `music_processed` exists only when `--preserve-speech`
|
|
93
|
+
or ducking altered the music bed.
|
|
94
|
+
|
|
95
|
+
### Dubbing
|
|
96
|
+
|
|
97
|
+
`dubbing` dubs a video into one or more target languages in a single async call:
|
|
98
|
+
|
|
99
|
+
sonilo dubbing --video-url https://example.com/clip.mp4 --languages es,fr --output dubbed.mp4
|
|
100
|
+
# writes dubbed.es.mp4 and dubbed.fr.mp4
|
|
101
|
+
|
|
102
|
+
- `--languages` is comma-separated; omit it to use the server default `zh_cn,es,fr`. Supported
|
|
103
|
+
codes: `en, zh_cn, ja, ko, pt, es, de, fr, it, ru`.
|
|
104
|
+
- Source videos may be at most 180 seconds long.
|
|
105
|
+
- `--output` is a filename template, not a single destination: a dubbing task returns one video
|
|
106
|
+
per language, so `--output clip.mp4` writes `clip.es.mp4`, `clip.fr.mp4`, etc.
|
|
107
|
+
- Billing is per language, and dubbing has **no free trial runs** — see [Free trial](#free-trial)
|
|
108
|
+
below.
|
|
109
|
+
- `--timeout` defaults to 7200 seconds, matching the backend's own ceiling for a dubbing job
|
|
110
|
+
(far longer than other commands' default, since dubbing can run well past the usual
|
|
111
|
+
`tasks wait --timeout 600`). If the wait still times out, the task keeps running
|
|
112
|
+
server-side — resume watching it with `sonilo tasks wait <task-id>`.
|
|
113
|
+
|
|
114
|
+
## Free trial
|
|
115
|
+
|
|
116
|
+
Accounts created through self-serve signup start with free runs on most endpoints — no card
|
|
117
|
+
required:
|
|
118
|
+
|
|
119
|
+
| Free runs | Endpoints |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| 2 each | text-to-music, text-to-sfx, audio-ducking |
|
|
122
|
+
| 1 each | video-to-music, video-to-sfx, video-to-video-music, video-to-video-sfx, video-to-sound, video-to-video-sound |
|
|
123
|
+
| 0 | dubbing |
|
|
124
|
+
|
|
125
|
+
Dubbing bills `video duration × number of languages`, so a free run on it would be worth far more
|
|
126
|
+
than a free run on any other endpoint — it has no free allowance and bills from the first call.
|
|
127
|
+
|
|
128
|
+
The table above is the current default. `sonilo account` prints the live numbers: the account JSON
|
|
129
|
+
goes to stdout, and when the account has a free-trial allowance one summary line goes to stderr:
|
|
130
|
+
|
|
131
|
+
Free trial: text-to-music 1/2 left, video-to-music 0/1 left
|
|
132
|
+
|
|
133
|
+
Because the summary is on stderr, `sonilo account | jq .trial` still sees clean JSON.
|
|
134
|
+
|
|
135
|
+
Once an endpoint's free runs are used up, calls to it bill at the normal rate — or, if the account
|
|
136
|
+
has never been funded, fail with `HTTP 402: ... (trial_exhausted)` until a payment method is added.
|
|
137
|
+
That is the one 402 a retry can never fix.
|
|
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sonilo-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
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.7.0,<0.8"]
|
|
14
14
|
keywords = ["sonilo", "cli", "music", "sfx", "text-to-music", "video-to-music", "ai"]
|
|
15
15
|
|
|
16
16
|
[project.urls]
|