lyrravoice 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,177 @@
1
+ # lyrravoice
2
+
3
+ Official TypeScript SDK for [LyrraVoice](https://github.com/pc152/LyrraVoice) — AI text-to-speech with voice cloning powered by XTTS v2.
4
+
5
+ - Zero dependencies (uses native `fetch`, Node.js 18+)
6
+ - Full TypeScript support with ESM + CJS dual build
7
+ - Covers all public API endpoints (TTS, voices, jobs, MCP keys)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install lyrravoice
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```ts
18
+ import { LyrraVoice } from "lyrravoice";
19
+
20
+ const client = new LyrraVoice({
21
+ apiKey: "lv_...",
22
+ baseUrl: "https://your-server.com",
23
+ });
24
+
25
+ // Generate speech and save to file
26
+ const audio = await client.generate({
27
+ text: "Bonjour, bienvenue sur LyrraVoice !",
28
+ voice_id: "marc",
29
+ language: "fr",
30
+ });
31
+ await audio.save("output.wav");
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Synchronous TTS
37
+
38
+ ```ts
39
+ const audio = await client.generate({
40
+ text: "Hello world",
41
+ voice_id: "sara",
42
+ language: "en",
43
+ format: "mp3",
44
+ speed: 1.2,
45
+ });
46
+
47
+ // Save to file
48
+ await audio.save("hello.mp3");
49
+
50
+ // Or get raw buffer
51
+ const buffer = await audio.buffer();
52
+ ```
53
+
54
+ ### Async TTS (Celery jobs)
55
+
56
+ ```ts
57
+ // Submit job
58
+ const { job_id } = await client.generateAsync({
59
+ text: "A very long text...",
60
+ voice_id: "marc",
61
+ });
62
+
63
+ // Poll until complete
64
+ const job = await client.jobs.poll(job_id, {
65
+ interval: 2000,
66
+ timeout: 60000,
67
+ });
68
+
69
+ // Download result
70
+ if (job.download_url) {
71
+ const audio = await client.jobs.download(job.download_url.split("/").pop()!);
72
+ await audio.save("result.wav");
73
+ }
74
+ ```
75
+
76
+ ### SSE Streaming
77
+
78
+ ```ts
79
+ for await (const event of client.stream({ text: "Streaming audio..." })) {
80
+ console.log(`Chunk ${event.chunk}/${event.total}`);
81
+ // event.audio contains base64-encoded WAV data
82
+ const chunk = Buffer.from(event.audio, "base64");
83
+ }
84
+ ```
85
+
86
+ ### Voice Management
87
+
88
+ ```ts
89
+ import fs from "node:fs";
90
+
91
+ // List voices
92
+ const voices = await client.voices.list();
93
+
94
+ // Clone a voice from audio
95
+ const result = await client.voices.clone({
96
+ name: "my-voice",
97
+ audio: fs.readFileSync("recording.wav"),
98
+ });
99
+
100
+ // Rename
101
+ await client.voices.rename("my-voice", "custom-narrator");
102
+
103
+ // Delete
104
+ await client.voices.delete("custom-narrator");
105
+
106
+ // Import a built-in preset
107
+ await client.voices.importPreset({ speaker_name: "Claribel Dervla" });
108
+ ```
109
+
110
+ ### Audio Processing
111
+
112
+ ```ts
113
+ import fs from "node:fs";
114
+
115
+ const cleaned = await client.audio.clean({
116
+ file: fs.readFileSync("noisy.wav"),
117
+ });
118
+ await cleaned.save("clean.wav");
119
+ ```
120
+
121
+ ### MCP API Keys
122
+
123
+ ```ts
124
+ // Create a key (full key is returned only once)
125
+ const key = await client.mcp.createKey("ci-pipeline");
126
+ console.log(key.key); // "lv_..."
127
+
128
+ // List keys
129
+ const keys = await client.mcp.listKeys();
130
+
131
+ // Revoke
132
+ await client.mcp.revokeKey(key.id);
133
+
134
+ // Server info
135
+ const info = await client.mcp.info();
136
+ ```
137
+
138
+ ### Health Check
139
+
140
+ ```ts
141
+ const health = await client.health();
142
+ // { status: "ok", model: "xtts_v2", device: "cpu" }
143
+ ```
144
+
145
+ ## Error Handling
146
+
147
+ ```ts
148
+ import { APIError, TimeoutError } from "lyrravoice";
149
+
150
+ try {
151
+ await client.generate({ text: "" });
152
+ } catch (err) {
153
+ if (err instanceof APIError) {
154
+ console.error(err.status); // 422
155
+ console.error(err.detail); // "Text is required"
156
+ }
157
+ if (err instanceof TimeoutError) {
158
+ console.error("Job took too long");
159
+ }
160
+ }
161
+ ```
162
+
163
+ ## Configuration
164
+
165
+ ```ts
166
+ const client = new LyrraVoice({
167
+ apiKey: "lv_...", // MCP API key
168
+ // OR
169
+ token: "eyJ...", // JWT bearer token
170
+ baseUrl: "http://localhost:5050", // Server URL (default)
171
+ timeout: 60000, // Request timeout in ms (default: 30000)
172
+ });
173
+ ```
174
+
175
+ ## License
176
+
177
+ MIT
@@ -0,0 +1,333 @@
1
+ interface LyrraVoiceOptions {
2
+ /** API key (e.g. "lv_...") */
3
+ apiKey?: string;
4
+ /** Bearer token (JWT) — alternative to apiKey */
5
+ token?: string;
6
+ /** Base URL of the LyrraVoice server (default: "http://localhost:5050") */
7
+ baseUrl?: string;
8
+ /** Default timeout in ms (default: 30000) */
9
+ timeout?: number;
10
+ }
11
+ type Language = "fr" | "en" | "es" | "de" | "it" | "pt" | "pl" | "tr" | "ru" | "nl" | "cs" | "ar" | "zh-cn" | "ja" | "ko" | "hu";
12
+ type AudioFormat = "wav" | "mp3";
13
+ interface GenerateParams {
14
+ /** Text to synthesize (1-1000 chars) */
15
+ text: string;
16
+ /** Voice identifier */
17
+ voice_id?: string;
18
+ /** Language code (default: "fr") */
19
+ language?: Language;
20
+ /** Output format (default: "wav") */
21
+ format?: AudioFormat;
22
+ /** Speed multiplier 0.5-2.0 (default: 1.0) */
23
+ speed?: number;
24
+ /** Sampling temperature 0.1-1.0 (default: 0.65) */
25
+ temperature?: number;
26
+ /** Repetition penalty 1.0-10.0 (default: 2.0) */
27
+ repetition_penalty?: number;
28
+ /** Top-K sampling 1-100 (default: 50) */
29
+ top_k?: number;
30
+ /** Top-P sampling 0.1-1.0 (default: 0.8) */
31
+ top_p?: number;
32
+ }
33
+ interface AsyncJobResponse {
34
+ job_id: string;
35
+ status: "pending";
36
+ message: string;
37
+ }
38
+ interface StreamEvent {
39
+ chunk: number;
40
+ total: number;
41
+ audio: string;
42
+ }
43
+ interface StreamDoneEvent {
44
+ done: true;
45
+ }
46
+ interface StreamErrorEvent {
47
+ error: string;
48
+ }
49
+ interface TTSJob {
50
+ id: string;
51
+ text: string;
52
+ voice_id: string;
53
+ language: string;
54
+ format: string;
55
+ speed: number;
56
+ status: string;
57
+ file_size: number | null;
58
+ duration_seconds: number | null;
59
+ error: string | null;
60
+ created_at: string | null;
61
+ completed_at: string | null;
62
+ download_url?: string;
63
+ }
64
+ interface JobListResponse {
65
+ jobs: TTSJob[];
66
+ total: number;
67
+ limit: number;
68
+ offset: number;
69
+ }
70
+ interface JobListParams {
71
+ status?: string;
72
+ limit?: number;
73
+ offset?: number;
74
+ }
75
+ interface PollOptions {
76
+ /** Polling interval in ms (default: 1000) */
77
+ interval?: number;
78
+ /** Maximum wait time in ms (default: 300000 = 5min) */
79
+ timeout?: number;
80
+ }
81
+ interface Voice {
82
+ id: string;
83
+ name: string;
84
+ type: "embedding" | "reference" | "empty";
85
+ gender: "male" | "female" | null;
86
+ samples_count: number;
87
+ samples: string[];
88
+ is_official: boolean;
89
+ quality_rating: number | null;
90
+ preview_url?: string | null;
91
+ }
92
+ interface VoiceCloneParams {
93
+ /** Display name for the cloned voice */
94
+ name: string;
95
+ /** Audio file (WAV recommended, 6-30s) */
96
+ audio: Blob | Buffer | ReadableStream;
97
+ /** Filename hint (default: "audio.wav") */
98
+ filename?: string;
99
+ }
100
+ interface VoiceCloneResponse {
101
+ success: boolean;
102
+ voice_id: string;
103
+ message: string;
104
+ }
105
+ interface VoiceUploadResponse {
106
+ success: boolean;
107
+ voice_id: string;
108
+ filename: string;
109
+ size: number;
110
+ embedding_extracted?: boolean;
111
+ voice_type?: string;
112
+ }
113
+ interface VoiceRenameResponse {
114
+ success: boolean;
115
+ old_id: string;
116
+ new_id: string;
117
+ }
118
+ interface VoiceDeleteResponse {
119
+ success: boolean;
120
+ voice_id: string;
121
+ }
122
+ interface VoiceExtractEmbeddingResponse {
123
+ success: boolean;
124
+ message: string;
125
+ voice_type: string;
126
+ }
127
+ interface VoicePresetsResponse {
128
+ speakers: string[];
129
+ count: number;
130
+ }
131
+ interface VoicePresetImportParams {
132
+ speaker_name: string;
133
+ voice_id?: string;
134
+ }
135
+ interface VoicePresetImportResponse {
136
+ success: boolean;
137
+ message: string;
138
+ voice_id: string;
139
+ display_name: string;
140
+ gender: string | null;
141
+ }
142
+ interface AudioCleanParams {
143
+ /** Audio file to clean */
144
+ file: Blob | Buffer | ReadableStream;
145
+ /** Filename hint (default: "audio.wav") */
146
+ filename?: string;
147
+ }
148
+ interface McpKey {
149
+ id: string;
150
+ name: string;
151
+ key_prefix: string;
152
+ is_active: boolean;
153
+ last_used_at: string | null;
154
+ created_at: string;
155
+ }
156
+ interface McpKeyCreateResponse {
157
+ id: string;
158
+ name: string;
159
+ /** Full API key — returned only once */
160
+ key: string;
161
+ key_prefix: string;
162
+ }
163
+ interface McpInfo {
164
+ endpoint: string;
165
+ auth_enabled: boolean;
166
+ transport: string;
167
+ version: string;
168
+ }
169
+ interface HealthResponse {
170
+ status: "ok" | "loading";
171
+ model: string;
172
+ device: string;
173
+ }
174
+
175
+ /**
176
+ * Wraps a binary audio response with convenience methods.
177
+ */
178
+ declare class AudioResponse {
179
+ private readonly _response;
180
+ private _buffer;
181
+ constructor(response: Response);
182
+ /** The raw Response object. */
183
+ get response(): Response;
184
+ /** Content-Type header value. */
185
+ get contentType(): string;
186
+ /** Read the full response as an ArrayBuffer (cached). */
187
+ arrayBuffer(): Promise<ArrayBuffer>;
188
+ /** Read the full response as a Node.js Buffer. */
189
+ buffer(): Promise<Buffer>;
190
+ /** Get a ReadableStream of the response body. Only works if buffer() hasn't been called yet. */
191
+ stream(): ReadableStream<Uint8Array> | null;
192
+ /** Save audio to a file. */
193
+ save(path: string): Promise<void>;
194
+ /** Return as a Blob (useful in non-Node environments). */
195
+ blob(): Promise<Blob>;
196
+ }
197
+
198
+ declare class Voices {
199
+ private readonly _client;
200
+ constructor(_client: LyrraVoiceClient);
201
+ /** List all voices accessible to the current user. */
202
+ list(): Promise<Voice[]>;
203
+ /** List voices with preview URLs. */
204
+ listWithPreviews(language?: string): Promise<Voice[]>;
205
+ /** Get a test/preview audio for a voice. */
206
+ test(voiceId: string, language?: string): Promise<AudioResponse>;
207
+ /** Clone a voice from an audio sample. */
208
+ clone(params: VoiceCloneParams): Promise<VoiceCloneResponse>;
209
+ /** Upload a file (.wav, .mp3, .pth) to an existing voice. */
210
+ upload(voiceId: string, file: Blob | Buffer, filename?: string): Promise<VoiceUploadResponse>;
211
+ /** Rename a voice. */
212
+ rename(voiceId: string, newName: string): Promise<VoiceRenameResponse>;
213
+ /** Delete a voice. */
214
+ delete(voiceId: string): Promise<VoiceDeleteResponse>;
215
+ /** Extract .pth embedding from voice audio references. */
216
+ extractEmbedding(voiceId: string, force?: boolean): Promise<VoiceExtractEmbeddingResponse>;
217
+ /** List built-in XTTS v2 speaker presets. */
218
+ presets(): Promise<VoicePresetsResponse>;
219
+ /** Import a built-in preset as a user voice. */
220
+ importPreset(params: VoicePresetImportParams): Promise<VoicePresetImportResponse>;
221
+ }
222
+
223
+ declare class Jobs {
224
+ private readonly _client;
225
+ constructor(_client: LyrraVoiceClient);
226
+ /** List TTS jobs. */
227
+ list(params?: JobListParams): Promise<JobListResponse>;
228
+ /** Get a single job by ID. */
229
+ get(jobId: string): Promise<TTSJob>;
230
+ /**
231
+ * Poll a job until it reaches a terminal state (completed / failed).
232
+ * Returns the final job object.
233
+ */
234
+ poll(jobId: string, options?: PollOptions): Promise<TTSJob>;
235
+ /** Download the audio output of a completed job. */
236
+ download(fileId: string): Promise<AudioResponse>;
237
+ }
238
+
239
+ declare class AudioProcessing {
240
+ private readonly _client;
241
+ constructor(_client: LyrraVoiceClient);
242
+ /**
243
+ * Clean an audio file (noise reduction + normalization).
244
+ * Returns the cleaned audio as an AudioResponse.
245
+ */
246
+ clean(params: AudioCleanParams): Promise<AudioResponse>;
247
+ }
248
+
249
+ declare class Mcp {
250
+ private readonly _client;
251
+ constructor(_client: LyrraVoiceClient);
252
+ /** List all MCP API keys for the current user. */
253
+ listKeys(): Promise<McpKey[]>;
254
+ /**
255
+ * Create a new MCP API key.
256
+ * The full key is returned only once — store it securely.
257
+ */
258
+ createKey(name: string): Promise<McpKeyCreateResponse>;
259
+ /** Revoke an MCP API key. */
260
+ revokeKey(keyId: string): Promise<{
261
+ success: boolean;
262
+ }>;
263
+ /** Get MCP server info (endpoint, version, transport). */
264
+ info(): Promise<McpInfo>;
265
+ }
266
+
267
+ /** Internal interface — exposed for resource classes via `LyrraVoiceClient`. */
268
+ interface LyrraVoiceClient {
269
+ _get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
270
+ _getRaw(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<AudioResponse>;
271
+ _post<T>(path: string, body?: unknown, query?: Record<string, string | undefined>): Promise<T>;
272
+ _postRaw(path: string, body?: unknown): Promise<AudioResponse>;
273
+ _postForm<T>(path: string, form: FormData): Promise<T>;
274
+ _postFormRaw(path: string, form: FormData): Promise<AudioResponse>;
275
+ _putForm<T>(path: string, form: FormData): Promise<T>;
276
+ _delete<T>(path: string): Promise<T>;
277
+ }
278
+ declare class LyrraVoice implements LyrraVoiceClient {
279
+ private readonly _baseUrl;
280
+ private readonly _apiKey;
281
+ private readonly _token;
282
+ private readonly _timeout;
283
+ /** Voice management (list, clone, rename, delete, presets...) */
284
+ readonly voices: Voices;
285
+ /** Async TTS job management (list, get, poll, download) */
286
+ readonly jobs: Jobs;
287
+ /** Audio processing (clean/denoise) */
288
+ readonly audio: AudioProcessing;
289
+ /** MCP API key management */
290
+ readonly mcp: Mcp;
291
+ constructor(options?: LyrraVoiceOptions);
292
+ /** Synchronous TTS — returns audio. */
293
+ generate(params: GenerateParams): Promise<AudioResponse>;
294
+ /** Async TTS — submits a Celery job, returns job_id for polling. */
295
+ generateAsync(params: GenerateParams): Promise<AsyncJobResponse>;
296
+ /**
297
+ * SSE streaming TTS — yields audio chunks as they are generated.
298
+ *
299
+ * ```ts
300
+ * for await (const event of client.stream({ text: "Hello" })) {
301
+ * // event.audio is base64-encoded WAV chunk
302
+ * }
303
+ * ```
304
+ */
305
+ stream(params: GenerateParams): AsyncGenerator<StreamEvent, void, undefined>;
306
+ /** Health check. */
307
+ health(): Promise<HealthResponse>;
308
+ _get<T>(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<T>;
309
+ _getRaw(path: string, query?: Record<string, string | number | boolean | undefined>): Promise<AudioResponse>;
310
+ _post<T>(path: string, body?: unknown, query?: Record<string, string | undefined>): Promise<T>;
311
+ _postRaw(path: string, body?: unknown): Promise<AudioResponse>;
312
+ _postForm<T>(path: string, form: FormData): Promise<T>;
313
+ _postFormRaw(path: string, form: FormData): Promise<AudioResponse>;
314
+ _putForm<T>(path: string, form: FormData): Promise<T>;
315
+ _delete<T>(path: string): Promise<T>;
316
+ private _url;
317
+ private _fetch;
318
+ }
319
+
320
+ declare class LyrraVoiceError extends Error {
321
+ constructor(message: string);
322
+ }
323
+ declare class APIError extends LyrraVoiceError {
324
+ readonly status: number;
325
+ readonly detail: string | undefined;
326
+ readonly body: unknown;
327
+ constructor(status: number, body: unknown);
328
+ }
329
+ declare class TimeoutError extends LyrraVoiceError {
330
+ constructor(message?: string);
331
+ }
332
+
333
+ export { APIError, type AsyncJobResponse, type AudioCleanParams, type AudioFormat, AudioProcessing, AudioResponse, type GenerateParams, type HealthResponse, type JobListParams, type JobListResponse, Jobs, type Language, LyrraVoice, type LyrraVoiceClient, LyrraVoiceError, type LyrraVoiceOptions, Mcp, type McpInfo, type McpKey, type McpKeyCreateResponse, type PollOptions, type StreamDoneEvent, type StreamErrorEvent, type StreamEvent, type TTSJob, TimeoutError, type Voice, type VoiceCloneParams, type VoiceCloneResponse, type VoiceDeleteResponse, type VoiceExtractEmbeddingResponse, type VoicePresetImportParams, type VoicePresetImportResponse, type VoicePresetsResponse, type VoiceRenameResponse, type VoiceUploadResponse, Voices };