sonilo 0.2.1 → 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
@@ -51,6 +51,10 @@ interface TextToMusicParams {
51
51
  prompt: string;
52
52
  duration: number;
53
53
  segments?: Segment[];
54
+ /** "stream" (default) or "async" (required by `submit()` and `output_format: "wav"`). */
55
+ mode?: "stream" | "async";
56
+ /** Container for the async result. `wav` requires `mode: "async"`. Defaults to m4a server-side. */
57
+ outputFormat?: "m4a" | "wav";
54
58
  /** Bounds the stream: aborting this cancels the in-flight generation.
55
59
  * Passed straight through to `fetch` — it is never rewrapped as
56
60
  * RequestTimeoutError, since the client's own absolute timeout does not
@@ -67,8 +71,28 @@ interface VideoToMusicParams {
67
71
  /** Bounds the stream: aborting this cancels the in-flight generation.
68
72
  * Passed straight through to `fetch` — it is never rewrapped as
69
73
  * RequestTimeoutError, since the client's own absolute timeout does not
70
- * apply to streaming music generation. */
74
+ * apply to streaming music generation. Only meaningful for `stream()`/
75
+ * `generate()`; `submit()` ignores it. */
71
76
  signal?: AbortSignal;
77
+ /** "stream" (the default, used by `stream()`/`generate()`) or "async"
78
+ * (required for `submit()`, and for `isolateVocals`). Only consulted by
79
+ * `submit()` — `stream()`/`generate()` always request a stream. */
80
+ mode?: "stream" | "async";
81
+ /** Split the generated track into a vocals-only stem alongside the mix.
82
+ * Requires `mode: "async"`; if `mode` is left unset it defaults to
83
+ * "async" automatically. Only usable via `submit()` — the backend
84
+ * rejects it on the plain stream. */
85
+ isolateVocals?: boolean;
86
+ /** Keep the source speech/vocals in the async result. Current name for
87
+ * `isolateVocals`; both are accepted and OR'd server-side. Requires
88
+ * `mode: "async"` (auto-selected by `submit()`). */
89
+ preserveSpeech?: boolean;
90
+ /** Container for the async result. `wav` requires async. Defaults to m4a. */
91
+ outputFormat?: "m4a" | "wav";
92
+ /** Duck the generated music under the source voice at finalize time.
93
+ * Default-ON server-side in async mode: leave unset to keep it on, pass
94
+ * `false` to opt out. Free, best-effort; only valid on `submit()`. */
95
+ ducking?: boolean;
72
96
  }
