Files
Hermes-ui/packages/server/src/controllers/hermes/tts.ts
T

71 lines
1.6 KiB
TypeScript
Raw Normal View History

2026-05-08 15:34:11 +08:00
import type { Context } from 'koa'
import { textToSpeech, openaiCompatibleTts, speedToEdgeRate } from '../../services/hermes/tts'
2026-05-08 15:34:11 +08:00
export async function generate(ctx: Context) {
const { text, lang } = ctx.request.body as {
text?: string
lang?: string
}
if (!text || typeof text !== 'string') {
ctx.status = 400
ctx.body = { error: 'text is required' }
return
}
if (text.length > 5000) {
ctx.status = 400
ctx.body = { error: 'text is too long (max 5000 characters)' }
return
}
const { audio, engine } = await textToSpeech({ text, lang })
ctx.set('Content-Type', 'audio/mpeg')
ctx.set('Content-Length', String(audio.length))
ctx.set('X-TTS-Engine', engine)
ctx.body = audio
}
/**
* OpenAI-compatible TTS endpoint.
* Accepts: { model, input, voice, speed }
* Returns audio/mpeg stream.
*/
export async function openaiProxy(ctx: Context) {
const body = ctx.request.body as {
input?: string
voice?: string
speed?: number
model?: string
rate?: string
pitch?: string
}
if (!body.input || typeof body.input !== 'string') {
ctx.status = 400
ctx.body = { error: 'input is required' }
return
}
if (body.input.length > 5000) {
ctx.status = 400
ctx.body = { error: 'input is too long (max 5000 characters)' }
return
}
const { audio, engine } = await openaiCompatibleTts({
input: body.input,
voice: body.voice,
speed: body.speed,
model: body.model,
rate: body.rate,
pitch: body.pitch,
})
ctx.set('Content-Type', 'audio/mpeg')
ctx.set('Content-Length', String(audio.length))
ctx.set('X-TTS-Engine', engine)
ctx.body = audio
}