songbrain 0.1.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/LICENSE +21 -0
- package/README.md +121 -0
- package/dist/index.cjs +397 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +570 -0
- package/dist/index.d.ts +570 -0
- package/dist/index.js +382 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response types of the Songbrain API, schema `songbrain.song/1`.
|
|
3
|
+
*
|
|
4
|
+
* Based on a real response from https://api.songbrain.ai/v1/examples/old-truck-home.
|
|
5
|
+
* New fields can appear at any time; existing fields don't change meaning.
|
|
6
|
+
* All times are seconds from the start of the song unless the name says
|
|
7
|
+
* otherwise (`start_in_clip`).
|
|
8
|
+
*/
|
|
9
|
+
type SongStatus = "processing" | "done" | "failed";
|
|
10
|
+
/** One of the sections a song document has. Use with `include`. */
|
|
11
|
+
type SectionName = "song_dna" | "timeline" | "best_moments" | "lyrics" | "scores" | "story" | "shot_plan";
|
|
12
|
+
interface Billing {
|
|
13
|
+
/** "free" or "credits". */
|
|
14
|
+
type: "free" | "credits" | (string & {});
|
|
15
|
+
credits: number;
|
|
16
|
+
}
|
|
17
|
+
interface SongInfo {
|
|
18
|
+
title: string | null;
|
|
19
|
+
artist: string | null;
|
|
20
|
+
duration_sec: number | null;
|
|
21
|
+
language: string | null;
|
|
22
|
+
has_vocals: boolean | null;
|
|
23
|
+
}
|
|
24
|
+
interface KeyChange {
|
|
25
|
+
at_sec: number;
|
|
26
|
+
to: string;
|
|
27
|
+
}
|
|
28
|
+
interface Instrument {
|
|
29
|
+
name: string;
|
|
30
|
+
confidence: number;
|
|
31
|
+
}
|
|
32
|
+
interface LoudnessCheck {
|
|
33
|
+
loudness_ok: boolean;
|
|
34
|
+
delta_lufs: number;
|
|
35
|
+
}
|
|
36
|
+
interface SoundsLike {
|
|
37
|
+
artist: string;
|
|
38
|
+
track: string;
|
|
39
|
+
}
|
|
40
|
+
interface SongDNA {
|
|
41
|
+
genre: string;
|
|
42
|
+
/** One of 37 subgenres. */
|
|
43
|
+
subgenre: string;
|
|
44
|
+
genre_confidence: number;
|
|
45
|
+
style?: string | null;
|
|
46
|
+
tempo_bpm: number;
|
|
47
|
+
tempo_confidence: number;
|
|
48
|
+
/** e.g. "C minor". */
|
|
49
|
+
key: string;
|
|
50
|
+
key_confidence: number;
|
|
51
|
+
key_changes: KeyChange[];
|
|
52
|
+
/** e.g. ["epic", "powerful", "melancholic"]. */
|
|
53
|
+
mood: string[];
|
|
54
|
+
/** Vocal style, or "instrumental". */
|
|
55
|
+
vocals: string;
|
|
56
|
+
/** Instruments with confidence >= 0.25. */
|
|
57
|
+
instruments: Instrument[];
|
|
58
|
+
/** 0-1. */
|
|
59
|
+
energy: number;
|
|
60
|
+
/** 0-1. */
|
|
61
|
+
brightness: number;
|
|
62
|
+
/** Integrated loudness (EBU R128). */
|
|
63
|
+
loudness_lufs: number;
|
|
64
|
+
true_peak_dbfs: number;
|
|
65
|
+
/** Per platform: spotify, youtube, apple. */
|
|
66
|
+
streaming_loudness: Record<string, LoudnessCheck>;
|
|
67
|
+
tagline?: string | null;
|
|
68
|
+
description?: string | null;
|
|
69
|
+
sounds_like?: SoundsLike[];
|
|
70
|
+
}
|
|
71
|
+
interface Section {
|
|
72
|
+
start_sec: number;
|
|
73
|
+
end_sec: number;
|
|
74
|
+
/** Same letter = same music, e.g. A, B, A. */
|
|
75
|
+
label: string;
|
|
76
|
+
/** 0-1, relative to the song's loudest part. */
|
|
77
|
+
energy: number;
|
|
78
|
+
}
|
|
79
|
+
interface VocalChange {
|
|
80
|
+
at_sec: number;
|
|
81
|
+
label: string;
|
|
82
|
+
}
|
|
83
|
+
interface LyricHook {
|
|
84
|
+
at_sec: number;
|
|
85
|
+
line: string;
|
|
86
|
+
role: string;
|
|
87
|
+
strength: number;
|
|
88
|
+
}
|
|
89
|
+
interface Timeline {
|
|
90
|
+
/** Every beat in seconds. In view=summary this is a short placeholder string. */
|
|
91
|
+
beats: number[] | string;
|
|
92
|
+
/** First beat of each bar. In view=summary this is a short placeholder string. */
|
|
93
|
+
downbeats: number[] | string;
|
|
94
|
+
time_signature: string;
|
|
95
|
+
sections: Section[];
|
|
96
|
+
vocal_timeline: VocalChange[];
|
|
97
|
+
lyric_hooks: LyricHook[] | null;
|
|
98
|
+
}
|
|
99
|
+
interface PlatformFit {
|
|
100
|
+
tiktok: number;
|
|
101
|
+
instagram_reels: number;
|
|
102
|
+
youtube_shorts: number;
|
|
103
|
+
}
|
|
104
|
+
interface CaptionIdeas {
|
|
105
|
+
curiosity?: string;
|
|
106
|
+
hype?: string;
|
|
107
|
+
storytelling?: string;
|
|
108
|
+
}
|
|
109
|
+
interface BestMoment {
|
|
110
|
+
/** 1 = strongest. */
|
|
111
|
+
rank: number;
|
|
112
|
+
start_sec: number;
|
|
113
|
+
peak_sec: number;
|
|
114
|
+
end_sec: number;
|
|
115
|
+
duration_sec: number;
|
|
116
|
+
kind: string;
|
|
117
|
+
/** 0-100. */
|
|
118
|
+
score: number;
|
|
119
|
+
why: string;
|
|
120
|
+
explanation: string;
|
|
121
|
+
judge_note: string;
|
|
122
|
+
/** The words sung inside the window. */
|
|
123
|
+
sung: string | null;
|
|
124
|
+
platform_fit: PlatformFit;
|
|
125
|
+
/** Beats inside the window. Omitted in view=summary. */
|
|
126
|
+
beat_grid_sec?: number[];
|
|
127
|
+
caption_ideas: CaptionIdeas | null;
|
|
128
|
+
hashtags: string[] | null;
|
|
129
|
+
}
|
|
130
|
+
interface Word {
|
|
131
|
+
w: string;
|
|
132
|
+
start: number;
|
|
133
|
+
end: number;
|
|
134
|
+
}
|
|
135
|
+
interface LyricLine {
|
|
136
|
+
start: number;
|
|
137
|
+
end: number;
|
|
138
|
+
text: string;
|
|
139
|
+
/** Omitted in view=summary. */
|
|
140
|
+
words?: Word[];
|
|
141
|
+
}
|
|
142
|
+
interface Lyrics {
|
|
143
|
+
language?: string | null;
|
|
144
|
+
lines?: LyricLine[];
|
|
145
|
+
/** true for recognised commercial recordings; then `lines` is absent. */
|
|
146
|
+
hidden?: boolean;
|
|
147
|
+
reason?: string;
|
|
148
|
+
}
|
|
149
|
+
interface ScoreBlock {
|
|
150
|
+
/** 0-100. */
|
|
151
|
+
score: number;
|
|
152
|
+
breakdown: Record<string, number>;
|
|
153
|
+
note?: string;
|
|
154
|
+
}
|
|
155
|
+
interface WhatWorks {
|
|
156
|
+
title: string;
|
|
157
|
+
detail: string;
|
|
158
|
+
}
|
|
159
|
+
interface WhatToFix {
|
|
160
|
+
title: string;
|
|
161
|
+
problem: string;
|
|
162
|
+
fix: string;
|
|
163
|
+
}
|
|
164
|
+
interface Scores {
|
|
165
|
+
/** Song strength for short-form video. Not a view prediction. */
|
|
166
|
+
virality: ScoreBlock;
|
|
167
|
+
quality: ScoreBlock;
|
|
168
|
+
lyrics: ScoreBlock;
|
|
169
|
+
summary: string;
|
|
170
|
+
what_works: WhatWorks[];
|
|
171
|
+
what_to_fix: WhatToFix[];
|
|
172
|
+
audience: string[];
|
|
173
|
+
}
|
|
174
|
+
interface StoryElement {
|
|
175
|
+
/** The one object the video is about. */
|
|
176
|
+
thing: string;
|
|
177
|
+
before: string;
|
|
178
|
+
event: string;
|
|
179
|
+
after: string;
|
|
180
|
+
}
|
|
181
|
+
interface StoryBeats {
|
|
182
|
+
setup: string;
|
|
183
|
+
turn: string;
|
|
184
|
+
payoff: string;
|
|
185
|
+
}
|
|
186
|
+
interface Story {
|
|
187
|
+
meaning: string;
|
|
188
|
+
why_these_visuals: string;
|
|
189
|
+
confidence: number;
|
|
190
|
+
/** One place for every shot. */
|
|
191
|
+
world: string;
|
|
192
|
+
protagonist: string | null;
|
|
193
|
+
carrier: string;
|
|
194
|
+
recurring_presence: string | null;
|
|
195
|
+
props: string[];
|
|
196
|
+
lyric_evidence: string[];
|
|
197
|
+
/** Three colours for every shot. */
|
|
198
|
+
palette: string[];
|
|
199
|
+
element: StoryElement;
|
|
200
|
+
story_beats: StoryBeats;
|
|
201
|
+
}
|
|
202
|
+
interface ShotStyle {
|
|
203
|
+
palette: string[] | null;
|
|
204
|
+
/** Append to your own prompts to stay in the look. */
|
|
205
|
+
prompt_suffix: string | null;
|
|
206
|
+
world: string | null;
|
|
207
|
+
}
|
|
208
|
+
interface Phases {
|
|
209
|
+
tease_end?: number;
|
|
210
|
+
payoff_start?: number;
|
|
211
|
+
payoff_end?: number;
|
|
212
|
+
outro_start?: number;
|
|
213
|
+
}
|
|
214
|
+
type SceneKind = "tease" | "build" | "cutaway" | "burst" | "strobe" | "hero" | "outro" | (string & {});
|
|
215
|
+
type Act = "setup" | "turn" | "payoff" | (string & {});
|
|
216
|
+
interface Scene {
|
|
217
|
+
index: number;
|
|
218
|
+
/** tease, build, cutaway, burst, strobe (about 4 frames), hero, outro. */
|
|
219
|
+
kind: SceneKind;
|
|
220
|
+
act: Act;
|
|
221
|
+
role: string;
|
|
222
|
+
/** Absolute seconds in the song. Cuts sit on beats. */
|
|
223
|
+
start_sec: number;
|
|
224
|
+
end_sec: number;
|
|
225
|
+
/** Relative to clip.window_sec[0]. */
|
|
226
|
+
start_in_clip: number;
|
|
227
|
+
duration_sec: number;
|
|
228
|
+
beats_in_scene: number;
|
|
229
|
+
transition_in: string;
|
|
230
|
+
motion: string;
|
|
231
|
+
/** A ready image/video prompt with world, palette and style. */
|
|
232
|
+
prompt: string | null;
|
|
233
|
+
/** The readable part of the prompt, without the style tail. */
|
|
234
|
+
visual: string | null;
|
|
235
|
+
framing: string | null;
|
|
236
|
+
emotion: string | null;
|
|
237
|
+
symbol: string | null;
|
|
238
|
+
story_beat: string | null;
|
|
239
|
+
/** The words sung during the scene. */
|
|
240
|
+
sung: string | null;
|
|
241
|
+
}
|
|
242
|
+
interface Clip {
|
|
243
|
+
/** [start, end] of the clip in the song. */
|
|
244
|
+
window_sec: [number, number];
|
|
245
|
+
aspect_ratio: string;
|
|
246
|
+
/** Tempo of the cut grid; can run at double time. */
|
|
247
|
+
grid_bpm: number | null;
|
|
248
|
+
/** Beats inside the window. Omitted in view=summary. */
|
|
249
|
+
beats_sec?: number[];
|
|
250
|
+
/** The repeated sung line the payoff lands on. */
|
|
251
|
+
hook_line: string | null;
|
|
252
|
+
phases_sec: Phases;
|
|
253
|
+
scenes: Scene[];
|
|
254
|
+
}
|
|
255
|
+
interface FullSongSection {
|
|
256
|
+
start_sec: number;
|
|
257
|
+
end_sec: number;
|
|
258
|
+
section: string;
|
|
259
|
+
energy: number;
|
|
260
|
+
energy_rank: number;
|
|
261
|
+
act: Act;
|
|
262
|
+
cut_every_beats: number;
|
|
263
|
+
/** Cut points on the song's own beats. Omitted in view=summary. */
|
|
264
|
+
cuts_sec?: number[] | null;
|
|
265
|
+
role: string;
|
|
266
|
+
visual: string | null;
|
|
267
|
+
prompt?: string | null;
|
|
268
|
+
sung: string | null;
|
|
269
|
+
}
|
|
270
|
+
interface FullSong {
|
|
271
|
+
note: string;
|
|
272
|
+
sections: FullSongSection[];
|
|
273
|
+
}
|
|
274
|
+
interface ShotPlan {
|
|
275
|
+
status: "ready" | "pending" | "unavailable" | (string & {});
|
|
276
|
+
/** Set when status is "unavailable". */
|
|
277
|
+
reason?: string;
|
|
278
|
+
engine?: string;
|
|
279
|
+
style?: ShotStyle;
|
|
280
|
+
/** A beat-synced edit of the best moment (about 15 s, 9:16). */
|
|
281
|
+
clip?: Clip;
|
|
282
|
+
/** Section-level plan for a full-length video. */
|
|
283
|
+
full_song?: FullSong | null;
|
|
284
|
+
}
|
|
285
|
+
interface ErrorInfo {
|
|
286
|
+
code: string;
|
|
287
|
+
message: string;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* The song document. While processing, only `id`, `status`, `progress` and
|
|
291
|
+
* `eta_sec` are set. With `include`, only the named sections are present.
|
|
292
|
+
*/
|
|
293
|
+
interface Song {
|
|
294
|
+
object: "song";
|
|
295
|
+
schema?: string;
|
|
296
|
+
id: string;
|
|
297
|
+
status: SongStatus;
|
|
298
|
+
progress?: number;
|
|
299
|
+
eta_sec?: number;
|
|
300
|
+
/** Set when status is "failed". */
|
|
301
|
+
error?: ErrorInfo;
|
|
302
|
+
song?: SongInfo;
|
|
303
|
+
song_dna?: SongDNA;
|
|
304
|
+
timeline?: Timeline;
|
|
305
|
+
best_moments?: BestMoment[];
|
|
306
|
+
lyrics?: Lyrics;
|
|
307
|
+
scores?: Scores;
|
|
308
|
+
story?: Story;
|
|
309
|
+
shot_plan?: ShotPlan;
|
|
310
|
+
created_at?: string | null;
|
|
311
|
+
external_ref?: string | null;
|
|
312
|
+
billing?: Billing;
|
|
313
|
+
}
|
|
314
|
+
/** The 202 body of POST /songs. */
|
|
315
|
+
interface SongCreated {
|
|
316
|
+
object: "song";
|
|
317
|
+
id: string;
|
|
318
|
+
status: "processing";
|
|
319
|
+
eta_sec: number;
|
|
320
|
+
billing: Billing;
|
|
321
|
+
external_ref?: string | null;
|
|
322
|
+
url: string;
|
|
323
|
+
}
|
|
324
|
+
/** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
|
|
325
|
+
interface SongShotPlan {
|
|
326
|
+
id: string;
|
|
327
|
+
status?: SongStatus;
|
|
328
|
+
song?: SongInfo;
|
|
329
|
+
story?: Story;
|
|
330
|
+
shot_plan?: ShotPlan;
|
|
331
|
+
}
|
|
332
|
+
interface SongListItem {
|
|
333
|
+
id: string;
|
|
334
|
+
status: string;
|
|
335
|
+
external_ref: string | null;
|
|
336
|
+
created_at: string | null;
|
|
337
|
+
billing: Billing;
|
|
338
|
+
}
|
|
339
|
+
interface SongList {
|
|
340
|
+
object: "list";
|
|
341
|
+
data: SongListItem[];
|
|
342
|
+
}
|
|
343
|
+
interface DeleteResult {
|
|
344
|
+
id: string;
|
|
345
|
+
deleted: boolean;
|
|
346
|
+
}
|
|
347
|
+
interface ExampleSummary {
|
|
348
|
+
id: string;
|
|
349
|
+
title: string;
|
|
350
|
+
artist: string;
|
|
351
|
+
genre: string;
|
|
352
|
+
subgenre: string;
|
|
353
|
+
tempo_bpm: number;
|
|
354
|
+
key: string;
|
|
355
|
+
duration_sec: number;
|
|
356
|
+
has_vocals: boolean;
|
|
357
|
+
about: string;
|
|
358
|
+
scenes: number;
|
|
359
|
+
url: string;
|
|
360
|
+
shot_plan_url: string;
|
|
361
|
+
}
|
|
362
|
+
interface ExampleList {
|
|
363
|
+
object: "list";
|
|
364
|
+
data: ExampleSummary[];
|
|
365
|
+
}
|
|
366
|
+
interface Account {
|
|
367
|
+
credits: number;
|
|
368
|
+
free_songs_per_month: number;
|
|
369
|
+
free_songs_used: number;
|
|
370
|
+
free_songs_left: number;
|
|
371
|
+
price_per_song_credits: number;
|
|
372
|
+
price_per_song_usd: number;
|
|
373
|
+
songs_this_month: number;
|
|
374
|
+
buy_credits_url: string;
|
|
375
|
+
songs_left_from_credits?: number;
|
|
376
|
+
low_balance?: boolean;
|
|
377
|
+
low_balance_threshold_songs?: number;
|
|
378
|
+
key?: {
|
|
379
|
+
id: string;
|
|
380
|
+
name: string | null;
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
interface Pricing {
|
|
384
|
+
free_songs_per_month: number;
|
|
385
|
+
price_per_song_credits: number;
|
|
386
|
+
price_per_song_usd: number;
|
|
387
|
+
credit_packs: {
|
|
388
|
+
credits: number;
|
|
389
|
+
usd: number;
|
|
390
|
+
}[];
|
|
391
|
+
included_per_song: string[];
|
|
392
|
+
volume: string;
|
|
393
|
+
limits: Record<string, number>;
|
|
394
|
+
examples_are_free: boolean;
|
|
395
|
+
}
|
|
396
|
+
/** A webhook event body. */
|
|
397
|
+
interface WebhookEvent<T = Record<string, unknown>> {
|
|
398
|
+
type: "song.done" | "song.failed" | "account.low_balance" | (string & {});
|
|
399
|
+
/** Unix seconds. */
|
|
400
|
+
created: number;
|
|
401
|
+
data: T;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
declare const DEFAULT_BASE_URL = "https://api.songbrain.ai/v1";
|
|
405
|
+
/** A Blob/File, a Buffer or bytes, or a local file path (Node). */
|
|
406
|
+
type FileInput = Blob | Uint8Array | ArrayBuffer | string;
|
|
407
|
+
interface SongbrainOptions {
|
|
408
|
+
/** Your key (`sb_live_…`). Defaults to `process.env.SONGBRAIN_API_KEY`. The example endpoints work without a key. */
|
|
409
|
+
apiKey?: string;
|
|
410
|
+
/** Defaults to `process.env.SONGBRAIN_BASE_URL` or https://api.songbrain.ai/v1. */
|
|
411
|
+
baseUrl?: string;
|
|
412
|
+
/** Timeout for each HTTP request in ms (uploads included). Default 60 000. */
|
|
413
|
+
timeoutMs?: number;
|
|
414
|
+
/** How often to retry 429 and 5xx responses, with backoff. Default 3. */
|
|
415
|
+
maxRetries?: number;
|
|
416
|
+
/** A custom fetch implementation. Defaults to the global fetch. */
|
|
417
|
+
fetch?: typeof fetch;
|
|
418
|
+
}
|
|
419
|
+
interface AnalyzeOptions {
|
|
420
|
+
/** A Blob/File, a Buffer or bytes, or a local file path. Pass this or `audioUrl`. */
|
|
421
|
+
file?: FileInput;
|
|
422
|
+
/** A public http(s) URL of the audio file. Pass this or `file`. */
|
|
423
|
+
audioUrl?: string;
|
|
424
|
+
title?: string;
|
|
425
|
+
artist?: string;
|
|
426
|
+
/** Receives `song.done` / `song.failed`. */
|
|
427
|
+
webhookUrl?: string;
|
|
428
|
+
/** Your own id, echoed back in responses and webhooks. */
|
|
429
|
+
externalRef?: string;
|
|
430
|
+
/** File name for an upload. Its extension must match the audio format. Taken from the path or File name, or guessed from the bytes. */
|
|
431
|
+
filename?: string;
|
|
432
|
+
/** Wait until the song is done and return the full document (default true). */
|
|
433
|
+
wait?: boolean;
|
|
434
|
+
/** Time between status checks while waiting. Default 5 000. */
|
|
435
|
+
pollIntervalMs?: number;
|
|
436
|
+
/** Total time to wait before throwing `WaitTimeout`. Default 300 000. */
|
|
437
|
+
timeoutMs?: number;
|
|
438
|
+
}
|
|
439
|
+
interface WaitOptions {
|
|
440
|
+
pollIntervalMs?: number;
|
|
441
|
+
timeoutMs?: number;
|
|
442
|
+
}
|
|
443
|
+
interface GetSongOptions {
|
|
444
|
+
/** "summary" drops word timings and beat arrays (about 3x smaller). */
|
|
445
|
+
view?: "full" | "summary";
|
|
446
|
+
/** Only these sections, e.g. ["song_dna", "shot_plan"]. */
|
|
447
|
+
include?: SectionName[] | string;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Client for the Songbrain API. Every method resolves to the JSON body of
|
|
451
|
+
* the response.
|
|
452
|
+
*
|
|
453
|
+
* ```ts
|
|
454
|
+
* const sb = new Songbrain(); // reads SONGBRAIN_API_KEY
|
|
455
|
+
* const song = await sb.analyze({ file: "song.mp3" });
|
|
456
|
+
* for (const s of song.shot_plan?.clip?.scenes ?? []) console.log(s.start_sec, s.prompt);
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
declare class Songbrain {
|
|
460
|
+
readonly apiKey: string | undefined;
|
|
461
|
+
readonly baseUrl: string;
|
|
462
|
+
readonly timeoutMs: number;
|
|
463
|
+
readonly maxRetries: number;
|
|
464
|
+
private readonly fetchImpl;
|
|
465
|
+
/** @internal Overridable in tests. */
|
|
466
|
+
_sleep: (ms: number) => Promise<void>;
|
|
467
|
+
constructor(options?: SongbrainOptions);
|
|
468
|
+
/**
|
|
469
|
+
* Analyse a song from a file or a public URL. Pass exactly one of `file` or `audioUrl`.
|
|
470
|
+
* Typically 60-90 s. With `wait: true` (default) resolves to the full document;
|
|
471
|
+
* with `wait: false` to the 202 body `{ id, status: "processing", eta_sec, billing }`.
|
|
472
|
+
*/
|
|
473
|
+
analyze(options: AnalyzeOptions & {
|
|
474
|
+
wait: false;
|
|
475
|
+
}): Promise<SongCreated>;
|
|
476
|
+
analyze(options: AnalyzeOptions & {
|
|
477
|
+
wait?: true;
|
|
478
|
+
}): Promise<Song>;
|
|
479
|
+
analyze(options: AnalyzeOptions): Promise<Song | SongCreated>;
|
|
480
|
+
/** Poll GET /songs/{id} until the song is done. Throws `AnalysisFailed` or `WaitTimeout`. */
|
|
481
|
+
waitFor(songId: string, options?: WaitOptions): Promise<Song>;
|
|
482
|
+
/** Status while processing, then the full document. */
|
|
483
|
+
getSong(songId: string, options?: GetSongOptions): Promise<Song>;
|
|
484
|
+
/** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
|
|
485
|
+
shotPlan(songId: string): Promise<SongShotPlan>;
|
|
486
|
+
/** Your songs, newest first. `limit` is 1-100 (default 20). */
|
|
487
|
+
listSongs(options?: {
|
|
488
|
+
limit?: number;
|
|
489
|
+
}): Promise<SongList>;
|
|
490
|
+
/** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
|
|
491
|
+
deleteSong(songId: string): Promise<DeleteResult>;
|
|
492
|
+
/** Free songs left this month, credit balance and price per song. */
|
|
493
|
+
account(): Promise<Account>;
|
|
494
|
+
/** Prices and limits. No key needed. */
|
|
495
|
+
pricing(): Promise<Pricing>;
|
|
496
|
+
/** Example analyses of Songbrain's own songs. No key needed. */
|
|
497
|
+
examples(): Promise<ExampleList>;
|
|
498
|
+
/** A full example document, in the same format as `getSong`. No key needed. */
|
|
499
|
+
example(exampleId: string, options?: GetSongOptions): Promise<Song>;
|
|
500
|
+
/** An example story and shot plan, in the same format as `shotPlan`. No key needed. */
|
|
501
|
+
exampleShotPlan(exampleId: string): Promise<SongShotPlan>;
|
|
502
|
+
private backoffMs;
|
|
503
|
+
private request;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Every API error has the shape `{ error: { code, message } }`. The client
|
|
508
|
+
* turns it into a `SongbrainError` (or a subclass) with `status`, `code` and
|
|
509
|
+
* `message`.
|
|
510
|
+
*/
|
|
511
|
+
declare class SongbrainError extends Error {
|
|
512
|
+
/** HTTP status, or 0 when the error did not come from an HTTP response. */
|
|
513
|
+
readonly status: number;
|
|
514
|
+
/** Machine-readable code, e.g. "invalid_url" or "rate_limited". */
|
|
515
|
+
readonly code: string;
|
|
516
|
+
/** The parsed response body, when there was one. */
|
|
517
|
+
readonly body: unknown;
|
|
518
|
+
constructor(status: number, code: string, message: string, body?: unknown);
|
|
519
|
+
}
|
|
520
|
+
/** 401: the API key is missing, invalid or revoked. */
|
|
521
|
+
declare class AuthenticationError extends SongbrainError {
|
|
522
|
+
}
|
|
523
|
+
/** 402: the free songs for this month are used up and the balance is below one song. */
|
|
524
|
+
declare class InsufficientCredits extends SongbrainError {
|
|
525
|
+
}
|
|
526
|
+
/** 404: unknown id, or the song belongs to another account. */
|
|
527
|
+
declare class NotFound extends SongbrainError {
|
|
528
|
+
}
|
|
529
|
+
/** 429: too many requests, too many songs in flight, daily cap or busy pipeline. */
|
|
530
|
+
declare class RateLimited extends SongbrainError {
|
|
531
|
+
/** Seconds to wait, from the Retry-After header (null if absent). */
|
|
532
|
+
readonly retryAfter: number | null;
|
|
533
|
+
constructor(status: number, code: string, message: string, body?: unknown, retryAfter?: number | null);
|
|
534
|
+
}
|
|
535
|
+
/** The song was accepted but its analysis failed. Paid credits are refunded automatically. */
|
|
536
|
+
declare class AnalysisFailed extends SongbrainError {
|
|
537
|
+
readonly songId: string;
|
|
538
|
+
constructor(songId: string, code: string, message: string, body?: unknown);
|
|
539
|
+
}
|
|
540
|
+
/** `analyze({ wait: true })` gave up before the song was done. It keeps processing on the server. */
|
|
541
|
+
declare class WaitTimeout extends SongbrainError {
|
|
542
|
+
readonly songId: string;
|
|
543
|
+
constructor(songId: string, timeoutMs: number);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** The header Songbrain signs webhooks with. */
|
|
547
|
+
declare const SIGNATURE_HEADER = "Songbrain-Signature";
|
|
548
|
+
type RawBody = string | Uint8Array | ArrayBuffer;
|
|
549
|
+
/**
|
|
550
|
+
* Check a `Songbrain-Signature: t=<unix>,v1=<hex>` header, where v1 is the
|
|
551
|
+
* HMAC-SHA256 of `"<t>.<raw body>"` with your key's webhook secret.
|
|
552
|
+
*
|
|
553
|
+
* Pass the raw request body exactly as received, before any JSON parsing.
|
|
554
|
+
* Returns false (never throws) for a missing, malformed, wrong or old signature.
|
|
555
|
+
*
|
|
556
|
+
* @param toleranceSec Maximum age in seconds (default 300). 0 disables the check.
|
|
557
|
+
*/
|
|
558
|
+
declare function verifyWebhook(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): boolean;
|
|
559
|
+
/**
|
|
560
|
+
* Verify the signature and return the parsed event:
|
|
561
|
+
* `{ type: "song.done" | "song.failed" | "account.low_balance", created, data }`.
|
|
562
|
+
* Throws if the signature is not valid.
|
|
563
|
+
*/
|
|
564
|
+
declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: RawBody, header: string | null | undefined, secret: string, toleranceSec?: number): WebhookEvent<T>;
|
|
565
|
+
/** Build a signature header value. Useful to test your webhook handler. */
|
|
566
|
+
declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
|
|
567
|
+
|
|
568
|
+
declare const VERSION = "0.1.0";
|
|
569
|
+
|
|
570
|
+
export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type CaptionIdeas, type Clip, DEFAULT_BASE_URL, type DeleteResult, type ErrorInfo, type ExampleList, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type KeyChange, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, NotFound, type Phases, type PlatformFit, type Pricing, RateLimited, SIGNATURE_HEADER, type Scene, type SceneKind, type ScoreBlock, type Scores, type Section, type SectionName, type ShotPlan, type ShotStyle, type Song, type SongCreated, type SongDNA, type SongInfo, type SongList, type SongListItem, type SongShotPlan, type SongStatus, Songbrain, SongbrainError, type SongbrainOptions, type SoundsLike, type Story, type StoryBeats, type StoryElement, type Timeline, VERSION, type VocalChange, type WaitOptions, WaitTimeout, type WebhookEvent, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
|