73
97
  interface AccountServices {
74
98
  available_services: string[];
@@ -120,20 +144,29 @@ interface SfxError {
120
144
  code?: string;
121
145
  message?: string;
122
146
  }
123
- /** State of an SFX task (`tasks.get`) or its final result (`wait`/`generate`). */
124
- interface SfxResult {
147
+ /**
148
+ * Common shape of any polled task (`tasks.get`/`tasks.wait`), regardless of
149
+ * which endpoint created it. `Tasks.get`/`Tasks.wait` are generic over this so
150
+ * each endpoint's result type (e.g. `SfxResult`, `MusicTaskResult`) can add
151
+ * its own `audio`/media fields while sharing the status/error/refund
152
+ * bookkeeping the poller relies on.
153
+ */
154
+ interface BaseTaskResult {
125
155
  task_id: string;
126
156
  type?: string;
127
157
  status: "processing" | "succeeded" | "failed" | (string & {});
128
- audio?: SfxMedia;
129
- /** Kept for backward compatibility; no longer populated — video-to-sfx returns audio only. */
130
- video?: SfxMedia;
131
158
  /** Only present when the account's task-field whitelist enables cost. */
132
159
  cost?: number;
133
160
  error?: SfxError;
134
161
  refunded?: boolean;
135
162
  [key: string]: unknown;
136
163
  }
164
+ /** State of an SFX task (`tasks.get`) or its final result (`wait`/`generate`). */
165
+ interface SfxResult extends BaseTaskResult {
166
+ audio?: SfxMedia;
167
+ /** Kept for backward compatibility; no longer populated — video-to-sfx returns audio only. */
168
+ video?: SfxMedia;
169
+ }
137
170
  interface TextToSfxParams {
138
171
  prompt: string;
139
172
  duration: number;
@@ -146,12 +179,66 @@ interface VideoToSfxParams {
146
179
  segments?: SfxSegment[];
147
180
  audioFormat?: SfxAudioFormat;
148
181
  }
182
+ /** One decoded audio stream of an async video-to-music result. Unlike SFX,
183
+ * `audio` on a music task is always an array — even without `isolateVocals` —
184
+ * since a music generation can carry more than one output stream. */
185
+ interface MusicMediaEntry extends SfxMedia {
186
+ stream_index: number;
187
+ sample_rate?: number;
188
+ channels?: number;
189
+ }
190
+ /** One muxed audio+video-aligned output, present only when `isolateVocals`
191
+ * is set. */
192
+ interface MusicMuxEntry extends SfxMedia {
193
+ stream_index: number;
194
+ }
195
+ interface MusicTitle {
196
+ title: string;
197
+ summary?: string;
198
+ display_tags?: string[];
199
+ }
200
+ /** State of an async video-to-music task (`tasks.get`) or its final result
201
+ * (`tasks.wait<MusicTaskResult>()`). Only reachable via `videoToMusic.submit()`
202
+ * with `mode: "async"`. */
203
+ interface MusicTaskResult extends BaseTaskResult {
204
+ audio?: MusicMediaEntry[];
205
+ /** Vocals-only stem; present only when `isolateVocals` was requested. */
206
+ vocals?: SfxMedia;
207
+ /** Muxed output per stream; present only when `isolateVocals` was requested. */
208
+ mux?: MusicMuxEntry[];
209
+ /** Music ducked under the source voice; present only when `ducking` ran. */
210
+ ducked?: MusicMediaEntry[];
211
+ title?: MusicTitle;
212
+ duration_seconds?: number;
213
+ }
149
214
  interface WaitOptions {
150
215
  /** Milliseconds between polls. Default 2000. */
151
216
  pollInterval?: number;
152
217
  /** Overall deadline in milliseconds. Default 600000. */
153
218
  timeout?: number;
154
219
  }
220
+ /** Result of an async video-to-video task (`videoToVideoMusic`/`videoToVideoSfx`):
221
+ * a re-hosted video with generated music or SFX muxed in. */
222
+ interface VideoResult extends BaseTaskResult {
223
+ video?: SfxMedia;
224
+ duration_seconds?: number;
225
+ }
226
+ interface VideoToVideoMusicParams {
227
+ video?: VideoInput;
228
+ videoUrl?: string;
229
+ prompt?: string;
230
+ /** Keep the source speech/vocals in the output. Both this and the legacy
231
+ * `isolateVocals` are accepted and OR'd server-side. */
232
+ preserveSpeech?: boolean;
233
+ /** @deprecated Legacy alias for `preserveSpeech`. */
234
+ isolateVocals?: boolean;
235
+ }
236
+ interface VideoToVideoSfxParams {
237
+ video?: VideoInput;
238
+ videoUrl?: string;
239
+ prompt?: string;
240
+ segments?: SfxSegment[];
241
+ }
155
242
 
156
243
  declare class Account {
157
244
  private readonly client;
@@ -165,10 +252,22 @@ declare class Account {
165
252
  declare class Tasks {
166
253
  private readonly client;
167
254
  constructor(client: SoniloClient);
168
- /** Fetch current task state. Never throws on a failed status. */
169
- get(taskId: string): Promise<SfxResult>;
170
- /** Poll until the task is terminal; throw on failure or deadline. */
171
- wait(taskId: string, opts?: WaitOptions): Promise<SfxResult>;
255
+ /**
256
+ * Fetch current task state. Never throws on a failed status.
257
+ *
258
+ * Generic over the result shape so callers can request the endpoint-
259
+ * specific type, e.g. `client.tasks.get<MusicTaskResult>(taskId)`.
260
+ * Defaults to `SfxResult` for back-compat.
261
+ */
262
+ get<T extends BaseTaskResult = SfxResult>(taskId: string): Promise<T>;
263
+ /**
264
+ * Poll until the task is terminal; throw on failure or deadline.
265
+ *
266
+ * Generic over the result shape, e.g.
267
+ * `client.tasks.wait<MusicTaskResult>(taskId)`. Defaults to `SfxResult`
268
+ * for back-compat.
269
+ */
270
+ wait<T extends BaseTaskResult = SfxResult>(taskId: string, opts?: WaitOptions): Promise<T>;
172
271
  }
173
272
 
174
273
  declare class TextToMusic {
@@ -178,6 +277,12 @@ declare class TextToMusic {
178
277
  stream(params: TextToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
179
278
  /** Generate and buffer the whole track; throws GenerationError on stream errors. */
180
279
  generate(params: TextToMusicParams): Promise<Track>;
280
+ /**
281
+ * Submit an async text-to-music task; poll with
282
+ * `client.tasks.wait<MusicTaskResult>(task.task_id)`. Required for
283
+ * `outputFormat: "wav"`. `stream()`/`generate()` remain the streaming path.
284
+ */
285
+ submit(params: TextToMusicParams): Promise<SfxTask>;
181
286
  }
182
287
 
183
288
  declare class VideoToMusic {
@@ -185,6 +290,13 @@ declare class VideoToMusic {
185
290
  constructor(client: SoniloClient);
186
291
  stream(params: VideoToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
187
292
  generate(params: VideoToMusicParams): Promise<Track>;
293
+ /**
294
+ * Submit an async video-to-music task; poll its result with
295
+ * `client.tasks.wait<MusicTaskResult>(task.task_id)`. Required for
296
+ * `isolateVocals` — the backend rejects vocal isolation on the plain
297
+ * stream, and it only ever runs in async mode.
298
+ */
299
+ submit(params: VideoToMusicParams): Promise<SfxTask>;
188
300
  }
189
301
 
190
302
  declare class TextToSfx {
@@ -201,6 +313,25 @@ declare class VideoToSfx {
201
313
  generate(params: VideoToSfxParams, opts?: WaitOptions): Promise<SfxResult>;
202
314
  }
203
315
 
316
+ /** Generate an original score for a video and get back a re-hosted video with
317
+ * the music muxed in. Async only: `submit()` returns a task ack; poll with
318
+ * `client.tasks.wait<VideoResult>(id)`, or use `generate()` to do both. */
319
+ declare class VideoToVideoMusic {
320
+ private readonly client;
321
+ constructor(client: SoniloClient);
322
+ submit(params: VideoToVideoMusicParams): Promise<SfxTask>;
323
+ generate(params: VideoToVideoMusicParams, opts?: WaitOptions): Promise<VideoResult>;
324
+ }
325
+
326
+ /** Generate sound effects for a video and get back a re-hosted video with the
327
+ * SFX muxed in. Async only. */
328
+ declare class VideoToVideoSfx {
329
+ private readonly client;
330
+ constructor(client: SoniloClient);
331
+ submit(params: VideoToVideoSfxParams): Promise<SfxTask>;
332
+ generate(params: VideoToVideoSfxParams, opts?: WaitOptions): Promise<VideoResult>;
333
+ }
334
+
204
335
  interface SoniloClientOptions {
205
336
  /** Defaults to the SONILO_API_KEY environment variable (Node.js only). */
206
337
  apiKey?: string;
@@ -224,6 +355,8 @@ declare class SoniloClient {
224
355
  readonly videoToMusic: VideoToMusic;
225
356
  readonly textToSfx: TextToSfx;
226
357
  readonly videoToSfx: VideoToSfx;
358
+ readonly videoToVideoMusic: VideoToVideoMusic;
359
+ readonly videoToVideoSfx: VideoToVideoSfx;
227
360
  constructor(options?: SoniloClientOptions);
228
361
  /**
229
362
  * Perform an authenticated request; throws a typed error on non-2xx.
@@ -292,6 +425,6 @@ declare class RequestTimeoutError extends SoniloError {
292
425
  /** Fetch a result media file. The URL is presigned — no API key is sent. */
293
426
  declare function download(media: SfxMedia | undefined, fetchFn?: typeof globalThis.fetch, timeout?: number): Promise<Uint8Array>;
294
427
 
295
- declare const VERSION = "0.2.0";
428
+ declare const VERSION = "0.4.0";
296
429
 
297
- export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type ErrorEvent, GenerationError, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoToMusicParams, type VideoToSfxParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
430
+ export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, 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 StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
package/dist/index.d.ts CHANGED
@@ -51,6 +51,10 @@ interface TextToMusicParams {
51
51
  prompt: string;
52
52
  duration: number;
53
53
  segments?: Segment[];
54
+ /** "stream" (default) or "async" (required by `submit()` and `output_format: "wav"`). */
55
+ mode?: "stream" | "async";
56
+ /** Container for the async result. `wav` requires `mode: "async"`. Defaults to m4a server-side. */
57
+ outputFormat?: "m4a" | "wav";
54
58
  /** Bounds the stream: aborting this cancels the in-flight generation.
55
59
  * Passed straight through to `fetch` — it is never rewrapped as
56
60
  * RequestTimeoutError, since the client's own absolute timeout does not
@@ -67,8 +71,28 @@ interface VideoToMusicParams {
67
71
  /** Bounds the stream: aborting this cancels the in-flight generation.
68
72
  * Passed straight through to `fetch` — it is never rewrapped as
69
73
  * RequestTimeoutError, since the client's own absolute timeout does not
70
- * apply to streaming music generation. */
74
+ * apply to streaming music generation. Only meaningful for `stream()`/
75
+ * `generate()`; `submit()` ignores it. */
71
76
  signal?: AbortSignal;
77
+ /** "stream" (the default, used by `stream()`/`generate()`) or "async"
78
+ * (required for `submit()`, and for `isolateVocals`). Only consulted by
79
+ * `submit()` — `stream()`/`generate()` always request a stream. */
80
+ mode?: "stream" | "async";
81
+ /** Split the generated track into a vocals-only stem alongside the mix.
82
+ * Requires `mode: "async"`; if `mode` is left unset it defaults to
83
+ * "async" automatically. Only usable via `submit()` — the backend
84
+ * rejects it on the plain stream. */
85
+ isolateVocals?: boolean;
86
+ /** Keep the source speech/vocals in the async result. Current name for
87
+ * `isolateVocals`; both are accepted and OR'd server-side. Requires
88
+ * `mode: "async"` (auto-selected by `submit()`). */
89
+ preserveSpeech?: boolean;
90
+ /** Container for the async result. `wav` requires async. Defaults to m4a. */
91
+ outputFormat?: "m4a" | "wav";
92
+ /** Duck the generated music under the source voice at finalize time.
93
+ * Default-ON server-side in async mode: leave unset to keep it on, pass
94
+ * `false` to opt out. Free, best-effort; only valid on `submit()`. */
95
+ ducking?: boolean;
72
96
  }
73
97
  interface AccountServices {
74
98
  available_services: string[];
@@ -120,20 +144,29 @@ interface SfxError {
120
144
  code?: string;
121
145
  message?: string;
122
146
  }
123
- /** State of an SFX task (`tasks.get`) or its final result (`wait`/`generate`). */
124
- interface SfxResult {
147
+ /**
148
+ * Common shape of any polled task (`tasks.get`/`tasks.wait`), regardless of
149
+ * which endpoint created it. `Tasks.get`/`Tasks.wait` are generic over this so
150
+ * each endpoint's result type (e.g. `SfxResult`, `MusicTaskResult`) can add
151
+ * its own `audio`/media fields while sharing the status/error/refund
152
+ * bookkeeping the poller relies on.
153
+ */
154
+ interface BaseTaskResult {
125
155
  task_id: string;
126
156
  type?: string;
127
157
  status: "processing" | "succeeded" | "failed" | (string & {});
128
- audio?: SfxMedia;
129
- /** Kept for backward compatibility; no longer populated — video-to-sfx returns audio only. */
130
- video?: SfxMedia;
131
158
  /** Only present when the account's task-field whitelist enables cost. */
132
159
  cost?: number;
133
160
  error?: SfxError;
134
161
  refunded?: boolean;
135
162
  [key: string]: unknown;
136
163
  }
164
+ /** State of an SFX task (`tasks.get`) or its final result (`wait`/`generate`). */
165
+ interface SfxResult extends BaseTaskResult {
166
+ audio?: SfxMedia;
167
+ /** Kept for backward compatibility; no longer populated — video-to-sfx returns audio only. */
168
+ video?: SfxMedia;
169
+ }
137
170
  interface TextToSfxParams {
138
171
  prompt: string;
139
172
  duration: number;
@@ -146,12 +179,66 @@ interface VideoToSfxParams {
146
179
  segments?: SfxSegment[];
147
180
  audioFormat?: SfxAudioFormat;
148
181
  }
182
+ /** One decoded audio stream of an async video-to-music result. Unlike SFX,
183
+ * `audio` on a music task is always an array — even without `isolateVocals` —
184
+ * since a music generation can carry more than one output stream. */
185
+ interface MusicMediaEntry extends SfxMedia {
186
+ stream_index: number;
187
+ sample_rate?: number;
188
+ channels?: number;
189
+ }
190
+ /** One muxed audio+video-aligned output, present only when `isolateVocals`
191
+ * is set. */
192
+ interface MusicMuxEntry extends SfxMedia {
193
+ stream_index: number;
194
+ }
195
+ interface MusicTitle {
196
+ title: string;
197
+ summary?: string;
198
+ display_tags?: string[];
199
+ }
200
+ /** State of an async video-to-music task (`tasks.get`) or its final result
201
+ * (`tasks.wait<MusicTaskResult>()`). Only reachable via `videoToMusic.submit()`
202
+ * with `mode: "async"`. */
203
+ interface MusicTaskResult extends BaseTaskResult {
204
+ audio?: MusicMediaEntry[];
205
+ /** Vocals-only stem; present only when `isolateVocals` was requested. */
206
+ vocals?: SfxMedia;
207
+ /** Muxed output per stream; present only when `isolateVocals` was requested. */
208
+ mux?: MusicMuxEntry[];
209
+ /** Music ducked under the source voice; present only when `ducking` ran. */
210
+ ducked?: MusicMediaEntry[];
211
+ title?: MusicTitle;
212
+ duration_seconds?: number;
213
+ }
149
214
  interface WaitOptions {
150
215
  /** Milliseconds between polls. Default 2000. */
151
216
  pollInterval?: number;
152
217
  /** Overall deadline in milliseconds. Default 600000. */
153
218
  timeout?: number;
154
219
  }
220
+ /** Result of an async video-to-video task (`videoToVideoMusic`/`videoToVideoSfx`):
221
+ * a re-hosted video with generated music or SFX muxed in. */
222
+ interface VideoResult extends BaseTaskResult {
223
+ video?: SfxMedia;
224
+ duration_seconds?: number;
225
+ }
226
+ interface VideoToVideoMusicParams {
227
+ video?: VideoInput;
228
+ videoUrl?: string;
229
+ prompt?: string;
230
+ /** Keep the source speech/vocals in the output. Both this and the legacy
231
+ * `isolateVocals` are accepted and OR'd server-side. */
232
+ preserveSpeech?: boolean;
233
+ /** @deprecated Legacy alias for `preserveSpeech`. */
234
+ isolateVocals?: boolean;
235
+ }
236
+ interface VideoToVideoSfxParams {
237
+ video?: VideoInput;
238
+ videoUrl?: string;
239
+ prompt?: string;
240
+ segments?: SfxSegment[];
241
+ }
155
242
 
156
243
  declare class Account {
157
244
  private readonly client;
@@ -165,10 +252,22 @@ declare class Account {
165
252
  declare class Tasks {
166
253
  private readonly client;
167
254
  constructor(client: SoniloClient);
168
- /** Fetch current task state. Never throws on a failed status. */
169
- get(taskId: string): Promise<SfxResult>;
170
- /** Poll until the task is terminal; throw on failure or deadline. */
171
- wait(taskId: string, opts?: WaitOptions): Promise<SfxResult>;
255
+ /**
256
+ * Fetch current task state. Never throws on a failed status.
257
+ *
258
+ * Generic over the result shape so callers can request the endpoint-
259
+ * specific type, e.g. `client.tasks.get<MusicTaskResult>(taskId)`.
260
+ * Defaults to `SfxResult` for back-compat.
261
+ */
262
+ get<T extends BaseTaskResult = SfxResult>(taskId: string): Promise<T>;
263
+ /**
264
+ * Poll until the task is terminal; throw on failure or deadline.
265
+ *
266
+ * Generic over the result shape, e.g.
267
+ * `client.tasks.wait<MusicTaskResult>(taskId)`. Defaults to `SfxResult`
268
+ * for back-compat.
269
+ */
270
+ wait<T extends BaseTaskResult = SfxResult>(taskId: string, opts?: WaitOptions): Promise<T>;
172
271
  }
173
272
 
174
273
  declare class TextToMusic {
@@ -178,6 +277,12 @@ declare class TextToMusic {
178
277
  stream(params: TextToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
179
278
  /** Generate and buffer the whole track; throws GenerationError on stream errors. */
180
279
  generate(params: TextToMusicParams): Promise<Track>;
280
+ /**
281
+ * Submit an async text-to-music task; poll with
282
+ * `client.tasks.wait<MusicTaskResult>(task.task_id)`. Required for
283
+ * `outputFormat: "wav"`. `stream()`/`generate()` remain the streaming path.
284
+ */
285
+ submit(params: TextToMusicParams): Promise<SfxTask>;
181
286
  }
182
287
 
183
288
  declare class VideoToMusic {
@@ -185,6 +290,13 @@ declare class VideoToMusic {
185
290
  constructor(client: SoniloClient);
186
291
  stream(params: VideoToMusicParams): AsyncGenerator<StreamEvent, void, undefined>;
187
292
  generate(params: VideoToMusicParams): Promise<Track>;
293
+ /**
294
+ * Submit an async video-to-music task; poll its result with
295
+ * `client.tasks.wait<MusicTaskResult>(task.task_id)`. Required for
296
+ * `isolateVocals` — the backend rejects vocal isolation on the plain
297
+ * stream, and it only ever runs in async mode.
298
+ */
299
+ submit(params: VideoToMusicParams): Promise<SfxTask>;
188
300
  }
189
301
 
190
302
  declare class TextToSfx {
@@ -201,6 +313,25 @@ declare class VideoToSfx {
201
313
  generate(params: VideoToSfxParams, opts?: WaitOptions): Promise<SfxResult>;
202
314
  }
203
315
 
316
+ /** Generate an original score for a video and get back a re-hosted video with
317
+ * the music muxed in. Async only: `submit()` returns a task ack; poll with
318
+ * `client.tasks.wait<VideoResult>(id)`, or use `generate()` to do both. */
319
+ declare class VideoToVideoMusic {
320
+ private readonly client;
321
+ constructor(client: SoniloClient);
322
+ submit(params: VideoToVideoMusicParams): Promise<SfxTask>;
323
+ generate(params: VideoToVideoMusicParams, opts?: WaitOptions): Promise<VideoResult>;
324
+ }
325
+
326
+ /** Generate sound effects for a video and get back a re-hosted video with the
327
+ * SFX muxed in. Async only. */
328
+ declare class VideoToVideoSfx {
329
+ private readonly client;
330
+ constructor(client: SoniloClient);
331
+ submit(params: VideoToVideoSfxParams): Promise<SfxTask>;
332
+ generate(params: VideoToVideoSfxParams, opts?: WaitOptions): Promise<VideoResult>;
333
+ }
334
+
204
335
  interface SoniloClientOptions {
205
336
  /** Defaults to the SONILO_API_KEY environment variable (Node.js only). */
206
337
  apiKey?: string;
@@ -224,6 +355,8 @@ declare class SoniloClient {
224
355
  readonly videoToMusic: VideoToMusic;
225
356
  readonly textToSfx: TextToSfx;
226
357
  readonly videoToSfx: VideoToSfx;
358
+ readonly videoToVideoMusic: VideoToVideoMusic;
359
+ readonly videoToVideoSfx: VideoToVideoSfx;
227
360
  constructor(options?: SoniloClientOptions);
228
361
  /**
229
362
  * Perform an authenticated request; throws a typed error on non-2xx.
@@ -292,6 +425,6 @@ declare class RequestTimeoutError extends SoniloError {
292
425
  /** Fetch a result media file. The URL is presigned — no API key is sent. */
293
426
  declare function download(media: SfxMedia | undefined, fetchFn?: typeof globalThis.fetch, timeout?: number): Promise<Uint8Array>;
294
427
 
295
- declare const VERSION = "0.2.0";
428
+ declare const VERSION = "0.4.0";
296
429
 
297
- export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, type ErrorEvent, GenerationError, PaymentRequiredError, RateLimitError, RequestTimeoutError, type Segment, type SegmentLabel, type SfxAudioFormat, type SfxError, type SfxMedia, type SfxResult, type SfxSegment, type SfxTask, SoniloClient, type SoniloClientOptions, SoniloError, type StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoToMusicParams, type VideoToSfxParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
430
+ export { APIError, type AccountServices, type AudioChunkEvent, AuthenticationError, BadRequestError, type BaseTaskResult, type CompleteEvent, type CostEvent, type CostInfo, DEFAULT_TIMEOUT_MS, type DailyUsage, 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 StreamEvent, TaskFailedError, TaskTimeoutError, type TextToMusicParams, type TextToSfxParams, type TitleEvent, type Track, type UnknownEvent, type UsageResponse, type UsageSummary, VERSION, type VideoInput, type VideoResult, type VideoToMusicParams, type VideoToSfxParams, type VideoToVideoMusicParams, type VideoToVideoSfxParams, type WaitOptions, download, isAudioChunkEvent, isErrorEvent };
package/dist/index.js CHANGED
@@ -136,12 +136,24 @@ var Tasks = class {
136
136
  constructor(client) {
137
137
  this.client = client;
138
138
  }
139
- /** Fetch current task state. Never throws on a failed status. */
139
+ /**
140
+ * Fetch current task state. Never throws on a failed status.
141
+ *
142
+ * Generic over the result shape so callers can request the endpoint-
143
+ * specific type, e.g. `client.tasks.get<MusicTaskResult>(taskId)`.
144
+ * Defaults to `SfxResult` for back-compat.
145
+ */
140
146
  async get(taskId) {
141
147
  const res = await this.client.request(`/v1/tasks/${encodeURIComponent(taskId)}`);
142
148
  return await res.json();
143
149
  }
144
- /** Poll until the task is terminal; throw on failure or deadline. */
150
+ /**
151
+ * Poll until the task is terminal; throw on failure or deadline.
152
+ *
153
+ * Generic over the result shape, e.g.
154
+ * `client.tasks.wait<MusicTaskResult>(taskId)`. Defaults to `SfxResult`
155
+ * for back-compat.
156
+ */
145
157
  async wait(taskId, opts = {}) {
146
158
  const pollInterval = opts.pollInterval ?? DEFAULT_POLL_INTERVAL_MS;
147
159
  const timeout = opts.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
@@ -283,6 +295,32 @@ var TextToMusic = class {
283
295
  generate(params) {
284
296
  return collectTrack(this.stream(params));
285
297
  }
298
+ /**
299
+ * Submit an async text-to-music task; poll with
300
+ * `client.tasks.wait<MusicTaskResult>(task.task_id)`. Required for
301
+ * `outputFormat: "wav"`. `stream()`/`generate()` remain the streaming path.
302
+ */
303
+ async submit(params) {
304
+ const mode = params.mode ?? "async";
305
+ if (mode !== "async") {
306
+ throw new SoniloError('submit() requires mode: "async"');
307
+ }
308
+ const form = new FormData();
309
+ form.set("prompt", params.prompt);
310
+ form.set("duration", String(params.duration));
311
+ if (params.segments !== void 0) {
312
+ form.set("segments", JSON.stringify(params.segments));
313
+ }
314
+ form.set("mode", mode);
315
+ if (params.outputFormat !== void 0) {
316
+ form.set("output_format", params.outputFormat);
317
+ }
318
+ const res = await this.client.request("/v1/text-to-music", {
319
+ method: "POST",
320
+ body: form
321
+ });
322
+ return await res.json();
323
+ }
286
324
  };
287
325
 
288
326
  // src/upload.ts
@@ -354,6 +392,54 @@ var VideoToMusic = class {
354
392
  generate(params) {
355
393
  return collectTrack(this.stream(params));
356
394
  }
395
+ /**
396
+ * Submit an async video-to-music task; poll its result with
397
+ * `client.tasks.wait<MusicTaskResult>(task.task_id)`. Required for
398
+ * `isolateVocals` — the backend rejects vocal isolation on the plain
399
+ * stream, and it only ever runs in async mode.
400
+ */
401
+ async submit(params) {
402
+ if (params.video === void 0 === (params.videoUrl === void 0)) {
403
+ throw new SoniloError("Provide exactly one of video or videoUrl");
404
+ }
405
+ let mode = params.mode;
406
+ const needsAsync = params.isolateVocals || params.preserveSpeech || params.ducking !== void 0 || params.outputFormat === "wav";
407
+ if (mode === void 0) mode = "async";
408
+ if (needsAsync && mode !== "async") {
409
+ throw new SoniloError(
410
+ 'isolateVocals/preserveSpeech/ducking/outputFormat "wav" require mode: "async"'
411
+ );
412
+ }
413
+ const form = new FormData();
414
+ if (params.video !== void 0) {
415
+ const { blob, filename } = await toUploadBlob(params.video);
416
+ form.set("video", blob, filename);
417
+ } else {
418
+ form.set("video_url", params.videoUrl);
419
+ }
420
+ if (params.prompt !== void 0) form.set("prompt", params.prompt);
421
+ if (params.segments !== void 0) {
422
+ form.set("segments", JSON.stringify(params.segments));
423
+ }
424
+ form.set("mode", mode);
425
+ if (params.preserveSpeech !== void 0) {
426
+ form.set("preserve_speech", String(params.preserveSpeech));
427
+ }
428
+ if (params.isolateVocals !== void 0) {
429
+ form.set("isolate_vocals", String(params.isolateVocals));
430
+ }
431
+ if (params.outputFormat !== void 0) {
432
+ form.set("output_format", params.outputFormat);
433
+ }
434
+ if (params.ducking !== void 0) {
435
+ form.set("ducking", String(params.ducking));
436
+ }
437
+ const res = await this.client.request("/v1/video-to-music", {
438
+ method: "POST",
439
+ body: form
440
+ });
441
+ return await res.json();
442
+ }
357
443
  };
358
444
 
359
445
  // src/resources/textToSfx.ts
@@ -411,8 +497,75 @@ var VideoToSfx = class {
411
497
  }
412
498
  };
413
499
 
500
+ // src/resources/videoToVideoMusic.ts
501
+ var VideoToVideoMusic = class {
502
+ constructor(client) {
503
+ this.client = client;
504
+ }
505
+ async submit(params) {
506
+ if (params.video === void 0 === (params.videoUrl === void 0)) {
507
+ throw new SoniloError("Provide exactly one of video or videoUrl");
508
+ }
509
+ const form = new FormData();
510
+ if (params.video !== void 0) {
511
+ const { blob, filename } = await toUploadBlob(params.video);
512
+ form.set("video", blob, filename);
513
+ } else {
514
+ form.set("video_url", params.videoUrl);
515
+ }
516
+ if (params.prompt !== void 0) form.set("prompt", params.prompt);
517
+ if (params.preserveSpeech !== void 0) {
518
+ form.set("preserve_speech", String(params.preserveSpeech));
519
+ }
520
+ if (params.isolateVocals !== void 0) {
521
+ form.set("isolate_vocals", String(params.isolateVocals));
522
+ }
523
+ const res = await this.client.request("/v1/video-to-video-music", {
524
+ method: "POST",
525
+ body: form
526
+ });
527
+ return await res.json();
528
+ }
529
+ async generate(params, opts) {
530
+ const task = await this.submit(params);
531
+ return this.client.tasks.wait(task.task_id, opts);
532
+ }
533
+ };
534
+
535
+ // src/resources/videoToVideoSfx.ts
536
+ var VideoToVideoSfx = class {
537
+ constructor(client) {
538
+ this.client = client;
539
+ }
540
+ async submit(params) {
541
+ if (params.video === void 0 === (params.videoUrl === void 0)) {
542
+ throw new SoniloError("Provide exactly one of video or videoUrl");
543
+ }
544
+ const form = new FormData();
545
+ if (params.video !== void 0) {
546
+ const { blob, filename } = await toUploadBlob(params.video);
547
+ form.set("video", blob, filename);
548
+ } else {
549
+ form.set("video_url", params.videoUrl);
550
+ }
551
+ if (params.prompt !== void 0) form.set("prompt", params.prompt);
552
+ if (params.segments !== void 0) {
553
+ form.set("segments", JSON.stringify(params.segments));
554
+ }
555
+ const res = await this.client.request("/v1/video-to-video-sfx", {
556
+ method: "POST",
557
+ body: form
558
+ });
559
+ return await res.json();
560
+ }
561
+ async generate(params, opts) {
562
+ const task = await this.submit(params);
563
+ return this.client.tasks.wait(task.task_id, opts);
564
+ }
565
+ };
566
+
414
567
  // src/version.ts
415
- var VERSION = "0.2.0";
568
+ var VERSION = "0.4.0";
416
569
 
417
570
  // src/client.ts
418
571
  var DEFAULT_BASE_URL = "https://api.sonilo.com";
@@ -436,6 +589,8 @@ var SoniloClient = class {
436
589
  this.videoToMusic = new VideoToMusic(this);
437
590
  this.textToSfx = new TextToSfx(this);
438
591
  this.videoToSfx = new VideoToSfx(this);
592
+ this.videoToVideoMusic = new VideoToVideoMusic(this);
593
+ this.videoToVideoSfx = new VideoToVideoSfx(this);
439
594
  }
440
595
  /**
441
596
  * Perform an authenticated request; throws a typed error on non-2xx.