feat: add (untested) polly module
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
PollyClient,
|
||||
DescribeVoicesCommand,
|
||||
Voice,
|
||||
SynthesizeSpeechCommand,
|
||||
Engine
|
||||
} from '@aws-sdk/client-polly';
|
||||
import { TTSModule, TTSResponse } from '../tts';
|
||||
import { config } from '../../utils/config';
|
||||
|
||||
const ENGINE_PRIORITY: Engine[] = [
|
||||
'generative',
|
||||
'neural',
|
||||
'standard',
|
||||
'long-form'
|
||||
];
|
||||
|
||||
class PollyTTS implements TTSModule {
|
||||
private client: PollyClient | undefined = undefined;
|
||||
private voices: Array<Voice> | undefined = undefined;
|
||||
|
||||
public name: string = 'AWS Polly';
|
||||
|
||||
constructor() {
|
||||
if (!config.aws_access_id || !config.aws_access_key) return;
|
||||
|
||||
this.client = new PollyClient({
|
||||
credentials: {
|
||||
accessKeyId: config.aws_access_id,
|
||||
secretAccessKey: config.aws_access_key
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getVoices(): Promise<Array<string> | undefined> {
|
||||
if (!this.client) return [];
|
||||
|
||||
if (!this.voices) {
|
||||
const cmd = new DescribeVoicesCommand({});
|
||||
|
||||
try {
|
||||
const res = await this.client.send(cmd);
|
||||
if (res.Voices) this.voices = res.Voices;
|
||||
} catch (err) {
|
||||
console.error('AWS Polly getVoices error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.voices)
|
||||
return this.voices.map((voice) => `${voice.LanguageCode} ${voice.Id}`);
|
||||
}
|
||||
|
||||
async generate(voice: string, text: string): Promise<TTSResponse> {
|
||||
if (!this.client || !this.voices) return { data: Buffer.from([]) };
|
||||
|
||||
voice = voice.split(' ').slice(1).join(' ');
|
||||
const voiceData = this.voices.find((voiceDesc) => voiceDesc.Name == voice);
|
||||
if (!voiceData) return {};
|
||||
|
||||
const bestEngine = this.getBestEngine(voiceData);
|
||||
if (!bestEngine) return {};
|
||||
|
||||
const cmd = new SynthesizeSpeechCommand({
|
||||
Engine: bestEngine,
|
||||
LanguageCode: voiceData.LanguageCode,
|
||||
OutputFormat: 'mp3',
|
||||
Text: text,
|
||||
VoiceId: voiceData.Id
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await this.client.send(cmd);
|
||||
if (!res.AudioStream) return {};
|
||||
|
||||
const buffer = Buffer.from(await res.AudioStream.transformToByteArray());
|
||||
|
||||
return { data: buffer };
|
||||
} catch (err) {
|
||||
console.error('AWS Polly gen error:', err);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
async canBeUsed(): Promise<boolean> {
|
||||
if (!config.aws_access_id || !config.aws_access_key)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private getBestEngine(voice: Voice): Engine | null {
|
||||
if (!voice.SupportedEngines || voice.SupportedEngines.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const supportedSet = new Set(voice.SupportedEngines);
|
||||
return ENGINE_PRIORITY.find((engine) => supportedSet.has(engine)) || null;
|
||||
}
|
||||
}
|
||||
|
||||
export default new PollyTTS();
|
||||
Reference in New Issue
Block a user