voicekit-client 0.1.0 → 0.2.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 CHANGED
@@ -3,7 +3,7 @@
3
3
  Official TypeScript/JavaScript wrapper for [VoiceKit](https://ttsapi.ru):
4
4
  neural speech synthesis, transcription, sentiment analysis, and batch operations.
5
5
 
6
- Zero runtime dependencies. Requires Node.js 18+ (global `fetch`).
6
+ Requires Node.js 18+ (global `fetch`). WebSocket streaming uses the `ws` package.
7
7
 
8
8
  ## Install
9
9
 
@@ -96,6 +96,23 @@ const fxFile = await client.downloadAudioEffects(fxJob.job_id);
96
96
  await writeFile("voice_fx.mp3", fxFile);
97
97
  ```
98
98
 
99
+ ### Audio cleaning (Pro/Business)
100
+
101
+ ```js
102
+ // Denoise + normalize an existing file as a background job
103
+ const cleanJob = await client.cleanAudio("noisy.wav"); // one-click preset
104
+ // or with options:
105
+ // const cleanJob = await client.cleanAudio("noisy.wav", { options: { denoise: { strength: 0.8 } } });
106
+
107
+ let cleanResult = await client.getAudioCleaningJob(cleanJob.job_id);
108
+ while (!["completed", "failed"].includes(cleanResult.status)) {
109
+ await new Promise(r => setTimeout(r, 1000));
110
+ cleanResult = await client.getAudioCleaningJob(cleanJob.job_id);
111
+ }
112
+ const cleanFile = await client.downloadAudioCleaning(cleanJob.job_id);
113
+ await writeFile("voice_clean.wav", cleanFile);
114
+ ```
115
+
99
116
  ### WebSocket streaming (Pro/Business)
100
117
 
101
118
  ```js
@@ -117,6 +134,25 @@ for await (const event of vad.events()) {
117
134
  vad.close();
118
135
  ```
119
136
 
137
+ ### Voice ID (Pro/Business)
138
+
139
+ ```js
140
+ // Voice passport: language, gender, age, emotion, speaker embedding, AI-vs-human
141
+ const passport = await client.voiceId("recording.wav");
142
+
143
+ // Voice biometrics on your own profiles
144
+ const profile = await client.enrollVoice("speaker.wav", "Alice"); // { profile_id: "voice_…" }
145
+
146
+ const check = await client.verifyVoice("check.wav", profile.profile_id);
147
+ // { profile_id: "voice_…", similarity: 0.81, verified: true, threshold: 0.7 }
148
+
149
+ const match = await client.identifyVoice("check.wav"); // 1:N across your profiles
150
+ // { best_match: {…}, matches: […], threshold: 0.7 }
151
+
152
+ const profiles = await client.listVoiceProfiles();
153
+ await client.deleteVoiceProfile(profile.profile_id);
154
+ ```
155
+
120
156
  ## Configuration
121
157
 
122
158
  | Option | Default | Description |
package/index.d.ts CHANGED
@@ -35,6 +35,12 @@ export class VoiceKitClient {
35
35
  getTranscriptionJob(jobId: string): Promise<Record<string, unknown>>;
36
36
  subtitles(jobId: string, format?: "vtt" | "srt"): Promise<string>;
37
37
  vad(audio: AudioSource): Promise<Record<string, unknown>>;
38
+ voiceId(audio: AudioSource): Promise<VoiceIdResult>;
39
+ enrollVoice(audio: AudioSource, name?: string): Promise<Record<string, unknown>>;
40
+ verifyVoice(audio: AudioSource, profileId: string): Promise<VoiceVerificationResult>;
41
+ identifyVoice(audio: AudioSource, profileIds?: string[]): Promise<VoiceIdentificationResult>;
42
+ listVoiceProfiles(): Promise<Array<Record<string, unknown>>>;
43
+ deleteVoiceProfile(profileId: string): Promise<void>;
38
44
 
39
45
  analyze(audio: AudioSource, opts?: AudioOptions): Promise<Record<string, unknown>>;
40
46
  analyzeSync(audio: AudioSource, opts?: AudioOptions): Promise<Record<string, unknown>>;
@@ -67,6 +73,13 @@ export class VoiceKitClient {
67
73
  getVideoEffectsJob(jobId: string): Promise<Record<string, unknown>>;
68
74
  downloadVideoEffects(jobId: string): Promise<Uint8Array>;
69
75
 
76
+ cleanAudio(
77
+ audio: AudioSource,
78
+ opts?: AudioCleaningOptions,
79
+ ): Promise<Record<string, unknown>>;
80
+ getAudioCleaningJob(jobId: string): Promise<Record<string, unknown>>;
81
+ downloadAudioCleaning(jobId: string): Promise<Uint8Array>;
82
+
70
83
  batchSynthesize(items: Array<Record<string, unknown>>): Promise<Record<string, unknown>>;
71
84
  batchAnalyze(items: Array<Record<string, unknown>>): Promise<Record<string, unknown>>;
72
85
  getBatch(batchId: string): Promise<Record<string, unknown>>;
@@ -106,6 +119,56 @@ export interface VideoEffectsOptions {
106
119
  webhookUrl?: string;
107
120
  }
108
121
 
122
+ export interface AudioCleaningOptions {
123
+ options?: Record<string, unknown>;
124
+ outputFormat?: "wav" | "mp3" | "ogg";
125
+ webhookUrl?: string;
126
+ }
127
+
128
+ export interface VoiceIdResult {
129
+ language: string;
130
+ language_confidence: number;
131
+ duration_seconds: number;
132
+ speech_ratio: number;
133
+ gender: string;
134
+ gender_confidence: number;
135
+ age_group: string;
136
+ age_confidence: number;
137
+ emotional_background: string;
138
+ emotions?: Record<string, number>;
139
+ ai_probability?: number;
140
+ fake_detection_available: boolean;
141
+ speaker_embedding?: number[];
142
+ nearest_voices: VoiceIdMatch[];
143
+ processing_time_ms: number;
144
+ }
145
+
146
+ export interface VoiceIdMatch {
147
+ voice_id: string;
148
+ similarity: number;
149
+ }
150
+
151
+ export interface VoiceVerificationResult {
152
+ profile_id: string;
153
+ similarity: number;
154
+ verified: boolean;
155
+ threshold: number;
156
+ }
157
+
158
+ export interface VoiceIdentificationResult {
159
+ best_match?: VoiceIdMatch;
160
+ matches: VoiceIdMatch[];
161
+ threshold: number;
162
+ }
163
+
164
+ export interface VoiceProfile {
165
+ profile_id: string;
166
+ name: string;
167
+ source: string;
168
+ clone_voice_id?: string;
169
+ created_at: string;
170
+ }
171
+
109
172
  export interface SynthesisAsyncOptions {
110
173
  voice?: string;
111
174
  format?: "wav";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voicekit-client",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Official TypeScript/JavaScript SDK for VoiceKit (synthesis, transcription, analysis, moderation, batches).",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Official JavaScript/TypeScript SDK for VoiceKit.
3
3
  *
4
- * Requires Node.js 18+ (global `fetch`, web streams). Zero runtime dependencies.
4
+ * Requires Node.js 18+ (global `fetch`, web streams). WebSocket streaming uses the `ws` package.
5
5
  *
6
6
  * @example
7
7
  * import { VoiceKitClient } from "voicekit-client";
@@ -227,6 +227,67 @@ export class VoiceKitClient {
227
227
  return this.#postForm("/v1/vad", audio, {});
228
228
  }
229
229
 
230
+ /**
231
+ * Analyze a voice recording and return a voice passport (language, gender,
232
+ * age group, emotional background, speaker embedding and an AI-vs-human
233
+ * probability — beta, null when the detector is not configured).
234
+ * @param {AudioSource} audio
235
+ * @returns {Promise<Record<string, unknown>>}
236
+ */
237
+ voiceId(audio) {
238
+ return this.#postForm("/v1/voice-id", audio, {});
239
+ }
240
+
241
+ /**
242
+ * Enroll an audio clip as a reusable voice profile (voiceprint).
243
+ * @param {AudioSource} audio
244
+ * @param {string} [name]
245
+ * @returns {Promise<Record<string, unknown>>}
246
+ */
247
+ enrollVoice(audio, name) {
248
+ return this.#postForm("/v1/voice-id/enroll", audio, { name });
249
+ }
250
+
251
+ /**
252
+ * Verify an audio clip against an enrolled profile (1:1).
253
+ * @param {AudioSource} audio
254
+ * @param {string} profileId
255
+ * @returns {Promise<Record<string, unknown>>}
256
+ */
257
+ verifyVoice(audio, profileId) {
258
+ return this.#postForm("/v1/voice-id/verify", audio, { profile_id: profileId });
259
+ }
260
+
261
+ /**
262
+ * Identify the closest matching profile for an audio clip (1:N).
263
+ * @param {AudioSource} audio
264
+ * @param {string[]} [profileIds]
265
+ * @returns {Promise<Record<string, unknown>>}
266
+ */
267
+ identifyVoice(audio, profileIds) {
268
+ const data = profileIds && profileIds.length
269
+ ? { profile_ids: profileIds.join(",") }
270
+ : {};
271
+ return this.#postForm("/v1/voice-id/identify", audio, data);
272
+ }
273
+
274
+ /**
275
+ * List the caller's enrolled voice profiles.
276
+ * @returns {Promise<Array<Record<string, unknown>>>}
277
+ */
278
+ listVoiceProfiles() {
279
+ return this.#getJson("/v1/voice-id/profiles");
280
+ }
281
+
282
+ /**
283
+ * Delete an enrolled voice profile by id.
284
+ * @param {string} profileId
285
+ * @returns {Promise<void>}
286
+ */
287
+ async deleteVoiceProfile(profileId) {
288
+ await this.#request(`/v1/voice-id/profiles/${profileId}`, { method: "DELETE" });
289
+ }
290
+
230
291
  // ──────────────────────── Analysis ─────────────────────────────────
231
292
 
232
293
  /**
@@ -375,6 +436,46 @@ export class VoiceKitClient {
375
436
  return new Uint8Array(await response.arrayBuffer());
376
437
  }
377
438
 
439
+ // ──────────────────────── Audio cleaning ───────────────────────────
440
+
441
+ /**
442
+ * Start an async audio-cleaning job; poll with `getAudioCleaningJob()`.
443
+ * With no `options` the one-click preset (denoise + normalize) is applied.
444
+ * @param {AudioSource} audio
445
+ * @param {{ options?: Record<string, unknown>, outputFormat?: string, webhookUrl?: string }} [opts]
446
+ * @returns {Promise<Record<string, unknown>>}
447
+ */
448
+ async cleanAudio(audio, opts = {}) {
449
+ const { options, outputFormat = "wav", webhookUrl } = opts;
450
+ const { data, filename } = await audioToParts(audio);
451
+ const form = new FormData();
452
+ form.append("audio", new Blob([data], { type: "audio/wav" }), filename);
453
+ form.append("output_format", outputFormat);
454
+ if (options) form.append("options", JSON.stringify(options));
455
+ const query = webhookUrl ? `?webhookUrl=${encodeURIComponent(webhookUrl)}` : "";
456
+ const response = await this.#request(`/v1/audio/clean${query}`, { method: "POST", form });
457
+ return response.json();
458
+ }
459
+
460
+ /**
461
+ * Poll an audio-cleaning job; returns the manifest when completed.
462
+ * @param {string} jobId
463
+ * @returns {Promise<Record<string, unknown>>}
464
+ */
465
+ getAudioCleaningJob(jobId) {
466
+ return this.#getJson(`/v1/audio/clean/${jobId}`);
467
+ }
468
+
469
+ /**
470
+ * Download the cleaned audio for a completed audio-cleaning job.
471
+ * @param {string} jobId
472
+ * @returns {Promise<Uint8Array>}
473
+ */
474
+ async downloadAudioCleaning(jobId) {
475
+ const response = await this.#request(`/v1/audio/clean/${jobId}/audio`);
476
+ return new Uint8Array(await response.arrayBuffer());
477
+ }
478
+
378
479
  // ──────────────────────── Batch ────────────────────────────────────
379
480
 
380
481
  /**