@mevdragon/vidfarm-devcli 0.19.1 → 0.20.0
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.
- package/.agents/skills/music/SKILL.md +416 -0
- package/.agents/skills/music/references/api_reference.md +519 -0
- package/.agents/skills/music/references/installation.md +65 -0
- package/.agents/skills/text-to-speech/SKILL.md +226 -0
- package/.agents/skills/text-to-speech/references/installation.md +90 -0
- package/.agents/skills/text-to-speech/references/streaming.md +307 -0
- package/.agents/skills/text-to-speech/references/voice-settings.md +115 -0
- package/.agents/skills/vidfarm-media/SKILL.md +25 -11
- package/.agents/skills/vidfarm-media/references/tts.md +41 -3
- package/SKILL.director.md +31 -13
- package/SKILL.platform.md +2 -2
- package/dist/src/app.js +129 -41
- package/dist/src/cli.js +162 -5
- package/dist/src/config.js +12 -0
- package/dist/src/devcli/clips.js +7 -2
- package/dist/src/domain.js +3 -0
- package/dist/src/editor-chat.js +4 -0
- package/dist/src/primitive-context.js +14 -0
- package/dist/src/primitive-registry.js +140 -18
- package/dist/src/reskin/chat-page.js +1 -1
- package/dist/src/reskin/inpaint-clipper-page.js +446 -205
- package/dist/src/reskin/library-page.js +7 -1
- package/dist/src/reskin/portfolio-page.js +9 -9
- package/dist/src/reskin/settings-page.js +4 -4
- package/dist/src/reskin/theme.js +1 -0
- package/dist/src/services/billing.js +5 -0
- package/dist/src/services/clip-curation/ffmpeg.js +48 -0
- package/dist/src/services/clip-curation/hunt.js +2 -0
- package/dist/src/services/clip-curation/index.js +1 -1
- package/dist/src/services/clip-curation/scan.js +29 -16
- package/dist/src/services/elevenlabs.js +222 -0
- package/dist/src/services/providers.js +216 -2
- package/dist/src/services/swipe-customize.js +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
# Music API Reference
|
|
2
|
+
|
|
3
|
+
All endpoints below default to `music_v1` unless noted for backwards compatibility. Always pass `music_v2` unless the caller explicitly needs the legacy model.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [compose](#compose)
|
|
8
|
+
- [stream](#stream)
|
|
9
|
+
- [composition_plan.create](#composition_plancreate)
|
|
10
|
+
- [compose_detailed](#compose_detailed)
|
|
11
|
+
- [compose_detailed_stream](#compose_detailed_stream)
|
|
12
|
+
- [upload](#upload)
|
|
13
|
+
- [video_to_music](#video_to_music)
|
|
14
|
+
- [Inpainting](#inpainting)
|
|
15
|
+
- [Error Handling](#error-handling)
|
|
16
|
+
|
|
17
|
+
## compose
|
|
18
|
+
|
|
19
|
+
Generate music from a text prompt or composition plan. Returns an audio stream.
|
|
20
|
+
|
|
21
|
+
### Parameters
|
|
22
|
+
|
|
23
|
+
| Parameter | Type | Required | Description |
|
|
24
|
+
|-----------|------|----------|-------------|
|
|
25
|
+
| `prompt` | string | Yes* | Description of desired music |
|
|
26
|
+
| `composition_plan` | object | Yes* | Pre-defined composition plan (alternative to prompt) |
|
|
27
|
+
| `music_length_ms` | integer | No | Duration in milliseconds (3,000–600,000) when using `prompt`; if omitted, the model chooses. Returns an error if a composition plan is also provided. |
|
|
28
|
+
| `model_id` | string | No | Music model. Defaults to `music_v1`. |
|
|
29
|
+
| `force_instrumental` | boolean | No | Guarantee an instrumental output (prompt mode only) |
|
|
30
|
+
| `respect_sections_durations` | boolean | No | Enforce exact `duration_ms` for each composition plan chunk. Ignored if using `music_v2` model. |
|
|
31
|
+
| `output_format` | string | No | Query parameter for output codec/sample-rate/bitrate. `auto` selects `mp3_44100_128` for `music_v1` and `mp3_48000_192` for `music_v2`; high-bitrate MP3 options include `mp3_48000_240` and `mp3_48000_320`. |
|
|
32
|
+
|
|
33
|
+
*Provide either `prompt` or `composition_plan`, not both.
|
|
34
|
+
|
|
35
|
+
### Python
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
audio = client.music.compose(
|
|
39
|
+
prompt="An upbeat electronic track with synth leads",
|
|
40
|
+
music_length_ms=30000,
|
|
41
|
+
model_id="music_v2",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
with open("output.mp3", "wb") as f:
|
|
45
|
+
for chunk in audio:
|
|
46
|
+
f.write(chunk)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### TypeScript
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
const audio = await client.music.compose({
|
|
53
|
+
prompt: "An upbeat electronic track with synth leads",
|
|
54
|
+
musicLengthMs: 30000,
|
|
55
|
+
modelId: "music_v2",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const writeStream = createWriteStream("output.mp3");
|
|
59
|
+
audio.pipe(writeStream);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### With Composition Plan
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
plan = client.music.composition_plan.create(
|
|
66
|
+
prompt="A jazz ballad with piano and saxophone",
|
|
67
|
+
music_length_ms=60000,
|
|
68
|
+
model_id="music_v2",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Modify the plan as needed
|
|
72
|
+
audio = client.music.compose(
|
|
73
|
+
composition_plan=plan,
|
|
74
|
+
model_id="music_v2",
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const plan = await client.music.compositionPlan.create({
|
|
80
|
+
prompt: "A jazz ballad with piano and saxophone",
|
|
81
|
+
musicLengthMs: 60000,
|
|
82
|
+
modelId: "music_v2",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Modify the plan as needed
|
|
86
|
+
const audio = await client.music.compose({
|
|
87
|
+
compositionPlan: plan,
|
|
88
|
+
modelId: "music_v2",
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## stream
|
|
93
|
+
|
|
94
|
+
Stream audio chunks as they are generated. Same parameters as [`compose`](#compose); returns an
|
|
95
|
+
iterable/async-iterable of audio bytes instead of a single response. Available on paid plans only.
|
|
96
|
+
|
|
97
|
+
### Python
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from io import BytesIO
|
|
101
|
+
|
|
102
|
+
stream = client.music.stream(
|
|
103
|
+
prompt="A driving synthwave track with arpeggiated leads",
|
|
104
|
+
music_length_ms=30000,
|
|
105
|
+
model_id="music_v2",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
buffer = BytesIO()
|
|
109
|
+
for chunk in stream:
|
|
110
|
+
if chunk:
|
|
111
|
+
buffer.write(chunk)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### TypeScript
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const stream = await client.music.stream({
|
|
118
|
+
prompt: "A driving synthwave track with arpeggiated leads",
|
|
119
|
+
musicLengthMs: 30000,
|
|
120
|
+
modelId: "music_v2",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const chunks: Buffer[] = [];
|
|
124
|
+
for await (const chunk of stream) {
|
|
125
|
+
chunks.push(chunk);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## composition_plan.create
|
|
130
|
+
|
|
131
|
+
Generate a structured composition plan from a prompt for granular control before generating audio.
|
|
132
|
+
|
|
133
|
+
### Parameters
|
|
134
|
+
|
|
135
|
+
| Parameter | Type | Required | Description |
|
|
136
|
+
|-----------|------|----------|-------------|
|
|
137
|
+
| `prompt` | string | Yes | Music description |
|
|
138
|
+
| `music_length_ms` | integer | Yes | Total duration in milliseconds (3,000–600,000) |
|
|
139
|
+
| `model_id` | string | No | Defaults to `music_v2`. Plan structure differs between models. |
|
|
140
|
+
|
|
141
|
+
### Plan Structure (`music_v2`)
|
|
142
|
+
|
|
143
|
+
A plan is an ordered list of `chunks`. Each chunk is either a **generation chunk** or an
|
|
144
|
+
**audio reference chunk** (see [Inpainting](#inpainting)).
|
|
145
|
+
|
|
146
|
+
| Field | Type | Description |
|
|
147
|
+
|-------|------|-------------|
|
|
148
|
+
| `chunks` | array | Up to 30 chunks; 3 s to 10 min total |
|
|
149
|
+
| `chunks[].text` | string | Section labels (`[Verse 1]`), lyrics, inline cues like `{guitar solo}` |
|
|
150
|
+
| `chunks[].duration_ms` | integer | 3,000–120,000 ms |
|
|
151
|
+
| `chunks[].positive_styles` | array<string> | Up to 50 desired qualities (genre, instrumentation, vocal style). Must be English. |
|
|
152
|
+
| `chunks[].negative_styles` | array<string> | Up to 50 qualities to avoid |
|
|
153
|
+
| `chunks[].context_adherence` | string | `low`, `medium`, or `high` (default). Higher = stays consistent with surrounding chunks. |
|
|
154
|
+
|
|
155
|
+
### Python
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
plan = client.music.composition_plan.create(
|
|
159
|
+
prompt="A peaceful ambient track with nature sounds",
|
|
160
|
+
music_length_ms=60000,
|
|
161
|
+
model_id="music_v2",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Inspect or edit the plan in place
|
|
165
|
+
print(plan["chunks"][0]["positive_styles"])
|
|
166
|
+
plan["chunks"][0]["text"] = "[Intro]\nSoft pad with rain"
|
|
167
|
+
|
|
168
|
+
audio = client.music.compose(composition_plan=plan, model_id="music_v2")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### TypeScript
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const plan = await client.music.compositionPlan.create({
|
|
175
|
+
prompt: "A peaceful ambient track with nature sounds",
|
|
176
|
+
musicLengthMs: 60000,
|
|
177
|
+
modelId: "music_v2",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
plan.chunks[0].text = "[Intro]\nSoft pad with rain";
|
|
181
|
+
const audio = await client.music.compose({ compositionPlan: plan, modelId: "music_v2" });
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## compose_detailed
|
|
185
|
+
|
|
186
|
+
Generate music while returning both the composition plan and metadata alongside the audio.
|
|
187
|
+
|
|
188
|
+
### Parameters
|
|
189
|
+
|
|
190
|
+
| Parameter | Type | Required | Description |
|
|
191
|
+
|-----------|------|----------|-------------|
|
|
192
|
+
| `prompt` | string | Yes* | Description of desired music |
|
|
193
|
+
| `composition_plan` | object | Yes* | Pre-defined composition plan (alternative to `prompt`) |
|
|
194
|
+
| `music_length_ms` | integer | No | Total duration in milliseconds when using `prompt` |
|
|
195
|
+
| `model_id` | string | No | Defaults to `music_v2` |
|
|
196
|
+
| `store_for_inpainting` | boolean | No | If `true`, retains the generated audio under a `song_id` so it can be referenced by later inpainting plans |
|
|
197
|
+
| `force_instrumental` | boolean | No | Guarantee an instrumental output (prompt mode only) |
|
|
198
|
+
| `output_format` | string | No | Query parameter for output codec/sample-rate/bitrate. `auto` selects `mp3_44100_128` for `music_v1` and `mp3_48000_192` for `music_v2`; high-bitrate MP3 options include `mp3_48000_240` and `mp3_48000_320`. |
|
|
199
|
+
|
|
200
|
+
*Provide either `prompt` or `composition_plan`, not both.
|
|
201
|
+
|
|
202
|
+
### Returns
|
|
203
|
+
|
|
204
|
+
| Field | Description |
|
|
205
|
+
|-------|-------------|
|
|
206
|
+
| `json` | Composition plan + song metadata (includes lyrics if applicable) |
|
|
207
|
+
| `filename` | Output file identifier |
|
|
208
|
+
| `audio` | Audio bytes |
|
|
209
|
+
| `song_id` | Identifier for the stored song (only when `store_for_inpainting=True`) |
|
|
210
|
+
|
|
211
|
+
### Python
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
result = client.music.compose_detailed(
|
|
215
|
+
prompt="A pop song about summer adventures",
|
|
216
|
+
music_length_ms=120000,
|
|
217
|
+
model_id="music_v2",
|
|
218
|
+
store_for_inpainting=True,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
print(result.json) # composition plan + metadata
|
|
222
|
+
print(result.song_id) # reusable identifier for inpainting
|
|
223
|
+
|
|
224
|
+
with open(result.filename, "wb") as f:
|
|
225
|
+
f.write(result.audio)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### TypeScript
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { writeFileSync } from "fs";
|
|
232
|
+
|
|
233
|
+
const result = await client.music.composeDetailed({
|
|
234
|
+
prompt: "A pop song about summer adventures",
|
|
235
|
+
musicLengthMs: 120000,
|
|
236
|
+
modelId: "music_v2",
|
|
237
|
+
storeForInpainting: true,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
console.log(result.json); // composition plan + metadata
|
|
241
|
+
console.log(result.songId); // reusable identifier for inpainting
|
|
242
|
+
|
|
243
|
+
writeFileSync(result.filename, result.audio);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## compose_detailed_stream
|
|
247
|
+
|
|
248
|
+
Stream music generation details as Server-Sent Events from
|
|
249
|
+
[`POST /v1/music/detailed/stream`](https://elevenlabs.io/docs/api-reference/music/compose-detailed-stream).
|
|
250
|
+
Use this when a client should receive the composition plan, song metadata, audio chunks, optional
|
|
251
|
+
word timestamps, and completion signal incrementally.
|
|
252
|
+
|
|
253
|
+
### Parameters
|
|
254
|
+
|
|
255
|
+
| Parameter | Type | Required | Description |
|
|
256
|
+
|-----------|------|----------|-------------|
|
|
257
|
+
| `prompt` | string | Yes* | Description of desired music |
|
|
258
|
+
| `composition_plan` | object | Yes* | Pre-defined composition plan (alternative to `prompt`) |
|
|
259
|
+
| `music_length_ms` | integer | No | Duration in milliseconds when using `prompt` |
|
|
260
|
+
| `model_id` | string | No | Defaults to `music_v1`; pass `music_v2` for the current model |
|
|
261
|
+
| `seed` | integer | No | Random seed for more consistent generation; cannot be used with `prompt` |
|
|
262
|
+
| `force_instrumental` | boolean | No | Guarantee an instrumental output (prompt mode only) |
|
|
263
|
+
| `store_for_inpainting` | boolean | No | Store the generated song so it can be referenced by later inpainting plans |
|
|
264
|
+
| `with_timestamps` | boolean | No | Include word timestamps in the streamed events |
|
|
265
|
+
| `output_format` | string | No | Query parameter for output codec/sample-rate/bitrate. `auto` selects `mp3_44100_128` for `music_v1` and `mp3_48000_192` for `music_v2`. |
|
|
266
|
+
|
|
267
|
+
*Provide either `prompt` or `composition_plan`, not both.
|
|
268
|
+
|
|
269
|
+
### cURL
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
curl -N -X POST "https://api.elevenlabs.io/v1/music/detailed/stream?output_format=auto" \
|
|
273
|
+
-H "xi-api-key: $ELEVENLABS_API_KEY" \
|
|
274
|
+
-H "Content-Type: application/json" \
|
|
275
|
+
-d '{"prompt": "A bright indie pop hook with warm guitars", "music_length_ms": 30000, "model_id": "music_v2", "with_timestamps": true}'
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## upload
|
|
279
|
+
|
|
280
|
+
Upload a music file for later inpainting workflows. This endpoint is available to enterprise clients with access to the inpainting feature.
|
|
281
|
+
|
|
282
|
+
### Parameters
|
|
283
|
+
|
|
284
|
+
| Parameter | Type | Required | Description |
|
|
285
|
+
|-----------|------|----------|-------------|
|
|
286
|
+
| `file` | file | Yes | The audio file to upload |
|
|
287
|
+
| `extract_composition_plan` | boolean \| string | No | If `true` (or a model id such as `"music_v2"`), the response includes an extracted composition plan; passing a model id chooses the extraction model. The request may take longer to return. |
|
|
288
|
+
| `with_timestamps` | boolean | No | Transcribe the uploaded song and include word-level timestamps in the response. The request may take longer to return. |
|
|
289
|
+
|
|
290
|
+
### Returns
|
|
291
|
+
|
|
292
|
+
| Field | Description |
|
|
293
|
+
|-------|-------------|
|
|
294
|
+
| `song_id` | Unique identifier for the uploaded song |
|
|
295
|
+
| `composition_plan` | Extracted composition plan, or `null` when `extract_composition_plan` is not enabled |
|
|
296
|
+
| `words_timestamps` | Word-level timestamps when `with_timestamps` is enabled |
|
|
297
|
+
|
|
298
|
+
### Python
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
response = client.music.upload(
|
|
302
|
+
file=open("my-song.mp3", "rb"),
|
|
303
|
+
extract_composition_plan="music_v2",
|
|
304
|
+
)
|
|
305
|
+
song_id = response.song_id
|
|
306
|
+
composition_plan = response.composition_plan
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### TypeScript
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { createReadStream } from "fs";
|
|
313
|
+
|
|
314
|
+
const response = await client.music.upload({
|
|
315
|
+
file: createReadStream("my-song.mp3"),
|
|
316
|
+
extractCompositionPlan: "music_v2",
|
|
317
|
+
});
|
|
318
|
+
const songId = response.songId;
|
|
319
|
+
const compositionPlan = response.compositionPlan;
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### cURL
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
curl -X POST "https://api.elevenlabs.io/v1/music/upload" \
|
|
326
|
+
-H "xi-api-key: $ELEVENLABS_API_KEY" \
|
|
327
|
+
-F "file=@<file1>" \
|
|
328
|
+
-F "extract_composition_plan=music_v2"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## video_to_music
|
|
332
|
+
|
|
333
|
+
Generate background music from uploaded video clips. This is a **separate endpoint** from
|
|
334
|
+
[`compose`](#compose) (`POST /v1/music/video-to-music`, not `POST /v1/music`). See the
|
|
335
|
+
[Video to music API reference](https://elevenlabs.io/docs/api-reference/music/video-to-music).
|
|
336
|
+
|
|
337
|
+
Videos are combined in order before music generation.
|
|
338
|
+
|
|
339
|
+
### Parameters
|
|
340
|
+
|
|
341
|
+
| Parameter | Type | Required | Description |
|
|
342
|
+
|-----------|------|----------|-------------|
|
|
343
|
+
| `videos` | array of files | Yes | One or more video files. Up to 10 files, 200MB combined size, and 600 seconds total duration. |
|
|
344
|
+
| `description` | string | No | Optional text prompt describing the desired music (up to 1000 characters). |
|
|
345
|
+
| `tags` | array of strings | No | Optional style tags such as `upbeat` or `cinematic` (up to 10 tags). |
|
|
346
|
+
| `model_id` | string | No | Music model to use. Defaults to `music_v1` on this endpoint — pass `music_v2` to opt in. |
|
|
347
|
+
| `sign_with_c2pa` | boolean | No | Sign generated MP3 output with C2PA metadata. Defaults to `false`. |
|
|
348
|
+
| `output_format` | string | No | Output codec/sample-rate/bitrate, such as `mp3_44100_128`, `pcm_44100`, or `opus_48000_96`. |
|
|
349
|
+
|
|
350
|
+
### Python
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
audio = client.music.video_to_music(
|
|
354
|
+
videos=[open("scene-1.mp4", "rb"), open("scene-2.mp4", "rb")],
|
|
355
|
+
description="Cinematic ambient score with a gentle build",
|
|
356
|
+
tags=["cinematic", "ambient"],
|
|
357
|
+
model_id="music_v2",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
with open("video-score.mp3", "wb") as f:
|
|
361
|
+
for chunk in audio:
|
|
362
|
+
f.write(chunk)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### TypeScript
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import { createReadStream, createWriteStream } from "fs";
|
|
369
|
+
|
|
370
|
+
const audio = await client.music.videoToMusic({
|
|
371
|
+
videos: [createReadStream("scene-1.mp4"), createReadStream("scene-2.mp4")],
|
|
372
|
+
description: "Cinematic ambient score with a gentle build",
|
|
373
|
+
tags: ["cinematic", "ambient"],
|
|
374
|
+
modelId: "music_v2",
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
audio.pipe(createWriteStream("video-score.mp3"));
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### cURL
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
curl -X POST "https://api.elevenlabs.io/v1/music/video-to-music?output_format=mp3_44100_128" \
|
|
384
|
+
-H "xi-api-key: $ELEVENLABS_API_KEY" \
|
|
385
|
+
-F "videos=@scene-1.mp4" \
|
|
386
|
+
-F "videos=@scene-2.mp4" \
|
|
387
|
+
-F "description=Cinematic ambient score with a gentle build" \
|
|
388
|
+
-F "tags=cinematic" \
|
|
389
|
+
-F "tags=ambient" \
|
|
390
|
+
--output video-score.mp3
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Inpainting
|
|
394
|
+
|
|
395
|
+
Inpainting edits or extends a stored song by combining **audio reference chunks** (unchanged
|
|
396
|
+
slices of stored audio) with **generation chunks** in a single `music_v2` composition plan.
|
|
397
|
+
Available to enterprise clients.
|
|
398
|
+
|
|
399
|
+
### Getting a `song_id`
|
|
400
|
+
|
|
401
|
+
- Call [`compose_detailed`](#compose_detailed) or [`compose`](#compose) with `store_for_inpainting=True` to keep a
|
|
402
|
+
generation for later editing.
|
|
403
|
+
- Call [`upload`](#upload) with `extract_composition_plan="music_v2"` to import an existing
|
|
404
|
+
track and recover its plan.
|
|
405
|
+
|
|
406
|
+
### Chunk Types
|
|
407
|
+
|
|
408
|
+
**Audio reference chunk** — replay a slice of a stored song unchanged:
|
|
409
|
+
|
|
410
|
+
| Field | Type | Description |
|
|
411
|
+
|-------|------|-------------|
|
|
412
|
+
| `song_id` | string | Stored song identifier |
|
|
413
|
+
| `range.start_ms` | integer | Start offset (minimum 50 ms range) |
|
|
414
|
+
| `range.end_ms` | integer | End offset |
|
|
415
|
+
|
|
416
|
+
**Generation chunk** — same fields as a normal `music_v2` plan chunk (`text`,
|
|
417
|
+
`duration_ms`, `positive_styles`, `negative_styles`, `context_adherence`), with two optional
|
|
418
|
+
fields for matching the feel of an existing slice:
|
|
419
|
+
|
|
420
|
+
| Field | Type | Description |
|
|
421
|
+
|-------|------|-------------|
|
|
422
|
+
| `conditioning_ref.song_id` | string | Song to condition on |
|
|
423
|
+
| `conditioning_ref.range` | object | `start_ms`/`end_ms`; up to 30,000 ms |
|
|
424
|
+
| `condition_strength` | string | `low`, `medium` (default), `high`, or `xhigh` |
|
|
425
|
+
|
|
426
|
+
`conditioning_ref` on the first chunk influences every subsequent chunk in the plan.
|
|
427
|
+
|
|
428
|
+
### Constraints
|
|
429
|
+
|
|
430
|
+
| Constraint | Limit |
|
|
431
|
+
|------------|-------|
|
|
432
|
+
| Chunks per plan | 30 |
|
|
433
|
+
| Chunk duration | 3,000–120,000 ms |
|
|
434
|
+
| Conditioning reference duration | up to 30,000 ms |
|
|
435
|
+
| Minimum time range | 50 ms |
|
|
436
|
+
|
|
437
|
+
### Python
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
plan = {
|
|
441
|
+
"chunks": [
|
|
442
|
+
{"song_id": song_id, "range": {"start_ms": 0, "end_ms": 30000}},
|
|
443
|
+
{
|
|
444
|
+
"text": "[Chorus]\nWe're rising up tonight",
|
|
445
|
+
"duration_ms": 30000,
|
|
446
|
+
"positive_styles": ["bigger drums", "layered vocals", "anthemic"],
|
|
447
|
+
"negative_styles": ["sparse"],
|
|
448
|
+
"context_adherence": "high",
|
|
449
|
+
"conditioning_ref": {
|
|
450
|
+
"song_id": song_id,
|
|
451
|
+
"range": {"start_ms": 30000, "end_ms": 45000},
|
|
452
|
+
},
|
|
453
|
+
"condition_strength": "high",
|
|
454
|
+
},
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
audio = client.music.compose(composition_plan=plan, model_id="music_v2")
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### TypeScript
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
const plan = {
|
|
465
|
+
chunks: [
|
|
466
|
+
{ songId, range: { startMs: 0, endMs: 30000 } },
|
|
467
|
+
{
|
|
468
|
+
text: "[Chorus]\nWe're rising up tonight",
|
|
469
|
+
durationMs: 30000,
|
|
470
|
+
positiveStyles: ["bigger drums", "layered vocals", "anthemic"],
|
|
471
|
+
negativeStyles: ["sparse"],
|
|
472
|
+
contextAdherence: "high",
|
|
473
|
+
conditioningRef: { songId, range: { startMs: 30000, endMs: 45000 } },
|
|
474
|
+
conditionStrength: "high",
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
const audio = await client.music.compose({ compositionPlan: plan, modelId: "music_v2" });
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Error Handling
|
|
483
|
+
|
|
484
|
+
### bad_prompt
|
|
485
|
+
|
|
486
|
+
Occurs when the prompt references copyrighted material (specific artists, bands, or copyrighted lyrics). The error response includes a `prompt_suggestion` with alternative phrasing.
|
|
487
|
+
|
|
488
|
+
```python
|
|
489
|
+
try:
|
|
490
|
+
audio = client.music.compose(
|
|
491
|
+
prompt="A song like Beatles",
|
|
492
|
+
music_length_ms=30000
|
|
493
|
+
)
|
|
494
|
+
except Exception as e:
|
|
495
|
+
print(f"Request failed: {e}")
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
try {
|
|
500
|
+
const audio = await client.music.compose({
|
|
501
|
+
prompt: "A song like Beatles",
|
|
502
|
+
musicLengthMs: 30000,
|
|
503
|
+
});
|
|
504
|
+
} catch (err) {
|
|
505
|
+
console.error("Request failed:", err);
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### bad_composition_plan
|
|
510
|
+
|
|
511
|
+
Returned when a composition plan contains copyrighted styles. The error includes a `composition_plan_suggestion` with corrected styles. No suggestion is provided for harmful content.
|
|
512
|
+
|
|
513
|
+
### Common HTTP Errors
|
|
514
|
+
|
|
515
|
+
| Code | Meaning |
|
|
516
|
+
|------|---------|
|
|
517
|
+
| 401 | Invalid API key |
|
|
518
|
+
| 422 | Invalid parameters |
|
|
519
|
+
| 429 | Rate limit exceeded |
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
## JavaScript / TypeScript
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @elevenlabs/elevenlabs-js
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
> **Important:** Always use `@elevenlabs/elevenlabs-js`. The old `elevenlabs` npm package (v1.x) is deprecated and should not be used.
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
|
|
13
|
+
|
|
14
|
+
// Option 1: Environment variable (recommended)
|
|
15
|
+
// Set ELEVENLABS_API_KEY in your environment
|
|
16
|
+
const client = new ElevenLabsClient();
|
|
17
|
+
|
|
18
|
+
// Option 2: Pass directly
|
|
19
|
+
const client = new ElevenLabsClient({ apiKey: "your-api-key" });
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Python
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install elevenlabs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from elevenlabs import ElevenLabs
|
|
30
|
+
|
|
31
|
+
# Option 1: Environment variable (recommended)
|
|
32
|
+
# Set ELEVENLABS_API_KEY in your environment
|
|
33
|
+
client = ElevenLabs()
|
|
34
|
+
|
|
35
|
+
# Option 2: Pass directly
|
|
36
|
+
client = ElevenLabs(api_key="your-api-key")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## cURL / REST API
|
|
40
|
+
|
|
41
|
+
Set your API key as an environment variable:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export ELEVENLABS_API_KEY="your-api-key"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Include in requests via the `xi-api-key` header:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -X POST "https://api.elevenlabs.io/v1/music" \
|
|
51
|
+
-H "xi-api-key: $ELEVENLABS_API_KEY" \
|
|
52
|
+
-H "Content-Type: application/json" \
|
|
53
|
+
-d '{"prompt": "A chill lo-fi beat", "music_length_ms": 30000}'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Getting an API Key
|
|
57
|
+
|
|
58
|
+
1. Sign up at [elevenlabs.io](https://elevenlabs.io)
|
|
59
|
+
2. Go to [API Keys](https://elevenlabs.io/app/settings/api-keys)
|
|
60
|
+
3. Click **Create API Key**
|
|
61
|
+
4. Copy and store securely
|
|
62
|
+
|
|
63
|
+
Or use the `setup-api-key` skill for guided setup.
|
|
64
|
+
|
|
65
|
+
**Note:** Music generation requires a paid ElevenLabs plan.
|