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.
@@ -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.1.1"
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.5,<0.6"]
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]
@@ -1,3 +1,3 @@
1
- __version__ = "0.1.1"
1
+ __version__ = "0.4.0"
2
2
 
3
3
  __all__ = ["__version__"]