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