refactor: migrate /api/pronounce/[voice]/[pronunciation] to h3

This commit is contained in:
Valentyne Stigloher 2025-09-14 19:54:28 +02:00
parent 40d7f6a7d8
commit 12c5124ec7

View File

@ -2,24 +2,18 @@ import { Polly } from '@aws-sdk/client-polly';
import { NoSuchKey } from '@aws-sdk/client-s3'; import { NoSuchKey } from '@aws-sdk/client-s3';
import type { S3 } from '@aws-sdk/client-s3'; import type { S3 } from '@aws-sdk/client-s3';
import type { NodeJsClient } from '@smithy/types'; import type { NodeJsClient } from '@smithy/types';
import { Router } from 'express';
import { getH3Event } from 'h3-express';
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
import sha1 from 'sha1'; import sha1 from 'sha1';
import { s3, awsConfig, s3BucketParams } from '../cloudServices.ts';
import { import {
convertPronunciationStringToSsml, convertPronunciationStringToSsml,
convertPronunciationStringToNarakeetFormat, convertPronunciationStringToNarakeetFormat,
handleErrorAsync,
} from '#shared/helpers.ts'; } from '#shared/helpers.ts';
import { voices } from '#shared/pronunciation/voices.ts'; import { voices } from '#shared/pronunciation/voices.ts';
import type { VoiceKey, AwsPollyVoice, NarakeetVoice, Voice } from '#shared/pronunciation/voices.ts'; import type { VoiceKey, AwsPollyVoice, NarakeetVoice, Voice } from '#shared/pronunciation/voices.ts';
import { s3, awsConfig, s3BucketParams } from '~~/server/cloudServices.ts';
import { getLocale, loadConfig } from '~~/server/data.ts'; import { getLocale, loadConfig } from '~~/server/data.ts';
const router = Router();
type ProviderKey = 'aws_polly' | 'narakeet'; type ProviderKey = 'aws_polly' | 'narakeet';
interface Provider { interface Provider {
tokenised(text: string): string; tokenised(text: string): string;
@ -78,29 +72,36 @@ const providers: Record<ProviderKey, Provider> = {
}, },
}; };
router.get('/pronounce/:voice/:pronunciation', handleErrorAsync(async (req, res) => { export default defineEventHandler(async (event) => {
const text = Base64.decode(req.params.pronunciation); const text = Base64.decode(getRouterParam(event, 'pronunciation')!);
if (!text || text.length > 256) { if (!text || text.length > 256) {
return res.status(404).json({ error: 'Not found' }); throw createError({
status: 404,
statusMessage: 'Not Found',
});
} }
const locale = getLocale(getH3Event(req)); const locale = getLocale(event);
const config = await loadConfig(locale); const config = await loadConfig(locale);
if (!(req.params.voice in voices)) { const voiceKey = getRouterParam(event, 'voice');
return res.status(404).json({ error: 'Not found' }); if (voiceKey === undefined || !(voiceKey in voices)) {
throw createError({
status: 404,
statusMessage: 'Not Found',
});
} }
const voice = voices[req.params.voice as VoiceKey]; const voice = voices[voiceKey as VoiceKey];
const provider = providers[(voice.provider || 'aws_polly') as ProviderKey]; const provider = providers[(voice.provider || 'aws_polly') as ProviderKey];
const tokenised = provider.tokenised(text); const tokenised = provider.tokenised(text);
const key = `pronunciation/${config.locale}-${req.params.voice}/${sha1(tokenised)}.mp3`; const key = `pronunciation/${config.locale}-${voiceKey}/${sha1(tokenised)}.mp3`;
try { try {
const s3Response = await (s3 as NodeJsClient<S3>).getObject({ Key: key, ...s3BucketParams }); const s3Response = await (s3 as NodeJsClient<S3>).getObject({ Key: key, ...s3BucketParams });
res.set('content-type', s3Response.ContentType); setResponseHeader(event, 'content-type', s3Response.ContentType);
return s3Response.Body!.pipe(res); return s3Response.Body;
} catch (error) { } catch (error) {
if (!(error instanceof NoSuchKey)) { if (!(error instanceof NoSuchKey)) {
throw error; throw error;
@ -115,10 +116,7 @@ router.get('/pronounce/:voice/:pronunciation', handleErrorAsync(async (req, res)
...s3BucketParams, ...s3BucketParams,
}); });
res.set('content-type', contentType); setResponseHeader(event, 'content-type', contentType);
res.write(buffer); return buffer;
return res.end();
} }
})); });
export default router;