Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1d355993e | |||
| a401fdab15 | |||
| c5e6395b89 | |||
| 0c394bdcbe |
Generated
+1270
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@
|
|||||||
"typescript-eslint": "^8.52.0"
|
"typescript-eslint": "^8.52.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-polly": "^3.968.0",
|
||||||
"@discordjs/opus": "^0.10.0",
|
"@discordjs/opus": "^0.10.0",
|
||||||
"@discordjs/voice": "^0.19.0",
|
"@discordjs/voice": "^0.19.0",
|
||||||
"@snazzah/davey": "^0.1.9",
|
"@snazzah/davey": "^0.1.9",
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class AzureTTS implements TTSModule {
|
|||||||
|
|
||||||
return { data: Buffer.concat(buffers) };
|
return { data: Buffer.concat(buffers) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async canBeUsed(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new AzureTTS();
|
export default new AzureTTS();
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ const USER_AGENT =
|
|||||||
|
|
||||||
const ttsGoogle: TTSModule = {
|
const ttsGoogle: TTSModule = {
|
||||||
name: 'Google',
|
name: 'Google',
|
||||||
getVoices: async (): Promise<string[]> => GOOGLE_TTS_VOICES.voices,
|
async getVoices(): Promise<string[]> { return GOOGLE_TTS_VOICES.voices },
|
||||||
|
|
||||||
generate: async (voice: string, text: string): Promise<TTSResponse> => {
|
async generate(voice: string, text: string): Promise<TTSResponse> {
|
||||||
const query = new URLSearchParams({
|
const query = new URLSearchParams({
|
||||||
ie: 'UTF-8',
|
ie: 'UTF-8',
|
||||||
q: text,
|
q: text,
|
||||||
@@ -41,6 +41,10 @@ const ttsGoogle: TTSModule = {
|
|||||||
resolve({ error: 'timed out' });
|
resolve({ error: 'timed out' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async canBeUsed(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -13,6 +13,7 @@ export interface TTSModule {
|
|||||||
name: string;
|
name: string;
|
||||||
getVoices: () => Promise<Array<string> | undefined>;
|
getVoices: () => Promise<Array<string> | undefined>;
|
||||||
generate: (voice: string, text: string) => Promise<TTSResponse>;
|
generate: (voice: string, text: string) => Promise<TTSResponse>;
|
||||||
|
canBeUsed: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TTSManager {
|
export class TTSManager {
|
||||||
|
|||||||
+6
-1
@@ -10,6 +10,9 @@ export interface Config {
|
|||||||
tts_elevenlabs_key: string | undefined;
|
tts_elevenlabs_key: string | undefined;
|
||||||
|
|
||||||
steam_webapi_key: string | undefined;
|
steam_webapi_key: string | undefined;
|
||||||
|
|
||||||
|
aws_access_id: string | undefined;
|
||||||
|
aws_access_key: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadConfig(): Config {
|
function loadConfig(): Config {
|
||||||
@@ -28,7 +31,9 @@ function loadConfig(): Config {
|
|||||||
tts_default_voice: process.env.DEFAULT_TTS_VOICE,
|
tts_default_voice: process.env.DEFAULT_TTS_VOICE,
|
||||||
tts_azure_key: process.env.TTS_AZURE_KEY,
|
tts_azure_key: process.env.TTS_AZURE_KEY,
|
||||||
tts_elevenlabs_key: process.env.TTS_ELEVENLABS_KEY,
|
tts_elevenlabs_key: process.env.TTS_ELEVENLABS_KEY,
|
||||||
steam_webapi_key: process.env.STEAM_WEBAPI_KEY
|
steam_webapi_key: process.env.STEAM_WEBAPI_KEY,
|
||||||
|
aws_access_id: process.env.AWS_ACCESS_ID,
|
||||||
|
aws_access_key: process.env.AWS_ACCESS_KEY
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user