myvoicemaker 0.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/README.md ADDED
@@ -0,0 +1,579 @@
1
+ # myvoicemaker
2
+
3
+ Official JavaScript / TypeScript SDK for the [VoiceMaker](https://myvoicemaker.ai) API.
4
+
5
+ Generate dialect-accurate speech, transcribe audio in Nigerian languages, create lip-sync animations, and more — all from a single, fully typed client.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/myvoicemaker)](https://www.npmjs.com/package/myvoicemaker)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ - [Installation](#installation)
15
+ - [Quick Start](#quick-start)
16
+ - [Authentication](#authentication)
17
+ - [Supported Languages](#supported-languages)
18
+ - [Modules](#modules)
19
+ - [Text-to-Speech (TTS)](#text-to-speech-tts)
20
+ - [Automatic Speech Recognition (ASR)](#automatic-speech-recognition-asr)
21
+ - [Lip-Sync Animation](#lip-sync-animation)
22
+ - [Explain](#explain)
23
+ - [Usage & Billing](#usage--billing)
24
+ - [Error Handling](#error-handling)
25
+ - [Polling Async Jobs](#polling-async-jobs)
26
+ - [TypeScript](#typescript)
27
+ - [Rate Limits](#rate-limits)
28
+
29
+ ---
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ npm install myvoicemaker
35
+ ```
36
+
37
+ Requires **Node.js 18+** (uses native `fetch`). No other runtime dependencies.
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ```typescript
44
+ import { VoiceMaker } from 'myvoicemaker';
45
+
46
+ const client = new VoiceMaker({ apiKey: 'vmk_live_...' });
47
+
48
+ // Generate speech
49
+ const tts = await client.tts.generate({
50
+ text: 'Bawo ni o se wa?',
51
+ voice_id: 'masoyinbo-male-conversational',
52
+ language: 'yo',
53
+ });
54
+ console.log(tts.audio_url);
55
+
56
+ // Transcribe an audio file
57
+ const job = await client.asr.transcribeFile('./sermon.mp3', { language: 'yo' });
58
+ const result = await client.asr.poll(job.job_id, { timeoutMs: 120_000 });
59
+ console.log(result.text);
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Authentication
65
+
66
+ All requests require a developer API key passed as a Bearer token. Create and manage your keys from the [VoiceMaker Developer Dashboard](https://myvoicemaker.ai).
67
+
68
+ ```typescript
69
+ const client = new VoiceMaker({ apiKey: 'vmk_live_...' });
70
+ ```
71
+
72
+ | Key environment | Prefix | Credits consumed |
73
+ | :--- | :--- | :--- |
74
+ | Production | `vmk_live_` | Yes |
75
+ | Test / Sandbox | `vmk_test_` | No |
76
+
77
+ **Keep your API key secret.** Use environment variables in production:
78
+
79
+ ```typescript
80
+ const client = new VoiceMaker({ apiKey: process.env.VOICEMAKER_API_KEY! });
81
+ ```
82
+
83
+ ### Configuration options
84
+
85
+ ```typescript
86
+ const client = new VoiceMaker({
87
+ apiKey: 'vmk_live_...',
88
+ baseUrl: 'https://api.myvoicemaker.ai', // default
89
+ timeoutMs: 30_000, // default: 30 seconds
90
+ });
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Supported Languages
96
+
97
+ | Language | Code | Auto-detect (ASR) |
98
+ | :--- | :--- | :---: |
99
+ | Yoruba | `yo` | ✓ |
100
+ | Igbo | `ig` | ✓ |
101
+ | Hausa | `ha` | ✓ |
102
+ | Nigerian Pidgin | `pcm` | ✓ |
103
+ | English | `en` | ✓ |
104
+ | Auto-detect | `auto` | — |
105
+
106
+ ---
107
+
108
+ ## Modules
109
+
110
+ ### Text-to-Speech (TTS)
111
+
112
+ Convert text into natural-sounding speech. Requests are **synchronous** — the audio URL is returned immediately.
113
+
114
+ #### `client.tts.listVoices(params?)`
115
+
116
+ Retrieve all available voices, optionally filtered by language.
117
+
118
+ ```typescript
119
+ const { voices } = await client.tts.listVoices({ language: 'yo' });
120
+
121
+ for (const voice of voices) {
122
+ console.log(`${voice.id} — ${voice.name} (${voice.gender}, ${voice.style})`);
123
+ console.log(` Sample: ${voice.sample_url}`);
124
+ }
125
+ ```
126
+
127
+ **Parameters**
128
+
129
+ | Parameter | Type | Description |
130
+ | :--- | :--- | :--- |
131
+ | `language` | `string` (optional) | Filter by language code: `yo`, `ig`, `ha`, `pcm`, `en` |
132
+
133
+ **Returns** `VoiceListResponse`
134
+
135
+ | Field | Type |
136
+ | :--- | :--- |
137
+ | `voices` | `Voice[]` |
138
+
139
+ Each `Voice` has: `id`, `name`, `gender`, `language`, `style`, `sample_url`.
140
+
141
+ ---
142
+
143
+ #### `client.tts.generate(params)`
144
+
145
+ ```typescript
146
+ const result = await client.tts.generate({
147
+ text: 'Nne, ka anyị bido oge a.',
148
+ voice_id: 'sunday-okafor-male-conversational',
149
+ language: 'ig',
150
+ speed: 0.9,
151
+ output_format: 'mp3',
152
+ });
153
+
154
+ console.log(result.audio_url); // https://media.myvoicemaker.ai/...
155
+ console.log(result.duration_seconds); // e.g. 3.2
156
+ console.log(result.credits_used); // e.g. 28
157
+ ```
158
+
159
+ **Parameters**
160
+
161
+ | Parameter | Type | Required | Description |
162
+ | :--- | :--- | :---: | :--- |
163
+ | `text` | `string` | ✓ | Text to synthesise (max 5,000 characters) |
164
+ | `voice_id` | `string` | ✓ | Voice identifier from `listVoices()` |
165
+ | `language` | `string` | ✓ | Language code |
166
+ | `speed` | `number` | | Playback speed: `0.5`–`2.0` (default `1.0`) |
167
+ | `output_format` | `string` | | `mp3` \| `wav` \| `ogg` (default `mp3`) |
168
+
169
+ **Returns** `TtsGenerateResponse`
170
+
171
+ | Field | Type |
172
+ | :--- | :--- |
173
+ | `id` | `string` |
174
+ | `audio_url` | `string` |
175
+ | `duration_seconds` | `number \| null` |
176
+ | `characters` | `number` |
177
+ | `credits_used` | `number` |
178
+ | `language` | `string` |
179
+ | `voice_id` | `string` |
180
+ | `created_at` | `string` (ISO 8601) |
181
+
182
+ ---
183
+
184
+ ### Automatic Speech Recognition (ASR)
185
+
186
+ Transcribe pre-recorded audio files. Jobs are **asynchronous** — submit a job, then poll for the result.
187
+
188
+ #### `client.asr.transcribe(params)` — from URL
189
+
190
+ ```typescript
191
+ const job = await client.asr.transcribe({
192
+ audio: 'https://storage.example.com/interview.wav',
193
+ language: 'ha',
194
+ webhookUrl: 'https://myapp.com/webhooks/voicemaker',
195
+ });
196
+ console.log(job.job_id); // use this to poll
197
+ ```
198
+
199
+ **Parameters**
200
+
201
+ | Parameter | Type | Required | Description |
202
+ | :--- | :--- | :---: | :--- |
203
+ | `audio` | `string` | ✓ | Publicly accessible URL of the audio file |
204
+ | `language` | `string` | | Language code or `auto` (default) |
205
+ | `webhookUrl` | `string` | | Callback URL when the job completes |
206
+
207
+ ---
208
+
209
+ #### `client.asr.transcribeFile(filePath, params?)` — from file
210
+
211
+ ```typescript
212
+ const job = await client.asr.transcribeFile('./hausa-interview.mp3', {
213
+ language: 'ha',
214
+ });
215
+ ```
216
+
217
+ **Parameters**
218
+
219
+ | Parameter | Type | Required | Description |
220
+ | :--- | :--- | :---: | :--- |
221
+ | `filePath` | `string` | ✓ | Absolute or relative path to the audio file |
222
+ | `language` | `string` | | Language code or `auto` (default) |
223
+ | `webhookUrl` | `string` | | Callback URL when the job completes |
224
+
225
+ Supported formats: `.mp3`, `.wav`, `.ogg`, `.m4a`, `.webm` — max 500 MB.
226
+
227
+ ---
228
+
229
+ #### `client.asr.getResult(jobId)`
230
+
231
+ ```typescript
232
+ const result = await client.asr.getResult('trans_1a2b3c...');
233
+ console.log(result.status); // 'queued' | 'processing' | 'completed' | 'failed'
234
+ console.log(result.text);
235
+ ```
236
+
237
+ ---
238
+
239
+ #### `client.asr.list(params?)`
240
+
241
+ ```typescript
242
+ const page = await client.asr.list({ limit: 10, status: 'COMPLETED' });
243
+
244
+ for (const job of page.items) {
245
+ console.log(`${job.job_id}: ${job.text?.slice(0, 60)}`);
246
+ }
247
+
248
+ // Load the next page
249
+ if (page.nextCursor) {
250
+ const next = await client.asr.list({ cursor: page.nextCursor });
251
+ }
252
+ ```
253
+
254
+ **Parameters**
255
+
256
+ | Parameter | Type | Description |
257
+ | :--- | :--- | :--- |
258
+ | `limit` | `number` | Items per page: 1–100 (default `20`) |
259
+ | `cursor` | `string` | Pagination cursor from a previous response |
260
+ | `status` | `string` | Filter: `QUEUED` \| `PROCESSING` \| `COMPLETED` \| `FAILED` |
261
+
262
+ ---
263
+
264
+ #### `client.asr.poll(jobId, options?)` — wait for completion
265
+
266
+ Polls `getResult` at a regular interval until the job reaches a terminal state (`completed` or `failed`).
267
+
268
+ ```typescript
269
+ const result = await client.asr.poll(job.job_id, {
270
+ intervalMs: 3_000, // poll every 3 seconds (default: 2 000)
271
+ timeoutMs: 120_000, // give up after 2 minutes (default: 120 000)
272
+ });
273
+
274
+ if (result.status === 'completed') {
275
+ console.log(`Transcript: ${result.text}`);
276
+ console.log(`Language detected: ${result.detected_language}`);
277
+ console.log(`Duration: ${result.duration_seconds}s`);
278
+ }
279
+ ```
280
+
281
+ Throws `TimeoutError` if `timeoutMs` is exceeded before the job completes.
282
+
283
+ **TranscriptionJob shape**
284
+
285
+ | Field | Type |
286
+ | :--- | :--- |
287
+ | `job_id` | `string` |
288
+ | `status` | `'queued' \| 'processing' \| 'completed' \| 'failed'` |
289
+ | `language` | `string` |
290
+ | `detected_language` | `string \| null` |
291
+ | `text` | `string \| null` |
292
+ | `confidence` | `number \| null` |
293
+ | `duration_seconds` | `number \| null` |
294
+ | `credits_used` | `number \| null` |
295
+ | `created_at` | `string` (ISO 8601) |
296
+ | `completed_at` | `string \| null` (ISO 8601) |
297
+
298
+ ---
299
+
300
+ ### Lip-Sync Animation
301
+
302
+ Generate a lip-sync video by combining a portrait image with an audio file. Jobs are **asynchronous**.
303
+
304
+ #### `client.animate.generate(params)`
305
+
306
+ ```typescript
307
+ const job = await client.animate.generate({
308
+ image_url: 'https://example.com/speaker-portrait.jpg',
309
+ audio_url: tts.audio_url,
310
+ output_format: 'mp4',
311
+ });
312
+ console.log(job.job_id);
313
+ console.log(`Estimated credits: ${job.estimated_credits}`);
314
+ ```
315
+
316
+ **Parameters**
317
+
318
+ | Parameter | Type | Required | Description |
319
+ | :--- | :--- | :---: | :--- |
320
+ | `image_url` | `string` | ✓ | URL of the source portrait image (max 5 MB, up to 4K) |
321
+ | `audio_url` | `string` | ✓ | URL of the audio file (max 30 seconds) |
322
+ | `output_format` | `string` | | `mp4` \| `webm` (default `mp4`) |
323
+
324
+ ---
325
+
326
+ #### `client.animate.getResult(jobId)`
327
+
328
+ ```typescript
329
+ const result = await client.animate.getResult('anim_1a2b3c...');
330
+ console.log(result.status); // 'queued' | 'processing' | 'completed' | 'failed'
331
+ console.log(result.video_url); // null until completed
332
+ ```
333
+
334
+ ---
335
+
336
+ #### `client.animate.poll(jobId, options?)`
337
+
338
+ ```typescript
339
+ const result = await client.animate.poll(job.job_id, {
340
+ intervalMs: 5_000,
341
+ timeoutMs: 300_000,
342
+ });
343
+
344
+ if (result.status === 'completed') {
345
+ console.log(`Video: ${result.video_url}`);
346
+ console.log(`Duration: ${result.duration_seconds}s`);
347
+ }
348
+ ```
349
+
350
+ ---
351
+
352
+ ### Explain
353
+
354
+ Process text in Nigerian languages — explain, summarise, translate, or simplify content using AI.
355
+
356
+ > **Note:** This module targets a planned endpoint. Check the [changelog](https://myvoicemaker.ai/docs/changelog) for availability.
357
+
358
+ #### `client.explain.process(params)`
359
+
360
+ ```typescript
361
+ const result = await client.explain.process({
362
+ text: 'Ìwé Mímọ̀ sọ pé...',
363
+ language: 'yo',
364
+ action: 'translate',
365
+ target_language: 'en',
366
+ max_tokens: 500,
367
+ });
368
+
369
+ console.log(result.result); // translated text
370
+ console.log(result.tokens_used); // e.g. 145
371
+ console.log(result.credits_used); // e.g. 145
372
+ ```
373
+
374
+ **Parameters**
375
+
376
+ | Parameter | Type | Required | Description |
377
+ | :--- | :--- | :---: | :--- |
378
+ | `text` | `string` | ✓ | Input text to process |
379
+ | `language` | `string` | ✓ | Source language code |
380
+ | `action` | `string` | ✓ | `explain` \| `summarize` \| `translate` \| `simplify` |
381
+ | `target_language` | `string` | | Required when `action` is `translate` |
382
+ | `max_tokens` | `number` | | Response length limit: 1–2000 (default `500`) |
383
+
384
+ ---
385
+
386
+ ### Usage & Billing
387
+
388
+ #### `client.usage.getBalance()`
389
+
390
+ ```typescript
391
+ const balance = await client.usage.getBalance();
392
+
393
+ console.log(`Tier: ${balance.tier}`);
394
+ console.log(`Credits remaining: ${balance.credits_remaining.toLocaleString()}`);
395
+ console.log(`Credits used: ${balance.credits_used.toLocaleString()}`);
396
+ ```
397
+
398
+ **Returns** `UsageBalanceResponse`
399
+
400
+ | Field | Type |
401
+ | :--- | :--- |
402
+ | `account_id` | `string` |
403
+ | `tier` | `'free' \| 'starter' \| 'growth' \| 'pro' \| 'enterprise'` |
404
+ | `credits_remaining` | `number` |
405
+ | `credits_used` | `number` |
406
+ | `credits_total` | `number` (present only if credits were ever purchased) |
407
+ | `credits_expire` | `string \| null` |
408
+ | `created_at` | `string` (ISO 8601) |
409
+
410
+ ---
411
+
412
+ #### `client.usage.getBreakdown(params)`
413
+
414
+ ```typescript
415
+ const report = await client.usage.getBreakdown({
416
+ start_date: '2026-05-01T00:00:00Z',
417
+ end_date: '2026-05-31T23:59:59Z',
418
+ module: 'asr', // optional filter
419
+ });
420
+
421
+ for (const entry of report.breakdown) {
422
+ console.log(`${entry.module}: ${entry.requests} requests, ${entry.credits_used} credits`);
423
+ }
424
+ console.log(`Total: ${report.total_credits_used} credits`);
425
+ ```
426
+
427
+ **Parameters**
428
+
429
+ | Parameter | Type | Required | Description |
430
+ | :--- | :--- | :---: | :--- |
431
+ | `start_date` | `string` | ✓ | ISO 8601 datetime (inclusive) |
432
+ | `end_date` | `string` | ✓ | ISO 8601 datetime (inclusive) |
433
+ | `module` | `string` | | Filter: `tts` \| `asr` \| `animate` \| `explain` |
434
+
435
+ ---
436
+
437
+ ## Error Handling
438
+
439
+ Every API error is thrown as a typed exception that extends `VoiceMakerAPIError`.
440
+
441
+ ```typescript
442
+ import {
443
+ VoiceMakerError,
444
+ AuthenticationError,
445
+ InsufficientCreditsError,
446
+ RateLimitError,
447
+ ValidationError,
448
+ NotFoundError,
449
+ TimeoutError,
450
+ } from 'myvoicemaker';
451
+
452
+ try {
453
+ const result = await client.tts.generate({ ... });
454
+ } catch (err) {
455
+ if (err instanceof AuthenticationError) {
456
+ console.error('Invalid or expired API key');
457
+ } else if (err instanceof InsufficientCreditsError) {
458
+ console.error('Not enough credits. Top up at myvoicemaker.ai/billing');
459
+ } else if (err instanceof RateLimitError) {
460
+ const wait = err.retryAfter ?? 60;
461
+ console.error(`Rate limited. Retry in ${wait}s`);
462
+ } else if (err instanceof ValidationError) {
463
+ console.error('Invalid parameters:', err.issues);
464
+ } else if (err instanceof TimeoutError) {
465
+ console.error(`Job ${err.jobId} timed out after ${err.timeoutMs}ms`);
466
+ } else if (err instanceof VoiceMakerError) {
467
+ console.error(`API error ${err.status}: ${err.detail}`);
468
+ }
469
+ }
470
+ ```
471
+
472
+ ### Error classes
473
+
474
+ | Class | HTTP Status | Description |
475
+ | :--- | :--- | :--- |
476
+ | `AuthenticationError` | 401 | Missing or invalid API key |
477
+ | `InsufficientCreditsError` | 402 | Insufficient purchased credits |
478
+ | `PermissionError` | 403 | Key lacks required scope or plan |
479
+ | `NotFoundError` | 404 | Resource not found |
480
+ | `FileSizeLimitError` | 413 | Audio file exceeds plan size limit |
481
+ | `UnsupportedMediaTypeError` | 415 | Unsupported file format |
482
+ | `ValidationError` | 422 | Invalid request parameters (check `.issues`) |
483
+ | `RateLimitError` | 429 | Rate limit or concurrency limit exceeded |
484
+ | `ServerError` | 500/503 | Internal server error |
485
+ | `TimeoutError` | — | `poll()` timed out waiting for job completion |
486
+
487
+ All HTTP error classes expose:
488
+
489
+ ```typescript
490
+ err.status // HTTP status code
491
+ err.error // Short error title from the API
492
+ err.detail // Human-readable explanation
493
+ err.requestId // Request ID for support (from X-Request-Id header)
494
+ err.issues // Validation issues array (ValidationError only)
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Polling Async Jobs
500
+
501
+ ASR and Animation jobs are processed asynchronously. The SDK's `poll()` helper handles the retry loop for you:
502
+
503
+ ```typescript
504
+ // Submit
505
+ const job = await client.asr.transcribeFile('./audio.mp3', { language: 'en' });
506
+
507
+ // Poll until done
508
+ const result = await client.asr.poll(job.job_id, {
509
+ intervalMs: 2_000, // how often to check (default: 2 000 ms)
510
+ timeoutMs: 120_000, // maximum wait time (default: 120 000 ms)
511
+ });
512
+ ```
513
+
514
+ Alternatively, manage polling manually:
515
+
516
+ ```typescript
517
+ let result = await client.asr.getResult(job.job_id);
518
+
519
+ while (result.status === 'queued' || result.status === 'processing') {
520
+ await new Promise(r => setTimeout(r, 3000));
521
+ result = await client.asr.getResult(job.job_id);
522
+ }
523
+ ```
524
+
525
+ ---
526
+
527
+ ## TypeScript
528
+
529
+ The SDK is written in TypeScript and ships with full type declarations. No additional `@types` packages are needed.
530
+
531
+ ```typescript
532
+ import type {
533
+ VoiceMakerConfig,
534
+ TranscriptionJob,
535
+ TtsGenerateResponse,
536
+ AnimateResultResponse,
537
+ VoiceListResponse,
538
+ Voice,
539
+ UsageBalanceResponse,
540
+ SupportedLanguage,
541
+ JobStatus,
542
+ PollOptions,
543
+ } from 'myvoicemaker';
544
+ ```
545
+
546
+ All response objects, request parameters, error classes, and union literals are exported directly from the package root.
547
+
548
+ ---
549
+
550
+ ## Rate Limits
551
+
552
+ Rate limits are enforced per API key and vary by plan:
553
+
554
+ | Plan | Requests / min | Concurrent jobs |
555
+ | :--- | :--- | :--- |
556
+ | Growth | 60 | 5 |
557
+ | Pro | 120 | 10 |
558
+ | Enterprise | Custom | Custom |
559
+
560
+ When a limit is exceeded the SDK throws `RateLimitError`. The `Retry-After` header value (seconds) is available as `err.retryAfter`.
561
+
562
+ Each API response also includes rate limit metadata in the response headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`).
563
+
564
+ ---
565
+
566
+ ## Credit Costs
567
+
568
+ | Module | Billable unit | Cost |
569
+ | :--- | :--- | :--- |
570
+ | TTS | Input characters | 1 credit / character |
571
+ | ASR | Audio duration | 5 credits / second |
572
+ | Animate | Video duration | 50 credits / second |
573
+ | Explain | LLM tokens | 1 credit / token |
574
+
575
+ ---
576
+
577
+ ## License
578
+
579
+ MIT © [Collins Edim](https://github.com/equalyzai)