audiopod 2.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/dist/index.js ADDED
@@ -0,0 +1,956 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ APIError: () => APIError,
34
+ AudioPod: () => AudioPod,
35
+ AudioPodError: () => AudioPodError,
36
+ AuthenticationError: () => AuthenticationError,
37
+ InsufficientBalanceError: () => InsufficientBalanceError,
38
+ RateLimitError: () => RateLimitError,
39
+ VERSION: () => VERSION2,
40
+ default: () => AudioPod
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/client.ts
45
+ var import_axios = __toESM(require("axios"));
46
+ var import_form_data = __toESM(require("form-data"));
47
+ var fs = __toESM(require("fs"));
48
+ var path = __toESM(require("path"));
49
+
50
+ // src/errors.ts
51
+ var AudioPodError = class _AudioPodError extends Error {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = "AudioPodError";
55
+ Object.setPrototypeOf(this, _AudioPodError.prototype);
56
+ }
57
+ };
58
+ var AuthenticationError = class _AuthenticationError extends AudioPodError {
59
+ constructor(message = "Authentication failed") {
60
+ super(message);
61
+ this.name = "AuthenticationError";
62
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
63
+ }
64
+ };
65
+ var APIError = class _APIError extends AudioPodError {
66
+ constructor(message, statusCode) {
67
+ super(message);
68
+ this.name = "APIError";
69
+ this.statusCode = statusCode;
70
+ Object.setPrototypeOf(this, _APIError.prototype);
71
+ }
72
+ };
73
+ var RateLimitError = class _RateLimitError extends AudioPodError {
74
+ constructor(message = "Rate limit exceeded") {
75
+ super(message);
76
+ this.name = "RateLimitError";
77
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
78
+ }
79
+ };
80
+ var InsufficientBalanceError = class _InsufficientBalanceError extends AudioPodError {
81
+ constructor(message = "Insufficient wallet balance", requiredCents, availableCents) {
82
+ super(message);
83
+ this.name = "InsufficientBalanceError";
84
+ this.requiredCents = requiredCents;
85
+ this.availableCents = availableCents;
86
+ Object.setPrototypeOf(this, _InsufficientBalanceError.prototype);
87
+ }
88
+ };
89
+
90
+ // src/resources/transcription.ts
91
+ var POLL_INTERVAL = 3e3;
92
+ var DEFAULT_TIMEOUT = 6e5;
93
+ var Transcription = class {
94
+ constructor(client) {
95
+ this.client = client;
96
+ }
97
+ /**
98
+ * Create a transcription job from URL(s)
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const job = await client.transcription.create({
103
+ * url: 'https://youtube.com/watch?v=...',
104
+ * speakerDiarization: true,
105
+ * });
106
+ * ```
107
+ */
108
+ async create(params) {
109
+ const urls = params.urls || (params.url ? [params.url] : []);
110
+ if (urls.length === 0) {
111
+ throw new Error("At least one URL is required");
112
+ }
113
+ return this.client.post("/api/v1/transcribe/transcribe", {
114
+ source_urls: urls,
115
+ language: params.language,
116
+ enable_speaker_diarization: params.speakerDiarization ?? false,
117
+ min_speakers: params.minSpeakers,
118
+ max_speakers: params.maxSpeakers,
119
+ enable_word_timestamps: params.wordTimestamps ?? true
120
+ });
121
+ }
122
+ /**
123
+ * Upload a file for transcription
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const job = await client.transcription.upload({
128
+ * file: './audio.mp3',
129
+ * language: 'en',
130
+ * });
131
+ * ```
132
+ */
133
+ async upload(params) {
134
+ return this.client.upload("/api/v1/transcribe/transcribe-upload", params.file, "files", {
135
+ language: params.language,
136
+ enable_speaker_diarization: params.speakerDiarization ?? false
137
+ });
138
+ }
139
+ /**
140
+ * Get a transcription job by ID
141
+ */
142
+ async get(jobId) {
143
+ return this.client.get(`/api/v1/transcribe/jobs/${jobId}`);
144
+ }
145
+ /**
146
+ * List transcription jobs
147
+ */
148
+ async list(params = {}) {
149
+ return this.client.get("/api/v1/transcribe/jobs", {
150
+ offset: params.skip ?? 0,
151
+ limit: params.limit ?? 50,
152
+ status: params.status
153
+ });
154
+ }
155
+ /**
156
+ * Delete a transcription job
157
+ */
158
+ async delete(jobId) {
159
+ await this.client.delete(`/api/v1/transcribe/jobs/${jobId}`);
160
+ }
161
+ /**
162
+ * Get transcript content
163
+ *
164
+ * @param format - Output format: 'json', 'txt', 'srt', 'vtt'
165
+ */
166
+ async getTranscript(jobId, format = "json") {
167
+ return this.client.get(`/api/v1/transcribe/jobs/${jobId}/transcript`, { format });
168
+ }
169
+ /**
170
+ * Wait for a transcription job to complete
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * const job = await client.transcription.create({ url: '...' });
175
+ * const result = await client.transcription.waitForCompletion(job.id);
176
+ * console.log(result.status); // 'COMPLETED'
177
+ * ```
178
+ */
179
+ async waitForCompletion(jobId, timeout = DEFAULT_TIMEOUT) {
180
+ const startTime = Date.now();
181
+ while (Date.now() - startTime < timeout) {
182
+ const job = await this.get(jobId);
183
+ if (job.status === "COMPLETED") {
184
+ return job;
185
+ }
186
+ if (job.status === "FAILED") {
187
+ throw new Error(`Transcription failed: ${job.error_message || "Unknown error"}`);
188
+ }
189
+ if (job.status === "CANCELLED") {
190
+ throw new Error("Transcription was cancelled");
191
+ }
192
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
193
+ }
194
+ throw new Error(`Transcription timed out after ${timeout / 1e3}s`);
195
+ }
196
+ /**
197
+ * Create and wait for completion in one call
198
+ */
199
+ async transcribe(params) {
200
+ const job = await this.create(params);
201
+ return this.waitForCompletion(job.id || job.job_id);
202
+ }
203
+ };
204
+
205
+ // src/resources/voice.ts
206
+ var POLL_INTERVAL2 = 2e3;
207
+ var DEFAULT_TIMEOUT2 = 3e5;
208
+ var Voice = class {
209
+ constructor(client) {
210
+ this.client = client;
211
+ }
212
+ /**
213
+ * List all voices
214
+ */
215
+ async list() {
216
+ return this.client.get("/api/v1/voice/voices");
217
+ }
218
+ /**
219
+ * Get a voice by ID
220
+ */
221
+ async get(voiceId) {
222
+ return this.client.get(`/api/v1/voice/voices/${voiceId}`);
223
+ }
224
+ /**
225
+ * Create a new voice clone
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * const voice = await client.voice.create({
230
+ * name: 'My Voice',
231
+ * audioFile: './sample.mp3',
232
+ * });
233
+ * ```
234
+ */
235
+ async create(params) {
236
+ return this.client.upload("/api/v1/voice/voices", params.audioFile, "audio_file", {
237
+ name: params.name,
238
+ description: params.description
239
+ });
240
+ }
241
+ /**
242
+ * Delete a voice
243
+ */
244
+ async delete(voiceId) {
245
+ await this.client.delete(`/api/v1/voice/voices/${voiceId}`);
246
+ }
247
+ /**
248
+ * Generate speech from text
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * const job = await client.voice.speak({
253
+ * text: 'Hello, world!',
254
+ * voiceId: 123,
255
+ * });
256
+ * ```
257
+ */
258
+ async speak(params) {
259
+ return this.client.post("/api/v1/voice/tts/generate", {
260
+ text: params.text,
261
+ voice_id: params.voiceId,
262
+ speed: params.speed ?? 1
263
+ });
264
+ }
265
+ /**
266
+ * Get TTS job status
267
+ */
268
+ async getJob(jobId) {
269
+ return this.client.get(`/api/v1/voice/tts/status/${jobId}`);
270
+ }
271
+ /**
272
+ * Wait for TTS job to complete
273
+ */
274
+ async waitForCompletion(jobId, timeout = DEFAULT_TIMEOUT2) {
275
+ const startTime = Date.now();
276
+ while (Date.now() - startTime < timeout) {
277
+ const job = await this.getJob(jobId);
278
+ if (job.status === "COMPLETED") {
279
+ return job;
280
+ }
281
+ if (job.status === "FAILED") {
282
+ throw new Error(`TTS generation failed: ${job.error_message || "Unknown error"}`);
283
+ }
284
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL2));
285
+ }
286
+ throw new Error(`TTS generation timed out after ${timeout / 1e3}s`);
287
+ }
288
+ /**
289
+ * Generate speech and wait for completion
290
+ */
291
+ async generate(params) {
292
+ const job = await this.speak(params);
293
+ return this.waitForCompletion(job.id);
294
+ }
295
+ };
296
+
297
+ // src/resources/music.ts
298
+ var POLL_INTERVAL3 = 5e3;
299
+ var DEFAULT_TIMEOUT3 = 6e5;
300
+ var Music = class {
301
+ constructor(client) {
302
+ this.client = client;
303
+ }
304
+ /**
305
+ * Generate music from text
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const job = await client.music.create({
310
+ * prompt: 'Upbeat electronic dance music',
311
+ * duration: 30,
312
+ * });
313
+ * ```
314
+ */
315
+ async create(params) {
316
+ const task = params.task || (params.lyrics ? "text2music" : "prompt2instrumental");
317
+ const endpoint = `/api/v1/music/${task}`;
318
+ return this.client.post(endpoint, {
319
+ prompt: params.prompt,
320
+ lyrics: params.lyrics,
321
+ audio_duration: params.duration ?? 30,
322
+ display_name: params.displayName,
323
+ genre_preset: params.genre
324
+ });
325
+ }
326
+ /**
327
+ * Generate instrumental music
328
+ */
329
+ async instrumental(prompt, duration = 30) {
330
+ return this.create({ prompt, duration, task: "prompt2instrumental" });
331
+ }
332
+ /**
333
+ * Generate a song with vocals
334
+ */
335
+ async song(prompt, lyrics, duration = 60) {
336
+ return this.create({ prompt, lyrics, duration, task: "text2music" });
337
+ }
338
+ /**
339
+ * Generate rap music
340
+ */
341
+ async rap(prompt, lyrics, duration = 60) {
342
+ return this.create({ prompt, lyrics, duration, task: "text2rap" });
343
+ }
344
+ /**
345
+ * Get a music job by ID
346
+ */
347
+ async get(jobId) {
348
+ return this.client.get(`/api/v1/music/jobs/${jobId}/status`);
349
+ }
350
+ /**
351
+ * List music jobs
352
+ */
353
+ async list(params = {}) {
354
+ return this.client.get("/api/v1/music/jobs", {
355
+ skip: params.skip ?? 0,
356
+ limit: params.limit ?? 50,
357
+ task: params.task,
358
+ status: params.status
359
+ });
360
+ }
361
+ /**
362
+ * Delete a music job
363
+ */
364
+ async delete(jobId) {
365
+ await this.client.delete(`/api/v1/music/jobs/${jobId}`);
366
+ }
367
+ /**
368
+ * Wait for a music job to complete
369
+ */
370
+ async waitForCompletion(jobId, timeout = DEFAULT_TIMEOUT3) {
371
+ const startTime = Date.now();
372
+ while (Date.now() - startTime < timeout) {
373
+ const job = await this.get(jobId);
374
+ if (job.status === "COMPLETED") {
375
+ return job;
376
+ }
377
+ if (job.status === "FAILED") {
378
+ throw new Error(`Music generation failed: ${job.error_message || "Unknown error"}`);
379
+ }
380
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL3));
381
+ }
382
+ throw new Error(`Music generation timed out after ${timeout / 1e3}s`);
383
+ }
384
+ /**
385
+ * Generate music and wait for completion
386
+ */
387
+ async generate(params) {
388
+ const job = await this.create(params);
389
+ return this.waitForCompletion(job.id);
390
+ }
391
+ /**
392
+ * Get available genre presets
393
+ */
394
+ async getPresets() {
395
+ return this.client.get("/api/v1/music/presets");
396
+ }
397
+ };
398
+
399
+ // src/resources/stems.ts
400
+ var POLL_INTERVAL4 = 3e3;
401
+ var DEFAULT_TIMEOUT4 = 6e5;
402
+ var StemExtraction = class {
403
+ constructor(client) {
404
+ this.client = client;
405
+ }
406
+ /**
407
+ * Extract stems from audio using simple mode selection.
408
+ *
409
+ * @param params.file - Path to local audio file (MP3, WAV, FLAC, M4A, OGG)
410
+ * @param params.url - URL of audio/video (YouTube, SoundCloud, direct link)
411
+ * @param params.mode - Separation mode:
412
+ * - "single": Extract one stem (specify stem param)
413
+ * - "two": Vocals + Instrumental
414
+ * - "four": Vocals, Drums, Bass, Other (default)
415
+ * - "six": Vocals, Drums, Bass, Guitar, Piano, Other
416
+ * - "producer": 8 stems with kick, snare, hihat
417
+ * - "studio": 12 stems for professional mixing
418
+ * - "mastering": 16 stems maximum detail
419
+ * @param params.stem - For mode="single", which stem to extract
420
+ *
421
+ * @example
422
+ * ```typescript
423
+ * // From URL with 6 stems
424
+ * const job = await client.stems.extract({
425
+ * url: 'https://youtube.com/watch?v=VIDEO_ID',
426
+ * mode: 'six'
427
+ * });
428
+ *
429
+ * // From file with 4 stems
430
+ * const job = await client.stems.extract({
431
+ * file: './song.mp3',
432
+ * mode: 'four'
433
+ * });
434
+ *
435
+ * // Extract only vocals
436
+ * const job = await client.stems.extract({
437
+ * url: 'https://youtube.com/watch?v=VIDEO_ID',
438
+ * mode: 'single',
439
+ * stem: 'vocals'
440
+ * });
441
+ * ```
442
+ */
443
+ async extract(params) {
444
+ const mode = params.mode || "four";
445
+ if (!params.file && !params.url) {
446
+ throw new Error("Either file or url must be provided");
447
+ }
448
+ if (mode === "single" && !params.stem) {
449
+ throw new Error(
450
+ "stem parameter required for mode='single'. Options: vocals, drums, bass, guitar, piano, other"
451
+ );
452
+ }
453
+ const additionalFields = { mode };
454
+ if (params.stem) {
455
+ additionalFields.stem = params.stem;
456
+ }
457
+ if (params.file) {
458
+ return this.client.upload(
459
+ "/api/v1/stem-extraction/api/extract",
460
+ params.file,
461
+ "file",
462
+ additionalFields
463
+ );
464
+ }
465
+ if (params.url) {
466
+ additionalFields.url = params.url;
467
+ return this.client.postForm(
468
+ "/api/v1/stem-extraction/api/extract",
469
+ additionalFields
470
+ );
471
+ }
472
+ throw new Error("Either file or url must be provided");
473
+ }
474
+ /**
475
+ * Get the status of a stem extraction job.
476
+ *
477
+ * @param jobId - The job ID returned from extract()
478
+ * @returns Job status with download_urls when completed
479
+ *
480
+ * @example
481
+ * ```typescript
482
+ * const status = await client.stems.status(5512);
483
+ * if (status.status === 'COMPLETED') {
484
+ * console.log(status.download_urls);
485
+ * }
486
+ * ```
487
+ */
488
+ async status(jobId) {
489
+ return this.client.get(`/api/v1/stem-extraction/status/${jobId}`);
490
+ }
491
+ /**
492
+ * Get a stem extraction job by ID
493
+ */
494
+ async get(jobId) {
495
+ return this.client.get(`/api/v1/stem-extraction/jobs/${jobId}`);
496
+ }
497
+ /**
498
+ * List stem extraction jobs
499
+ *
500
+ * @param params.skip - Number of jobs to skip (pagination)
501
+ * @param params.limit - Maximum jobs to return (default: 50)
502
+ * @param params.status - Filter by status
503
+ */
504
+ async list(params = {}) {
505
+ return this.client.get("/api/v1/stem-extraction/jobs", {
506
+ skip: params.skip ?? 0,
507
+ limit: params.limit ?? 50,
508
+ status: params.status
509
+ });
510
+ }
511
+ /**
512
+ * Delete a stem extraction job
513
+ */
514
+ async delete(jobId) {
515
+ await this.client.delete(`/api/v1/stem-extraction/jobs/${jobId}`);
516
+ }
517
+ /**
518
+ * Wait for stem extraction to complete.
519
+ *
520
+ * @param jobId - The job ID to wait for
521
+ * @param timeout - Maximum wait time in milliseconds (default: 600000)
522
+ * @returns Completed job with download_urls
523
+ *
524
+ * @example
525
+ * ```typescript
526
+ * const job = await client.stems.extract({ url: '...', mode: 'six' });
527
+ * const result = await client.stems.waitForCompletion(job.id);
528
+ *
529
+ * for (const [stem, url] of Object.entries(result.download_urls)) {
530
+ * console.log(`${stem}: ${url}`);
531
+ * }
532
+ * ```
533
+ */
534
+ async waitForCompletion(jobId, timeout = DEFAULT_TIMEOUT4) {
535
+ const startTime = Date.now();
536
+ while (Date.now() - startTime < timeout) {
537
+ const job = await this.status(jobId);
538
+ if (job.status === "COMPLETED") {
539
+ return job;
540
+ }
541
+ if (job.status === "FAILED") {
542
+ throw new Error(
543
+ `Stem extraction failed: ${job.error_message || "Unknown error"}`
544
+ );
545
+ }
546
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL4));
547
+ }
548
+ throw new Error(`Stem extraction timed out after ${timeout / 1e3}s`);
549
+ }
550
+ /**
551
+ * Extract stems and wait for completion (convenience method).
552
+ *
553
+ * @example
554
+ * ```typescript
555
+ * // One-liner: extract and wait
556
+ * const result = await client.stems.separate({
557
+ * url: 'https://youtube.com/watch?v=VIDEO_ID',
558
+ * mode: 'six'
559
+ * });
560
+ * console.log(result.download_urls.vocals);
561
+ * ```
562
+ */
563
+ async separate(params, timeout = DEFAULT_TIMEOUT4) {
564
+ const job = await this.extract(params);
565
+ return this.waitForCompletion(job.id, timeout);
566
+ }
567
+ /**
568
+ * Get available separation modes.
569
+ *
570
+ * @example
571
+ * ```typescript
572
+ * const modes = await client.stems.modes();
573
+ * modes.modes.forEach(m => {
574
+ * console.log(`${m.mode}: ${m.description}`);
575
+ * });
576
+ * ```
577
+ */
578
+ async modes() {
579
+ return this.client.get("/api/v1/stem-extraction/modes");
580
+ }
581
+ };
582
+
583
+ // src/resources/denoiser.ts
584
+ var POLL_INTERVAL5 = 3e3;
585
+ var DEFAULT_TIMEOUT5 = 6e5;
586
+ var Denoiser = class {
587
+ constructor(client) {
588
+ this.client = client;
589
+ }
590
+ /**
591
+ * Denoise audio/video
592
+ *
593
+ * @example
594
+ * ```typescript
595
+ * // Denoise from file
596
+ * const job = await client.denoiser.create({
597
+ * file: './noisy-audio.mp3',
598
+ * });
599
+ *
600
+ * // Denoise from URL
601
+ * const job = await client.denoiser.create({
602
+ * url: 'https://example.com/audio.mp3',
603
+ * });
604
+ * ```
605
+ */
606
+ async create(params) {
607
+ if (params.file) {
608
+ return this.client.upload("/api/v1/denoiser/denoise", params.file, "file");
609
+ }
610
+ if (params.url) {
611
+ return this.client.post("/api/v1/denoiser/denoise", {
612
+ url: params.url
613
+ });
614
+ }
615
+ throw new Error("Either file or url must be provided");
616
+ }
617
+ /**
618
+ * Get a denoise job by ID
619
+ */
620
+ async get(jobId) {
621
+ return this.client.get(`/api/v1/denoiser/jobs/${jobId}`);
622
+ }
623
+ /**
624
+ * List denoise jobs
625
+ */
626
+ async list(params = {}) {
627
+ return this.client.get("/api/v1/denoiser/jobs", {
628
+ skip: params.skip ?? 0,
629
+ limit: params.limit ?? 50,
630
+ status: params.status
631
+ });
632
+ }
633
+ /**
634
+ * Delete a denoise job
635
+ */
636
+ async delete(jobId) {
637
+ await this.client.delete(`/api/v1/denoiser/jobs/${jobId}`);
638
+ }
639
+ /**
640
+ * Wait for denoising to complete
641
+ */
642
+ async waitForCompletion(jobId, timeout = DEFAULT_TIMEOUT5) {
643
+ const startTime = Date.now();
644
+ while (Date.now() - startTime < timeout) {
645
+ const job = await this.get(jobId);
646
+ if (job.status === "COMPLETED") {
647
+ return job;
648
+ }
649
+ if (job.status === "FAILED") {
650
+ throw new Error(`Denoising failed: ${job.error_message || "Unknown error"}`);
651
+ }
652
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL5));
653
+ }
654
+ throw new Error(`Denoising timed out after ${timeout / 1e3}s`);
655
+ }
656
+ /**
657
+ * Denoise and wait for completion
658
+ */
659
+ async denoise(params) {
660
+ const job = await this.create(params);
661
+ return this.waitForCompletion(job.id);
662
+ }
663
+ };
664
+
665
+ // src/resources/speaker.ts
666
+ var POLL_INTERVAL6 = 3e3;
667
+ var DEFAULT_TIMEOUT6 = 6e5;
668
+ var Speaker = class {
669
+ constructor(client) {
670
+ this.client = client;
671
+ }
672
+ /**
673
+ * Create a speaker diarization job
674
+ *
675
+ * @example
676
+ * ```typescript
677
+ * // Diarize from file
678
+ * const job = await client.speaker.diarize({
679
+ * file: './meeting.mp3',
680
+ * numSpeakers: 3,
681
+ * });
682
+ *
683
+ * // Diarize from URL
684
+ * const job = await client.speaker.diarize({
685
+ * url: 'https://youtube.com/watch?v=...',
686
+ * });
687
+ * ```
688
+ */
689
+ async diarize(params) {
690
+ if (params.file) {
691
+ return this.client.upload("/api/v1/speaker/diarize", params.file, "file", {
692
+ num_speakers: params.numSpeakers
693
+ });
694
+ }
695
+ if (params.url) {
696
+ return this.client.post("/api/v1/speaker/diarize", {
697
+ url: params.url,
698
+ num_speakers: params.numSpeakers
699
+ });
700
+ }
701
+ throw new Error("Either file or url must be provided");
702
+ }
703
+ /**
704
+ * Get a speaker job by ID
705
+ */
706
+ async get(jobId) {
707
+ return this.client.get(`/api/v1/speaker/jobs/${jobId}`);
708
+ }
709
+ /**
710
+ * List speaker jobs
711
+ */
712
+ async list(params = {}) {
713
+ return this.client.get("/api/v1/speaker/jobs", {
714
+ skip: params.skip ?? 0,
715
+ limit: params.limit ?? 50,
716
+ status: params.status,
717
+ job_type: params.jobType
718
+ });
719
+ }
720
+ /**
721
+ * Delete a speaker job
722
+ */
723
+ async delete(jobId) {
724
+ await this.client.delete(`/api/v1/speaker/jobs/${jobId}`);
725
+ }
726
+ /**
727
+ * Wait for speaker job to complete
728
+ */
729
+ async waitForCompletion(jobId, timeout = DEFAULT_TIMEOUT6) {
730
+ const startTime = Date.now();
731
+ while (Date.now() - startTime < timeout) {
732
+ const job = await this.get(jobId);
733
+ if (job.status === "COMPLETED") {
734
+ return job;
735
+ }
736
+ if (job.status === "FAILED") {
737
+ throw new Error(`Speaker processing failed: ${job.error_message || "Unknown error"}`);
738
+ }
739
+ await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL6));
740
+ }
741
+ throw new Error(`Speaker processing timed out after ${timeout / 1e3}s`);
742
+ }
743
+ /**
744
+ * Diarize and wait for completion
745
+ */
746
+ async identify(params) {
747
+ const job = await this.diarize(params);
748
+ return this.waitForCompletion(job.id);
749
+ }
750
+ };
751
+
752
+ // src/resources/wallet.ts
753
+ var Wallet = class {
754
+ constructor(client) {
755
+ this.client = client;
756
+ }
757
+ /**
758
+ * Get current wallet balance
759
+ *
760
+ * @example
761
+ * ```typescript
762
+ * const balance = await client.wallet.getBalance();
763
+ * console.log(`Balance: ${balance.balance_usd}`);
764
+ * ```
765
+ */
766
+ async getBalance() {
767
+ return this.client.get("/api/v1/api-wallet/balance");
768
+ }
769
+ /**
770
+ * Get usage history
771
+ */
772
+ async getUsage(params = {}) {
773
+ return this.client.get("/api/v1/api-wallet/usage", {
774
+ page: params.page ?? 1,
775
+ limit: params.limit ?? 50,
776
+ api_key_id: params.apiKeyId
777
+ });
778
+ }
779
+ /**
780
+ * Get pricing information for all services
781
+ */
782
+ async getPricing() {
783
+ return this.client.get("/api/v1/api-wallet/pricing");
784
+ }
785
+ /**
786
+ * Estimate cost for an operation
787
+ *
788
+ * @example
789
+ * ```typescript
790
+ * const estimate = await client.wallet.estimateCost({
791
+ * serviceType: 'stem_extraction',
792
+ * durationSeconds: 300,
793
+ * });
794
+ * console.log(`Estimated cost: ${estimate.cost_usd}`);
795
+ * ```
796
+ */
797
+ async estimateCost(params) {
798
+ return this.client.post("/api/v1/api-wallet/estimate", {
799
+ service_type: params.serviceType,
800
+ duration_seconds: params.durationSeconds
801
+ });
802
+ }
803
+ /**
804
+ * Check if balance is sufficient for an operation
805
+ */
806
+ async checkBalance(params) {
807
+ return this.client.post("/api/v1/api-wallet/check-balance", {
808
+ service_type: params.serviceType,
809
+ duration_seconds: params.durationSeconds
810
+ });
811
+ }
812
+ };
813
+
814
+ // src/client.ts
815
+ var VERSION = "2.1.0";
816
+ var DEFAULT_BASE_URL = "https://api.audiopod.ai";
817
+ var DEFAULT_TIMEOUT7 = 6e4;
818
+ var AudioPod = class {
819
+ constructor(options = {}) {
820
+ this._apiKey = options.apiKey || process.env.AUDIOPOD_API_KEY || "";
821
+ if (!this._apiKey) {
822
+ throw new AuthenticationError(
823
+ "API key is required. Pass apiKey option or set AUDIOPOD_API_KEY environment variable."
824
+ );
825
+ }
826
+ if (!this._apiKey.startsWith("ap_")) {
827
+ throw new AuthenticationError(
828
+ "Invalid API key format. AudioPod API keys start with 'ap_'"
829
+ );
830
+ }
831
+ this._axios = import_axios.default.create({
832
+ baseURL: options.baseUrl || DEFAULT_BASE_URL,
833
+ timeout: options.timeout || DEFAULT_TIMEOUT7,
834
+ headers: {
835
+ Authorization: `Bearer ${this._apiKey}`,
836
+ "X-API-Key": this._apiKey,
837
+ "Content-Type": "application/json",
838
+ "User-Agent": `audiopod-node/${VERSION}`,
839
+ Accept: "application/json"
840
+ }
841
+ });
842
+ this._axios.interceptors.response.use(
843
+ (response) => response,
844
+ (error) => this._handleError(error)
845
+ );
846
+ this.transcription = new Transcription(this);
847
+ this.voice = new Voice(this);
848
+ this.music = new Music(this);
849
+ this.stems = new StemExtraction(this);
850
+ this.denoiser = new Denoiser(this);
851
+ this.speaker = new Speaker(this);
852
+ this.wallet = new Wallet(this);
853
+ }
854
+ _handleError(error) {
855
+ if (error.response) {
856
+ const status = error.response.status;
857
+ const data = error.response.data;
858
+ const message = data?.detail || data?.message || error.message;
859
+ switch (status) {
860
+ case 401:
861
+ throw new AuthenticationError(message || "Invalid API key");
862
+ case 402:
863
+ throw new InsufficientBalanceError(
864
+ message || "Insufficient wallet balance",
865
+ data?.required_cents,
866
+ data?.available_cents
867
+ );
868
+ case 429:
869
+ throw new RateLimitError(message || "Rate limit exceeded");
870
+ default:
871
+ throw new APIError(message, status);
872
+ }
873
+ }
874
+ throw new APIError(error.message);
875
+ }
876
+ /** @internal Make a GET request */
877
+ async get(endpoint, params) {
878
+ const response = await this._axios.get(endpoint, { params });
879
+ return response.data;
880
+ }
881
+ /** @internal Make a POST request */
882
+ async post(endpoint, data) {
883
+ const response = await this._axios.post(endpoint, data);
884
+ return response.data;
885
+ }
886
+ /** @internal Make a POST request with form data (non-file) */
887
+ async postForm(endpoint, data) {
888
+ const formData = new import_form_data.default();
889
+ for (const [key, value] of Object.entries(data)) {
890
+ if (value !== void 0 && value !== null) {
891
+ formData.append(key, String(value));
892
+ }
893
+ }
894
+ const response = await this._axios.post(endpoint, formData, {
895
+ headers: formData.getHeaders()
896
+ });
897
+ return response.data;
898
+ }
899
+ /** @internal Make a DELETE request */
900
+ async delete(endpoint) {
901
+ const response = await this._axios.delete(endpoint);
902
+ return response.data;
903
+ }
904
+ /** @internal Upload a file */
905
+ async upload(endpoint, filePath, fieldName = "file", additionalFields) {
906
+ const formData = new import_form_data.default();
907
+ const filename = path.basename(filePath);
908
+ formData.append(fieldName, fs.createReadStream(filePath), filename);
909
+ if (additionalFields) {
910
+ for (const [key, value] of Object.entries(additionalFields)) {
911
+ if (value !== void 0 && value !== null) {
912
+ formData.append(
913
+ key,
914
+ typeof value === "object" ? JSON.stringify(value) : String(value)
915
+ );
916
+ }
917
+ }
918
+ }
919
+ const response = await this._axios.post(endpoint, formData, {
920
+ headers: formData.getHeaders()
921
+ });
922
+ return response.data;
923
+ }
924
+ /** @internal Upload with buffer */
925
+ async uploadBuffer(endpoint, buffer, filename, fieldName = "file", additionalFields) {
926
+ const formData = new import_form_data.default();
927
+ formData.append(fieldName, buffer, { filename });
928
+ if (additionalFields) {
929
+ for (const [key, value] of Object.entries(additionalFields)) {
930
+ if (value !== void 0 && value !== null) {
931
+ formData.append(
932
+ key,
933
+ typeof value === "object" ? JSON.stringify(value) : String(value)
934
+ );
935
+ }
936
+ }
937
+ }
938
+ const response = await this._axios.post(endpoint, formData, {
939
+ headers: formData.getHeaders()
940
+ });
941
+ return response.data;
942
+ }
943
+ };
944
+
945
+ // src/index.ts
946
+ var VERSION2 = "2.1.0";
947
+ // Annotate the CommonJS export names for ESM import in node:
948
+ 0 && (module.exports = {
949
+ APIError,
950
+ AudioPod,
951
+ AudioPodError,
952
+ AuthenticationError,
953
+ InsufficientBalanceError,
954
+ RateLimitError,
955
+ VERSION
956
+ });