UNPKG

js-tts-wrapper

Version:

A JavaScript/TypeScript library that provides a unified API for working with multiple cloud-based Text-to-Speech (TTS) services

176 lines (175 loc) 5.29 kB
import { AbstractTTSClient } from "../core/abstract-tts.js"; import * as SSMLUtils from "../core/ssml-utils.js"; import * as SpeechMarkdown from "../markdown/converter.js"; import { getFetch } from "../utils/fetch-utils.js"; // Static voice list as provider doesn't expose voice query API const UPLIFTAI_VOICES = [ { id: "v_8eelc901", name: "Info/Education", gender: "Unknown", provider: "upliftai", languageCodes: [ { bcp47: "ur-PK", iso639_3: "urd", display: "Urdu (Pakistan)", }, ], }, { id: "v_30s70t3a", name: "Nostalgic News", gender: "Unknown", provider: "upliftai", languageCodes: [ { bcp47: "ur-PK", iso639_3: "urd", display: "Urdu (Pakistan)", }, ], }, { id: "v_yypgzenx", name: "Dada Jee", gender: "Unknown", provider: "upliftai", languageCodes: [ { bcp47: "ur-PK", iso639_3: "urd", display: "Urdu (Pakistan)", }, ], }, { id: "v_kwmp7zxt", name: "Gen Z (beta)", gender: "Unknown", provider: "upliftai", languageCodes: [ { bcp47: "ur-PK", iso639_3: "urd", display: "Urdu (Pakistan)", }, ], }, ]; /** * UpliftAI TTS Client */ export class UpliftAITTSClient extends AbstractTTSClient { /** * Create a new UpliftAI TTS client * @param credentials Credentials including API key */ constructor(credentials = {}) { super(credentials); Object.defineProperty(this, "apiKey", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "baseUrl", { enumerable: true, configurable: true, writable: true, value: "https://api.upliftai.org/v1/synthesis" }); Object.defineProperty(this, "outputFormat", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "sampleRate", { enumerable: true, configurable: true, writable: true, value: 22050 }); this._models = [{ id: "upliftai", features: [] }]; this.apiKey = credentials.apiKey || process.env.UPLIFTAI_API_KEY || ""; this.outputFormat = "MP3_22050_128"; // Default format } /** * Check if credentials are valid */ async checkCredentials() { if (!this.apiKey) { console.error("UpliftAI API key is required"); return false; } return true; } /** * Get required credential field names */ getRequiredCredentials() { return ["apiKey"]; } /** * Get available voices (static list) */ async _getVoices() { return UPLIFTAI_VOICES; } /** * Synthesize text to audio bytes */ async synthToBytes(text, options = {}) { const { audioStream } = await this.synthToBytestream(text, options); const reader = audioStream.getReader(); const chunks = []; while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } const totalLength = chunks.reduce((acc, cur) => acc + cur.length, 0); const result = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { result.set(chunk, offset); offset += chunk.length; } return result; } /** * Synthesize text to a byte stream */ async synthToBytestream(text, options = {}) { let processedText = text; if (options.useSpeechMarkdown && SpeechMarkdown.isSpeechMarkdown(processedText)) { const ssml = await SpeechMarkdown.toSSML(processedText); processedText = SSMLUtils.stripSSML(ssml); } if (SSMLUtils.isSSML(processedText)) { processedText = SSMLUtils.stripSSML(processedText); } const voiceId = options.voice || this.voiceId || UPLIFTAI_VOICES[0].id; this.voiceId = voiceId; const response = await getFetch()(`${this.baseUrl}/text-to-speech/stream`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }, body: JSON.stringify({ voiceId, text: processedText, outputFormat: options.outputFormat || this.outputFormat, }), }); if (!response.ok || !response.body) { throw new Error(`Failed to synthesize speech: ${response.status} ${response.statusText}`); } options.onEnd?.(); return { audioStream: response.body, wordBoundaries: [] }; } } export default UpliftAITTSClient;