sonilo 0.17.0 → 0.18.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
@@ -538,6 +538,65 @@ type DubbingLanguage = "en" | "zh_cn" | "ja" | "ko" | "pt"
538
538
  | "es_419" | "de" | "fr" | "it" | "ru" | "th"
539
539
  /** Unqualified Arabic, not one of the country dialects. */
540
540
  | "ar" | "tr" | "vi" | "id" | (string & {});
541
+ /**
542
+ * One target-language subtitle script for /v1/dubbing.
543
+ *
544
+ * A string starting with `https://` (case-insensitive) travels as a URL; any
545
+ * other string is a local file path (Node.js only), read and uploaded. The
546
+ * split is unambiguous — a real path cannot start with `https://` — and it
547
+ * matches how `video` already accepts a path string. A `File` is accepted for
548
+ * browsers, where paths do not exist; its `.name` supplies the filename.
549
+ *
550
+ * Blobs, byte arrays and streams are deliberately NOT accepted, unlike
551
+ * `VideoInput`: the server requires an uploaded part's filename to end in
552
+ * `.srt` or `.vtt`, and those types carry no name to derive one from.
553
+ */
554
+ type SubtitleInput = string | File;
555
+ /** One language's entry in the `subtitle_preflight` map: what the pipeline
556
+ * made of that script before anything was charged. The pipeline returns these
557
+ * numbers as strings on a finished task, so every numeric field is typed
558
+ * `number | string` — read them through `Number(...)`. */
559
+ interface SubtitlePreflightReport {
560
+ /** `ok`, `review_required` or `blocked`. Open for values added later. */
561
+ status?: "ok" | "review_required" | "blocked" | (string & {});
562
+ cue_count?: number | string;
563
+ issues?: string[];
564
+ changes_count?: number | string;
565
+ /** `null`, not absent, when there is no report to link to: the key is
566
+ * always written. Test the value, never just the key's presence. */
567
+ report_url?: string | null;
568
+ [key: string]: unknown;
569
+ }
570
+ /** One language's entry in the `subtitle_export` map: how the re-timed SRT
571
+ * for that language came out. A `blocked` export does not fail the task — the
572
+ * dubbed videos are still delivered, and `subtitles` simply lacks that
573
+ * language. Numbers may arrive as strings, as in `SubtitlePreflightReport`. */
574
+ interface SubtitleExportReport {
575
+ /** `exported`, `exported_review_required` or `blocked`. Open for values
576
+ * added later. */
577
+ status?: "exported" | "exported_review_required" | "blocked" | (string & {});
578
+ /** How far the re-timed cues drifted from the delivered audio during forced
579
+ * alignment. Lower is better. */
580
+ alignment_loss?: number | string;
581
+ issues?: string[];
582
+ error?: string;
583
+ /** `null`, not absent, when there is no report to link to: the key is
584
+ * always written. Test the value, never just the key's presence. */
585
+ report_url?: string | null;
586
+ [key: string]: unknown;
587
+ }
588
+ /**
589
+ * The 202 acknowledgement from /v1/dubbing. Additive over the `SfxTask` every
590
+ * other endpoint returns — that shape is shared and stays untouched — because
591
+ * this one endpoint answers with a preflight report per submitted script.
592
+ *
593
+ * A `review_required` status here means the pipeline altered lines in the
594
+ * script that was submitted. It is the one moment a caller who never polls
595
+ * the task can still learn that, so it is worth surfacing.
596
+ */
597
+ interface DubbingTask extends SfxTask {
598
+ subtitle_preflight?: Record<string, SubtitlePreflightReport>;
599
+ }
541
600
  interface DubbingParams {
542
601
  /** Exactly one of `video` / `videoUrl`. */
543
602
  video?: VideoInput;
@@ -568,6 +627,24 @@ interface DubbingParams {
568
627
  * unaffected.
569
628
  */
570
629
  lipsync?: boolean;
630
+ /**
631
+ * One subtitle script per target language, keyed by language code:
632
+ * `{ ja: "./ja.srt", es: "https://example.com/es.vtt" }`.
633
+ *
634
+ * These are TARGET-language scripts carrying the lines you want spoken, not
635
+ * source-language transcripts. The key set must match `languages` exactly —
636
+ * that rule is enforced server-side, before anything is charged, and is
637
+ * deliberately not duplicated here: a local copy would also break a caller
638
+ * who relies on the server default `["zh_cn", "es", "fr"]` without passing
639
+ * `languages` at all.
640
+ */
641
+ subtitles?: Record<string, SubtitleInput>;
642
+ /**
643
+ * Return a re-timed SRT per language alongside the dubbed videos. Requires
644
+ * `subtitles`. The delivered audio of each language is force-aligned
645
+ * against that language's script, keeping its lines verbatim.
646
+ */
647
+ exportSrt?: boolean;
571
648
  }
572
649
  interface DubbingResult extends BaseTaskResult {
573
650
  /**
@@ -576,6 +653,15 @@ interface DubbingResult extends BaseTaskResult {
576
653
  * `video` slot — a dubbing task renders N artifacts, one per language.
577
654
  */
578
655
  outputs?: Record<string, string>;
656
+ /**
657
+ * One re-timed `.srt` URL per language. Only present when the request set
658
+ * `exportSrt`, and a language whose export was blocked is simply absent.
659
+ */
660
+ subtitles?: Record<string, string>;
661
+ /** What the pipeline made of each submitted script, keyed by language. */
662
+ subtitle_preflight?: Record<string, SubtitlePreflightReport>;
663
+ /** How each language's re-timed SRT came out, keyed by language. */
664
+ subtitle_export?: Record<string, SubtitleExportReport>;
579
665
  }
580
666
  interface VideoAnalysisParams {
581
667
  /** Exactly one of `video` / `videoUrl`. */
@@ -754,7 +840,12 @@ declare class AudioDucking {
754
840
  declare class Dubbing {
755
841
  private readonly client;
756
842
  constructor(client: SoniloClient);
757
- submit(params: DubbingParams): Promise<SfxTask>;
843
+ /** The acknowledgement carries `subtitle_preflight` when scripts were sent,
844
+ * which is why this returns `DubbingTask` rather than the shared `SfxTask`:
845
+ * a `review_required` preflight means the pipeline altered lines in the
846
+ * script that was submitted, and typing it away hides that from every
847
+ * caller who only ever sees the 202. */
848
+ submit(params: DubbingParams): Promise<DubbingTask>;
758
849
  generate(params: DubbingParams, opts?: WaitOptions): Promise<DubbingResult>;
759
850
  }
760
851
 
@@ -901,6 +992,6 @@ declare class RequestTimeoutError extends SoniloError {
901
992
  declare function download(media: SfxMedia | string | undefined, fetchFn?: typeof globalThis.fetch, timeout?: number): Promise<Uint8Array>;
902
993
 
903
994
  /** The SDK's own version. Generated by scripts/sync-versions.mjs — do not edit. */
904
- declare const VERSION = "0.17.0";
995
+ declare const VERSION = "0.18.0";
905
996
 
906
- export { APIError, type AccountServices, type AnalysisSegment, type AnalysisVariation, type AudioChunkEvent, type AudioDuckingParams, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type DuckingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StemsEntry, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoAnalysisParams, type VideoAnalysisResult, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
997
+ export { APIError, type AccountServices, type AnalysisSegment, type AnalysisVariation, type AudioChunkEvent, type AudioDuckingParams, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type DubbingTask, type DuckingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StemsEntry, type StreamEvent, type SubtitleExportReport, type SubtitleInput, type SubtitlePreflightReport, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoAnalysisParams, type VideoAnalysisResult, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
package/dist/index.d.ts CHANGED
@@ -538,6 +538,65 @@ type DubbingLanguage = "en" | "zh_cn" | "ja" | "ko" | "pt"
538
538
  | "es_419" | "de" | "fr" | "it" | "ru" | "th"
539
539
  /** Unqualified Arabic, not one of the country dialects. */
540
540
  | "ar" | "tr" | "vi" | "id" | (string & {});
541
+ /**
542
+ * One target-language subtitle script for /v1/dubbing.
543
+ *
544
+ * A string starting with `https://` (case-insensitive) travels as a URL; any
545
+ * other string is a local file path (Node.js only), read and uploaded. The
546
+ * split is unambiguous — a real path cannot start with `https://` — and it
547
+ * matches how `video` already accepts a path string. A `File` is accepted for
548
+ * browsers, where paths do not exist; its `.name` supplies the filename.
549
+ *
550
+ * Blobs, byte arrays and streams are deliberately NOT accepted, unlike
551
+ * `VideoInput`: the server requires an uploaded part's filename to end in
552
+ * `.srt` or `.vtt`, and those types carry no name to derive one from.
553
+ */
554
+ type SubtitleInput = string | File;
555
+ /** One language's entry in the `subtitle_preflight` map: what the pipeline
556
+ * made of that script before anything was charged. The pipeline returns these
557
+ * numbers as strings on a finished task, so every numeric field is typed
558
+ * `number | string` — read them through `Number(...)`. */
559
+ interface SubtitlePreflightReport {
560
+ /** `ok`, `review_required` or `blocked`. Open for values added later. */
561
+ status?: "ok" | "review_required" | "blocked" | (string & {});
562
+ cue_count?: number | string;
563
+ issues?: string[];
564
+ changes_count?: number | string;
565
+ /** `null`, not absent, when there is no report to link to: the key is
566
+ * always written. Test the value, never just the key's presence. */
567
+ report_url?: string | null;
568
+ [key: string]: unknown;
569
+ }
570
+ /** One language's entry in the `subtitle_export` map: how the re-timed SRT
571
+ * for that language came out. A `blocked` export does not fail the task — the
572
+ * dubbed videos are still delivered, and `subtitles` simply lacks that
573
+ * language. Numbers may arrive as strings, as in `SubtitlePreflightReport`. */
574
+ interface SubtitleExportReport {
575
+ /** `exported`, `exported_review_required` or `blocked`. Open for values
576
+ * added later. */
577
+ status?: "exported" | "exported_review_required" | "blocked" | (string & {});
578
+ /** How far the re-timed cues drifted from the delivered audio during forced
579
+ * alignment. Lower is better. */
580
+ alignment_loss?: number | string;
581
+ issues?: string[];
582
+ error?: string;
583
+ /** `null`, not absent, when there is no report to link to: the key is
584
+ * always written. Test the value, never just the key's presence. */
585
+ report_url?: string | null;
586
+ [key: string]: unknown;
587
+ }
588
+ /**
589
+ * The 202 acknowledgement from /v1/dubbing. Additive over the `SfxTask` every
590
+ * other endpoint returns — that shape is shared and stays untouched — because
591
+ * this one endpoint answers with a preflight report per submitted script.
592
+ *
593
+ * A `review_required` status here means the pipeline altered lines in the
594
+ * script that was submitted. It is the one moment a caller who never polls
595
+ * the task can still learn that, so it is worth surfacing.
596
+ */
597
+ interface DubbingTask extends SfxTask {
598
+ subtitle_preflight?: Record<string, SubtitlePreflightReport>;
599
+ }
541
600
  interface DubbingParams {
542
601
  /** Exactly one of `video` / `videoUrl`. */
543
602
  video?: VideoInput;
@@ -568,6 +627,24 @@ interface DubbingParams {
568
627
  * unaffected.
569
628
  */
570
629
  lipsync?: boolean;
630
+ /**
631
+ * One subtitle script per target language, keyed by language code:
632
+ * `{ ja: "./ja.srt", es: "https://example.com/es.vtt" }`.
633
+ *
634
+ * These are TARGET-language scripts carrying the lines you want spoken, not
635
+ * source-language transcripts. The key set must match `languages` exactly —
636
+ * that rule is enforced server-side, before anything is charged, and is
637
+ * deliberately not duplicated here: a local copy would also break a caller
638
+ * who relies on the server default `["zh_cn", "es", "fr"]` without passing
639
+ * `languages` at all.
640
+ */
641
+ subtitles?: Record<string, SubtitleInput>;
642
+ /**
643
+ * Return a re-timed SRT per language alongside the dubbed videos. Requires
644
+ * `subtitles`. The delivered audio of each language is force-aligned
645
+ * against that language's script, keeping its lines verbatim.
646
+ */
647
+ exportSrt?: boolean;
571
648
  }
572
649
  interface DubbingResult extends BaseTaskResult {
573
650
  /**
@@ -576,6 +653,15 @@ interface DubbingResult extends BaseTaskResult {
576
653
  * `video` slot — a dubbing task renders N artifacts, one per language.
577
654
  */
578
655
  outputs?: Record<string, string>;
656
+ /**
657
+ * One re-timed `.srt` URL per language. Only present when the request set
658
+ * `exportSrt`, and a language whose export was blocked is simply absent.
659
+ */
660
+ subtitles?: Record<string, string>;
661
+ /** What the pipeline made of each submitted script, keyed by language. */
662
+ subtitle_preflight?: Record<string, SubtitlePreflightReport>;
663
+ /** How each language's re-timed SRT came out, keyed by language. */
664
+ subtitle_export?: Record<string, SubtitleExportReport>;
579
665
  }
580
666
  interface VideoAnalysisParams {
581
667
  /** Exactly one of `video` / `videoUrl`. */
@@ -754,7 +840,12 @@ declare class AudioDucking {
754
840
  declare class Dubbing {
755
841
  private readonly client;
756
842
  constructor(client: SoniloClient);
757
- submit(params: DubbingParams): Promise<SfxTask>;
843
+ /** The acknowledgement carries `subtitle_preflight` when scripts were sent,
844
+ * which is why this returns `DubbingTask` rather than the shared `SfxTask`:
845
+ * a `review_required` preflight means the pipeline altered lines in the
846
+ * script that was submitted, and typing it away hides that from every
847
+ * caller who only ever sees the 202. */
848
+ submit(params: DubbingParams): Promise<DubbingTask>;
758
849
  generate(params: DubbingParams, opts?: WaitOptions): Promise<DubbingResult>;
759
850
  }
760
851
 
@@ -901,6 +992,6 @@ declare class RequestTimeoutError extends SoniloError {
901
992
  declare function download(media: SfxMedia | string | undefined, fetchFn?: typeof globalThis.fetch, timeout?: number): Promise<Uint8Array>;
902
993
 
903
994
  /** The SDK's own version. Generated by scripts/sync-versions.mjs — do not edit. */
904
- declare const VERSION = "0.17.0";
995
+ declare const VERSION = "0.18.0";
905
996
 
906
- export { APIError, type AccountServices, type AnalysisSegment, type AnalysisVariation, type AudioChunkEvent, type AudioDuckingParams, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type DuckingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StemsEntry, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoAnalysisParams, type VideoAnalysisResult, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
997
+ export { APIError, type AccountServices, type AnalysisSegment, type AnalysisVariation, type AudioChunkEvent, type AudioDuckingParams, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type DubbingLanguage, type DubbingParams, type DubbingResult, type DubbingTask, type DuckingResult, type ErrorEvent, GenerationError, type MusicMediaEntry, type MusicMuxEntry, type MusicTaskResult, type MusicTitle, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type SoundOutputEntry, type SoundResult, type StemsEntry, type StreamEvent, type SubtitleExportReport, type SubtitleInput, type SubtitlePreflightReport, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, TrialExhaustedError, type TrialQuota, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoAnalysisParams, type VideoAnalysisResult, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToSoundParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type VideoToVideoSoundParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
package/dist/index.js CHANGED
@@ -714,6 +714,17 @@ var AudioDucking = class {
714
714
  };
715
715
 
716
716
  // src/resources/dubbing.ts
717
+ var SUBTITLE_EXTENSIONS = [".srt", ".vtt"];
718
+ async function toSubtitleBlob(language, value) {
719
+ const name = typeof value === "string" ? value : value?.name ?? "";
720
+ const lowered = name.toLowerCase();
721
+ if (!SUBTITLE_EXTENSIONS.some((ext) => lowered.endsWith(ext))) {
722
+ throw new SoniloError(
723
+ `subtitles[${language}]: "${name}" must be an .srt or .vtt file, or an https:// URL`
724
+ );
725
+ }
726
+ return toUploadBlob(value);
727
+ }
717
728
  async function buildDubbingForm(params) {
718
729
  if (params.video === void 0 === (params.videoUrl === void 0)) {
719
730
  throw new SoniloError("Provide exactly one of video or videoUrl");
@@ -740,12 +751,34 @@ async function buildDubbingForm(params) {
740
751
  if (params.lipsync !== void 0) {
741
752
  form.set("lipsync", String(params.lipsync));
742
753
  }
754
+ if (params.subtitles !== void 0) {
755
+ for (const [language, value] of Object.entries(params.subtitles)) {
756
+ const field = `subtitles[${language}]`;
757
+ if (typeof value === "string" && value.toLowerCase().startsWith("https://")) {
758
+ form.set(field, value);
759
+ continue;
760
+ }
761
+ const { blob, filename } = await toSubtitleBlob(language, value);
762
+ form.set(field, blob, filename);
763
+ }
764
+ }
765
+ if (params.exportSrt !== void 0) {
766
+ if (params.exportSrt && Object.keys(params.subtitles ?? {}).length === 0) {
767
+ throw new SoniloError("exportSrt requires subtitles \u2014 there is nothing to align against");
768
+ }
769
+ form.set("export_srt", String(params.exportSrt));
770
+ }
743
771
  return form;
744
772
  }
745
773
  var Dubbing = class {
746
774
  constructor(client) {
747
775
  this.client = client;
748
776
  }
777
+ /** The acknowledgement carries `subtitle_preflight` when scripts were sent,
778
+ * which is why this returns `DubbingTask` rather than the shared `SfxTask`:
779
+ * a `review_required` preflight means the pipeline altered lines in the
780
+ * script that was submitted, and typing it away hides that from every
781
+ * caller who only ever sees the 202. */
749
782
  async submit(params) {
750
783
  const res = await this.client.request("/v1/dubbing", {
751
784
  method: "POST",
@@ -792,7 +825,7 @@ var VideoAnalysis = class {
792
825
  };
793
826
 
794
827
  // src/version.ts
795
- var VERSION = "0.17.0";
828
+ var VERSION = "0.18.0";
796
829
 
797
830
  // src/client.ts
798
831
  var DEFAULT_BASE_URL = "https://api.sonilo.com";