import { Polly } from '@aws-sdk/client-polly'; import { S3, NoSuchKey } from '@aws-sdk/client-s3'; import type { NodeJsClient } from '@smithy/types'; import { Router } from 'express'; import { Base64 } from 'js-base64'; import sha1 from 'sha1'; import { convertPronunciationStringToSsml, handleErrorAsync } from '../../src/helpers.ts'; import { awsConfig, awsParams } from '../aws.ts'; const router = Router(); router.get('/pronounce/:voice/:pronunciation', handleErrorAsync(async (req, res) => { const text = Base64.decode(req.params.pronunciation); if (!text || text.length > 256) { return res.status(404).json({ error: 'Not found' }); } const voice = global.config.pronunciation?.voices?.[req.params.voice]; if (!voice) { return res.status(404).json({ error: 'Not found' }); } const s3 = new S3(awsConfig) as NodeJsClient; const polly = new Polly(awsConfig) as NodeJsClient; const ssml = convertPronunciationStringToSsml(text); const key = `pronunciation/${global.config.locale}-${req.params.voice}/${sha1(ssml)}.mp3`; try { const s3Response = await s3.getObject({ Key: key, ...awsParams }); res.set('content-type', s3Response.ContentType); return s3Response.Body!.pipe(res); } catch (error) { if (!(error instanceof NoSuchKey)) { throw error; } const pollyResponse = await polly.synthesizeSpeech({ TextType: 'ssml', Text: ssml, OutputFormat: 'mp3', LanguageCode: voice.language, VoiceId: voice.voice, Engine: voice.engine, }); const buffer = await pollyResponse.AudioStream!.transformToByteArray(); await s3.putObject({ Key: key, Body: buffer, ContentType: pollyResponse.ContentType, ...awsParams, }); res.set('content-type', pollyResponse.ContentType); res.write(buffer); return res.end(); } })); export default router;