songbrain 0.3.3 → 0.4.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/dist/index.d.cts CHANGED
@@ -7,6 +7,12 @@
7
7
  * otherwise (`start_in_clip`).
8
8
  */
9
9
  type SongStatus = "processing" | "done" | "failed";
10
+ /**
11
+ * What a song includes. "full" (the default): Song DNA + video direction (story,
12
+ * shot plan), 25 credits. "dna": Song DNA only (song_dna, timeline, best_moments,
13
+ * lyrics, scores), 10 credits. Add the direction to a dna song later for 15 credits.
14
+ */
15
+ type Mode = "full" | "dna";
10
16
  /** One of the sections a song document has. Use with `include`. */
11
17
  type SectionName = "song_dna" | "timeline" | "best_moments" | "lyrics" | "scores" | "story" | "shot_plan";
12
18
  interface Billing {
@@ -305,9 +311,22 @@ interface ErrorInfo {
305
311
  /** "req_…", also in the Songbrain-Request-Id header. */
306
312
  request_id?: string;
307
313
  }
314
+ /** On a "dna" song: how to add the video direction (story + shot plan) later. */
315
+ interface DirectionOffer {
316
+ included: false;
317
+ /** "POST https://api.songbrain.ai/v1/songs/{id}/direction" */
318
+ add: string;
319
+ /** 15 */
320
+ credits: number;
321
+ /** 0.3 */
322
+ usd: number;
323
+ /** ["story", "shot_plan"] */
324
+ adds: SectionName[];
325
+ }
308
326
  /**
309
- * The song document. While processing, only `id`, `status`, `progress` and
310
- * `eta_sec` are set. With `include`, only the named sections are present.
327
+ * The song document. While processing, only `id`, `status`, `progress`,
328
+ * `eta_sec` and `mode` are set. With `include`, only the named sections are
329
+ * present. A "dna" song has no `story` and no `shot_plan`; it has `direction`.
311
330
  */
312
331
  interface Song {
313
332
  object: "song";
@@ -333,18 +352,35 @@ interface Song {
333
352
  livemode?: boolean;
334
353
  /** Measured vs. transcribed vs. model estimate vs. generated. */
335
354
  provenance?: Provenance;
355
+ /** "full" or "dna" (API 1.3). */
356
+ mode?: Mode;
357
+ /** Only on "dna" songs: how to add the story and shot plan. */
358
+ direction?: DirectionOffer;
336
359
  }
337
360
  /** The 202 body of POST /songs. */
338
361
  interface SongCreated {
339
362
  object: "song";
340
363
  id: string;
341
364
  status: "processing";
365
+ mode?: Mode;
342
366
  eta_sec: number;
343
367
  billing: Billing;
344
368
  external_ref?: string | null;
345
369
  url: string;
346
370
  livemode?: boolean;
347
371
  }
372
+ /** The 202 body of POST /songs/{id}/direction. */
373
+ interface DirectionCreated {
374
+ object: "song";
375
+ id: string;
376
+ /** "processing"; "done" right away for a test-mode song. */
377
+ status: "processing" | "done";
378
+ mode: "full";
379
+ eta_sec: number;
380
+ billing: Billing;
381
+ url: string;
382
+ livemode?: boolean;
383
+ }
348
384
  /** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
349
385
  interface SongShotPlan {
350
386
  id: string;
@@ -356,6 +392,7 @@ interface SongShotPlan {
356
392
  interface SongListItem {
357
393
  id: string;
358
394
  status: string;
395
+ mode?: Mode;
359
396
  external_ref: string | null;
360
397
  created_at: string | null;
361
398
  billing: Billing;
@@ -391,13 +428,36 @@ interface ExampleList {
391
428
  object: "list";
392
429
  data: ExampleSummary[];
393
430
  }
431
+ interface Price {
432
+ credits: number;
433
+ usd: number;
434
+ }
435
+ interface ModePrice extends Price {
436
+ name: string;
437
+ /** Section names the mode returns. */
438
+ includes: SectionName[];
439
+ default?: boolean;
440
+ }
441
+ interface AddDirectionPrice extends Price {
442
+ /** "POST /v1/songs/{id}/direction" */
443
+ endpoint: string;
444
+ note?: string;
445
+ }
394
446
  interface Account {
395
447
  credits: number;
396
448
  free_songs_per_month: number;
397
449
  free_songs_used: number;
398
450
  free_songs_left: number;
451
+ /** Mode "full". */
399
452
  price_per_song_credits: number;
453
+ /** Mode "full". */
400
454
  price_per_song_usd: number;
455
+ /** Price per mode (API 1.3). */
456
+ prices?: {
457
+ full: Price;
458
+ dna: Price;
459
+ add_direction: Price;
460
+ };
401
461
  songs_this_month: number;
402
462
  buy_credits_url: string;
403
463
  songs_left_from_credits?: number;
@@ -410,8 +470,19 @@ interface Account {
410
470
  }
411
471
  interface Pricing {
412
472
  free_songs_per_month: number;
473
+ /** "either mode" */
474
+ free_songs_cover?: string;
475
+ /** Mode "full". */
413
476
  price_per_song_credits: number;
477
+ /** Mode "full". */
414
478
  price_per_song_usd: number;
479
+ /** API 1.3. */
480
+ modes?: {
481
+ full: ModePrice;
482
+ dna: ModePrice;
483
+ };
484
+ /** API 1.3: story + shot plan for a finished dna song. */
485
+ add_direction?: AddDirectionPrice;
415
486
  credit_packs: {
416
487
  credits: number;
417
488
  usd: number;
@@ -421,7 +492,12 @@ interface Pricing {
421
492
  limits: Record<string, number>;
422
493
  examples_are_free: boolean;
423
494
  }
424
- /** A webhook event body. */
495
+ /**
496
+ * A webhook event body. For song.* events `data` has `id`, `status`, `mode`,
497
+ * `external_ref`, `url`, and `shot_plan_url` for "full" songs only. After
498
+ * `addDirection` a second `song.done` arrives with its own `id` and
499
+ * `data.direction_added: true`.
500
+ */
425
501
  interface WebhookEvent<T = Record<string, unknown>> {
426
502
  /** "evt_…". Stays the same across retries of one event: dedupe on it. */
427
503
  id: string;
@@ -500,6 +576,13 @@ interface AnalyzeOptions {
500
576
  * left out, never guessed.
501
577
  */
502
578
  lyrics?: string;
579
+ /**
580
+ * "full" (the API default, 25 credits) = Song DNA + video direction (story, shot plan).
581
+ * "dna" (10 credits) = Song DNA only: song_dna, timeline, best_moments, lyrics and
582
+ * scores, no story and no shot plan. Add the direction later with `addDirection`
583
+ * (15 credits). Left out, nothing is sent and the API uses "full". A free song covers either mode.
584
+ */
585
+ mode?: Mode;
503
586
  /**
504
587
  * Test mode: free, never charged, no audio needed. The song is done right away and
505
588
  * returns the Sugar Rush example analysis with `livemode: false`. `song.done` is
@@ -541,6 +624,14 @@ interface GetSongOptions {
541
624
  /** Only these sections, e.g. ["song_dna", "shot_plan"]. */
542
625
  include?: SectionName[] | string;
543
626
  }
627
+ interface ExampleOptions extends GetSongOptions {
628
+ /** "dna" returns what a Song DNA-only song looks like: no story, no shot plan, a `direction` offer. */
629
+ mode?: Mode;
630
+ }
631
+ interface AddDirectionOptions extends WaitOptions {
632
+ /** Wait until the direction is there and return the full document (default true). */
633
+ wait?: boolean;
634
+ }
544
635
  /**
545
636
  * Client for the Songbrain API. Every method resolves to the JSON body of
546
637
  * the response.
@@ -582,9 +673,32 @@ declare class Songbrain {
582
673
  analyze(options: AnalyzeOptions): Promise<Song | SongCreated>;
583
674
  /** Poll GET /songs/{id} until the song is done. Throws `AnalysisFailed` or `WaitTimeout`. */
584
675
  waitFor(songId: string, options?: WaitOptions): Promise<Song>;
676
+ /**
677
+ * Add the video direction (story + shot plan) to a finished `mode: "dna"` song:
678
+ * `POST /songs/{id}/direction`. 15 credits (never a free song), refunded automatically
679
+ * if it fails. No new upload. Takes about 30-40 s; the song is "processing" meanwhile,
680
+ * then "done" again with `mode: "full"`. Test-mode songs (`test_…`) upgrade right away, for free.
681
+ *
682
+ * With `wait: true` (default) resolves to the full document; with `wait: false` to the
683
+ * 202 body `{ id, status: "processing", mode: "full", eta_sec, billing }`.
684
+ * Throws `AnalysisFailed` (code "direction_failed", credits refunded, the song stays "dna"),
685
+ * `InsufficientCredits` (402), or `SongbrainError` 409 `direction_included`, `not_done`,
686
+ * `song_failed`, `direction_in_progress`.
687
+ */
688
+ addDirection(songId: string, options: AddDirectionOptions & {
689
+ wait: false;
690
+ }): Promise<DirectionCreated>;
691
+ addDirection(songId: string, options?: AddDirectionOptions & {
692
+ wait?: true;
693
+ }): Promise<Song>;
694
+ addDirection(songId: string, options?: AddDirectionOptions): Promise<Song | DirectionCreated>;
695
+ private poll;
585
696
  /** Status while processing, then the full document. */
586
697
  getSong(songId: string, options?: GetSongOptions): Promise<Song>;
587
- /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
698
+ /**
699
+ * Only the story and the shot plan: `{ id, status, song, story, shot_plan }`.
700
+ * A "dna" song has none: 409 `no_direction` (the error body's `direction` says how to add it).
701
+ */
588
702
  shotPlan(songId: string): Promise<SongShotPlan>;
589
703
  /** One page of your songs, newest first: `{ object: "list", data, has_more, next_cursor }`. */
590
704
  listSongs(options?: ListSongsOptions): Promise<SongList>;
@@ -598,7 +712,7 @@ declare class Songbrain {
598
712
  iterSongs(options?: IterSongsOptions): AsyncGenerator<SongListItem, void, undefined>;
599
713
  /** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
600
714
  deleteSong(songId: string): Promise<DeleteResult>;
601
- /** Free songs left this month, credit balance and price per song. */
715
+ /** Free songs left this month, credit balance and prices (`prices`: full, dna, add_direction). */
602
716
  account(): Promise<Account>;
603
717
  /** Prices and limits. No key needed. */
604
718
  pricing(): Promise<Pricing>;
@@ -613,8 +727,11 @@ declare class Songbrain {
613
727
  }): Promise<WebhookDeliveryList>;
614
728
  /** Example analyses of Songbrain's own songs. No key needed. */
615
729
  examples(): Promise<ExampleList>;
616
- /** A full example document, in the same format as `getSong`. No key needed. */
617
- example(exampleId: string, options?: GetSongOptions): Promise<Song>;
730
+ /**
731
+ * A full example document, in the same format as `getSong`. No key needed.
732
+ * `mode: "dna"` returns the Song DNA view (no story, no shot plan).
733
+ */
734
+ example(exampleId: string, options?: ExampleOptions): Promise<Song>;
618
735
  /** An example story and shot plan, in the same format as `shotPlan`. No key needed. */
619
736
  exampleShotPlan(exampleId: string): Promise<SongShotPlan>;
620
737
  private backoffMs;
@@ -690,6 +807,6 @@ declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: Raw
690
807
  /** Build a signature header value. Useful to test your webhook handler. */
691
808
  declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
692
809
 
693
- declare const VERSION = "0.3.3";
810
+ declare const VERSION = "0.4.0";
694
811
 
695
- export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, 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 IterSongsOptions, type KeyChange, type ListSongsOptions, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, type LyricsAlignment, NotFound, type Phases, type Pricing, type Provenance, type RateLimitInfo, 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 WebhookDelivery, type WebhookDeliveryList, type WebhookEvent, type WebhookTestResult, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
812
+ export { type Account, type Act, type AddDirectionOptions, type AddDirectionPrice, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type Clip, DEFAULT_BASE_URL, type DeleteResult, type DirectionCreated, type DirectionOffer, type ErrorInfo, type ExampleList, type ExampleOptions, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type IterSongsOptions, type KeyChange, type ListSongsOptions, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, type LyricsAlignment, type Mode, type ModePrice, NotFound, type Phases, type Price, type Pricing, type Provenance, type RateLimitInfo, 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 WebhookDelivery, type WebhookDeliveryList, type WebhookEvent, type WebhookTestResult, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
package/dist/index.d.ts CHANGED
@@ -7,6 +7,12 @@
7
7
  * otherwise (`start_in_clip`).
8
8
  */
9
9
  type SongStatus = "processing" | "done" | "failed";
10
+ /**
11
+ * What a song includes. "full" (the default): Song DNA + video direction (story,
12
+ * shot plan), 25 credits. "dna": Song DNA only (song_dna, timeline, best_moments,
13
+ * lyrics, scores), 10 credits. Add the direction to a dna song later for 15 credits.
14
+ */
15
+ type Mode = "full" | "dna";
10
16
  /** One of the sections a song document has. Use with `include`. */
11
17
  type SectionName = "song_dna" | "timeline" | "best_moments" | "lyrics" | "scores" | "story" | "shot_plan";
12
18
  interface Billing {
@@ -305,9 +311,22 @@ interface ErrorInfo {
305
311
  /** "req_…", also in the Songbrain-Request-Id header. */
306
312
  request_id?: string;
307
313
  }
314
+ /** On a "dna" song: how to add the video direction (story + shot plan) later. */
315
+ interface DirectionOffer {
316
+ included: false;
317
+ /** "POST https://api.songbrain.ai/v1/songs/{id}/direction" */
318
+ add: string;
319
+ /** 15 */
320
+ credits: number;
321
+ /** 0.3 */
322
+ usd: number;
323
+ /** ["story", "shot_plan"] */
324
+ adds: SectionName[];
325
+ }
308
326
  /**
309
- * The song document. While processing, only `id`, `status`, `progress` and
310
- * `eta_sec` are set. With `include`, only the named sections are present.
327
+ * The song document. While processing, only `id`, `status`, `progress`,
328
+ * `eta_sec` and `mode` are set. With `include`, only the named sections are
329
+ * present. A "dna" song has no `story` and no `shot_plan`; it has `direction`.
311
330
  */
312
331
  interface Song {
313
332
  object: "song";
@@ -333,18 +352,35 @@ interface Song {
333
352
  livemode?: boolean;
334
353
  /** Measured vs. transcribed vs. model estimate vs. generated. */
335
354
  provenance?: Provenance;
355
+ /** "full" or "dna" (API 1.3). */
356
+ mode?: Mode;
357
+ /** Only on "dna" songs: how to add the story and shot plan. */
358
+ direction?: DirectionOffer;
336
359
  }
337
360
  /** The 202 body of POST /songs. */
338
361
  interface SongCreated {
339
362
  object: "song";
340
363
  id: string;
341
364
  status: "processing";
365
+ mode?: Mode;
342
366
  eta_sec: number;
343
367
  billing: Billing;
344
368
  external_ref?: string | null;
345
369
  url: string;
346
370
  livemode?: boolean;
347
371
  }
372
+ /** The 202 body of POST /songs/{id}/direction. */
373
+ interface DirectionCreated {
374
+ object: "song";
375
+ id: string;
376
+ /** "processing"; "done" right away for a test-mode song. */
377
+ status: "processing" | "done";
378
+ mode: "full";
379
+ eta_sec: number;
380
+ billing: Billing;
381
+ url: string;
382
+ livemode?: boolean;
383
+ }
348
384
  /** GET /songs/{id}/shot-plan and GET /examples/{id}/shot-plan. */
349
385
  interface SongShotPlan {
350
386
  id: string;
@@ -356,6 +392,7 @@ interface SongShotPlan {
356
392
  interface SongListItem {
357
393
  id: string;
358
394
  status: string;
395
+ mode?: Mode;
359
396
  external_ref: string | null;
360
397
  created_at: string | null;
361
398
  billing: Billing;
@@ -391,13 +428,36 @@ interface ExampleList {
391
428
  object: "list";
392
429
  data: ExampleSummary[];
393
430
  }
431
+ interface Price {
432
+ credits: number;
433
+ usd: number;
434
+ }
435
+ interface ModePrice extends Price {
436
+ name: string;
437
+ /** Section names the mode returns. */
438
+ includes: SectionName[];
439
+ default?: boolean;
440
+ }
441
+ interface AddDirectionPrice extends Price {
442
+ /** "POST /v1/songs/{id}/direction" */
443
+ endpoint: string;
444
+ note?: string;
445
+ }
394
446
  interface Account {
395
447
  credits: number;
396
448
  free_songs_per_month: number;
397
449
  free_songs_used: number;
398
450
  free_songs_left: number;
451
+ /** Mode "full". */
399
452
  price_per_song_credits: number;
453
+ /** Mode "full". */
400
454
  price_per_song_usd: number;
455
+ /** Price per mode (API 1.3). */
456
+ prices?: {
457
+ full: Price;
458
+ dna: Price;
459
+ add_direction: Price;
460
+ };
401
461
  songs_this_month: number;
402
462
  buy_credits_url: string;
403
463
  songs_left_from_credits?: number;
@@ -410,8 +470,19 @@ interface Account {
410
470
  }
411
471
  interface Pricing {
412
472
  free_songs_per_month: number;
473
+ /** "either mode" */
474
+ free_songs_cover?: string;
475
+ /** Mode "full". */
413
476
  price_per_song_credits: number;
477
+ /** Mode "full". */
414
478
  price_per_song_usd: number;
479
+ /** API 1.3. */
480
+ modes?: {
481
+ full: ModePrice;
482
+ dna: ModePrice;
483
+ };
484
+ /** API 1.3: story + shot plan for a finished dna song. */
485
+ add_direction?: AddDirectionPrice;
415
486
  credit_packs: {
416
487
  credits: number;
417
488
  usd: number;
@@ -421,7 +492,12 @@ interface Pricing {
421
492
  limits: Record<string, number>;
422
493
  examples_are_free: boolean;
423
494
  }
424
- /** A webhook event body. */
495
+ /**
496
+ * A webhook event body. For song.* events `data` has `id`, `status`, `mode`,
497
+ * `external_ref`, `url`, and `shot_plan_url` for "full" songs only. After
498
+ * `addDirection` a second `song.done` arrives with its own `id` and
499
+ * `data.direction_added: true`.
500
+ */
425
501
  interface WebhookEvent<T = Record<string, unknown>> {
426
502
  /** "evt_…". Stays the same across retries of one event: dedupe on it. */
427
503
  id: string;
@@ -500,6 +576,13 @@ interface AnalyzeOptions {
500
576
  * left out, never guessed.
501
577
  */
502
578
  lyrics?: string;
579
+ /**
580
+ * "full" (the API default, 25 credits) = Song DNA + video direction (story, shot plan).
581
+ * "dna" (10 credits) = Song DNA only: song_dna, timeline, best_moments, lyrics and
582
+ * scores, no story and no shot plan. Add the direction later with `addDirection`
583
+ * (15 credits). Left out, nothing is sent and the API uses "full". A free song covers either mode.
584
+ */
585
+ mode?: Mode;
503
586
  /**
504
587
  * Test mode: free, never charged, no audio needed. The song is done right away and
505
588
  * returns the Sugar Rush example analysis with `livemode: false`. `song.done` is
@@ -541,6 +624,14 @@ interface GetSongOptions {
541
624
  /** Only these sections, e.g. ["song_dna", "shot_plan"]. */
542
625
  include?: SectionName[] | string;
543
626
  }
627
+ interface ExampleOptions extends GetSongOptions {
628
+ /** "dna" returns what a Song DNA-only song looks like: no story, no shot plan, a `direction` offer. */
629
+ mode?: Mode;
630
+ }
631
+ interface AddDirectionOptions extends WaitOptions {
632
+ /** Wait until the direction is there and return the full document (default true). */
633
+ wait?: boolean;
634
+ }
544
635
  /**
545
636
  * Client for the Songbrain API. Every method resolves to the JSON body of
546
637
  * the response.
@@ -582,9 +673,32 @@ declare class Songbrain {
582
673
  analyze(options: AnalyzeOptions): Promise<Song | SongCreated>;
583
674
  /** Poll GET /songs/{id} until the song is done. Throws `AnalysisFailed` or `WaitTimeout`. */
584
675
  waitFor(songId: string, options?: WaitOptions): Promise<Song>;
676
+ /**
677
+ * Add the video direction (story + shot plan) to a finished `mode: "dna"` song:
678
+ * `POST /songs/{id}/direction`. 15 credits (never a free song), refunded automatically
679
+ * if it fails. No new upload. Takes about 30-40 s; the song is "processing" meanwhile,
680
+ * then "done" again with `mode: "full"`. Test-mode songs (`test_…`) upgrade right away, for free.
681
+ *
682
+ * With `wait: true` (default) resolves to the full document; with `wait: false` to the
683
+ * 202 body `{ id, status: "processing", mode: "full", eta_sec, billing }`.
684
+ * Throws `AnalysisFailed` (code "direction_failed", credits refunded, the song stays "dna"),
685
+ * `InsufficientCredits` (402), or `SongbrainError` 409 `direction_included`, `not_done`,
686
+ * `song_failed`, `direction_in_progress`.
687
+ */
688
+ addDirection(songId: string, options: AddDirectionOptions & {
689
+ wait: false;
690
+ }): Promise<DirectionCreated>;
691
+ addDirection(songId: string, options?: AddDirectionOptions & {
692
+ wait?: true;
693
+ }): Promise<Song>;
694
+ addDirection(songId: string, options?: AddDirectionOptions): Promise<Song | DirectionCreated>;
695
+ private poll;
585
696
  /** Status while processing, then the full document. */
586
697
  getSong(songId: string, options?: GetSongOptions): Promise<Song>;
587
- /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
698
+ /**
699
+ * Only the story and the shot plan: `{ id, status, song, story, shot_plan }`.
700
+ * A "dna" song has none: 409 `no_direction` (the error body's `direction` says how to add it).
701
+ */
588
702
  shotPlan(songId: string): Promise<SongShotPlan>;
589
703
  /** One page of your songs, newest first: `{ object: "list", data, has_more, next_cursor }`. */
590
704
  listSongs(options?: ListSongsOptions): Promise<SongList>;
@@ -598,7 +712,7 @@ declare class Songbrain {
598
712
  iterSongs(options?: IterSongsOptions): AsyncGenerator<SongListItem, void, undefined>;
599
713
  /** Delete a song's audio and analysis now. 409 `still_processing` while it runs. */
600
714
  deleteSong(songId: string): Promise<DeleteResult>;
601
- /** Free songs left this month, credit balance and price per song. */
715
+ /** Free songs left this month, credit balance and prices (`prices`: full, dna, add_direction). */
602
716
  account(): Promise<Account>;
603
717
  /** Prices and limits. No key needed. */
604
718
  pricing(): Promise<Pricing>;
@@ -613,8 +727,11 @@ declare class Songbrain {
613
727
  }): Promise<WebhookDeliveryList>;
614
728
  /** Example analyses of Songbrain's own songs. No key needed. */
615
729
  examples(): Promise<ExampleList>;
616
- /** A full example document, in the same format as `getSong`. No key needed. */
617
- example(exampleId: string, options?: GetSongOptions): Promise<Song>;
730
+ /**
731
+ * A full example document, in the same format as `getSong`. No key needed.
732
+ * `mode: "dna"` returns the Song DNA view (no story, no shot plan).
733
+ */
734
+ example(exampleId: string, options?: ExampleOptions): Promise<Song>;
618
735
  /** An example story and shot plan, in the same format as `shotPlan`. No key needed. */
619
736
  exampleShotPlan(exampleId: string): Promise<SongShotPlan>;
620
737
  private backoffMs;
@@ -690,6 +807,6 @@ declare function constructWebhookEvent<T = Record<string, unknown>>(rawBody: Raw
690
807
  /** Build a signature header value. Useful to test your webhook handler. */
691
808
  declare function signWebhook(rawBody: RawBody, secret: string, timestampSec?: number): string;
692
809
 
693
- declare const VERSION = "0.3.3";
810
+ declare const VERSION = "0.4.0";
694
811
 
695
- export { type Account, type Act, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, 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 IterSongsOptions, type KeyChange, type ListSongsOptions, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, type LyricsAlignment, NotFound, type Phases, type Pricing, type Provenance, type RateLimitInfo, 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 WebhookDelivery, type WebhookDeliveryList, type WebhookEvent, type WebhookTestResult, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
812
+ export { type Account, type Act, type AddDirectionOptions, type AddDirectionPrice, AnalysisFailed, type AnalyzeOptions, AuthenticationError, type BestMoment, type Billing, type Clip, DEFAULT_BASE_URL, type DeleteResult, type DirectionCreated, type DirectionOffer, type ErrorInfo, type ExampleList, type ExampleOptions, type ExampleSummary, type FileInput, type FullSong, type FullSongSection, type GetSongOptions, type Instrument, InsufficientCredits, type IterSongsOptions, type KeyChange, type ListSongsOptions, type LoudnessCheck, type LyricHook, type LyricLine, type Lyrics, type LyricsAlignment, type Mode, type ModePrice, NotFound, type Phases, type Price, type Pricing, type Provenance, type RateLimitInfo, 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 WebhookDelivery, type WebhookDeliveryList, type WebhookEvent, type WebhookTestResult, type WhatToFix, type WhatWorks, type Word, constructWebhookEvent, signWebhook, verifyWebhook };
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@ var WaitTimeout = class extends SongbrainError {
54
54
  };
55
55
 
56
56
  // src/version.ts
57
- var VERSION = "0.3.3";
57
+ var VERSION = "0.4.0";
58
58
 
59
59
  // src/agent.ts
60
60
  var FLAGS = [
@@ -199,6 +199,7 @@ var Songbrain = class {
199
199
  if (options.webhookUrl !== void 0) fields.webhook_url = options.webhookUrl;
200
200
  if (options.externalRef !== void 0) fields.external_ref = options.externalRef;
201
201
  if (options.lyrics !== void 0) fields.lyrics = options.lyrics;
202
+ if (options.mode !== void 0) fields.mode = options.mode;
202
203
  const headers = { [IDEMPOTENCY_HEADER]: options.idempotencyKey || await newIdempotencyKey() };
203
204
  let created;
204
205
  if (file === void 0) {
@@ -225,12 +226,28 @@ var Songbrain = class {
225
226
  });
226
227
  }
227
228
  /** Poll GET /songs/{id} until the song is done. Throws `AnalysisFailed` or `WaitTimeout`. */
228
- async waitFor(songId, options = {}) {
229
+ waitFor(songId, options = {}) {
230
+ return this.poll(songId, options, false);
231
+ }
232
+ async addDirection(songId, options = {}) {
233
+ const created = await this.request("POST", `/songs/${encodeURIComponent(songId)}/direction`);
234
+ if (options.wait === false) return created;
235
+ return this.poll(songId, options, true);
236
+ }
237
+ async poll(songId, options, direction) {
229
238
  const timeoutMs = options.timeoutMs ?? 3e5;
230
239
  const interval = Math.max(500, options.pollIntervalMs ?? 5e3);
231
240
  const deadline = Date.now() + timeoutMs;
232
241
  for (; ; ) {
233
242
  const doc = await this.getSong(songId);
243
+ if (doc.status === "done" && direction && doc.mode === "dna") {
244
+ throw new AnalysisFailed(
245
+ songId,
246
+ "direction_failed",
247
+ "The video direction could not be added. The 15 credits were refunded automatically; the Song DNA is unchanged.",
248
+ doc
249
+ );
250
+ }
234
251
  if (doc.status === "done") return doc;
235
252
  if (doc.status === "failed") {
236
253
  throw new AnalysisFailed(
@@ -249,7 +266,10 @@ var Songbrain = class {
249
266
  getSong(songId, options = {}) {
250
267
  return this.request("GET", `/songs/${encodeURIComponent(songId)}`, { query: docQuery(options) });
251
268
  }
252
- /** Only the story and the shot plan: `{ id, status, song, story, shot_plan }`. */
269
+ /**
270
+ * Only the story and the shot plan: `{ id, status, song, story, shot_plan }`.
271
+ * A "dna" song has none: 409 `no_direction` (the error body's `direction` says how to add it).
272
+ */
253
273
  shotPlan(songId) {
254
274
  return this.request("GET", `/songs/${encodeURIComponent(songId)}/shot-plan`);
255
275
  }
@@ -281,7 +301,7 @@ var Songbrain = class {
281
301
  return this.request("DELETE", `/songs/${encodeURIComponent(songId)}`);
282
302
  }
283
303
  // ── account ──────────────────────────────────────────────────────────────
284
- /** Free songs left this month, credit balance and price per song. */
304
+ /** Free songs left this month, credit balance and prices (`prices`: full, dna, add_direction). */
285
305
  account() {
286
306
  return this.request("GET", "/account");
287
307
  }
@@ -308,9 +328,14 @@ var Songbrain = class {
308
328
  examples() {
309
329
  return this.request("GET", "/examples");
310
330
  }
311
- /** A full example document, in the same format as `getSong`. No key needed. */
331
+ /**
332
+ * A full example document, in the same format as `getSong`. No key needed.
333
+ * `mode: "dna"` returns the Song DNA view (no story, no shot plan).
334
+ */
312
335
  example(exampleId, options = {}) {
313
- return this.request("GET", `/examples/${encodeURIComponent(exampleId)}`, { query: docQuery(options) });
336
+ return this.request("GET", `/examples/${encodeURIComponent(exampleId)}`, {
337
+ query: { ...docQuery(options), mode: options.mode }
338
+ });
314
339
  }
315
340
  /** An example story and shot plan, in the same format as `shotPlan`. No key needed. */
316
341
  exampleShotPlan(exampleId) {