From 3356e267db3b0edb0ec687232c2734b895fbbdad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=8B=E9=94=A6=E5=BC=BA?= <1061669148@qq.com>
Date: Tue, 11 Mar 2025 19:36:17 +0800
Subject: [PATCH] feat: add OpenAI compatible TTS endpoint and update
documentation
---
workers/src/index.js | 119 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 117 insertions(+), 2 deletions(-)
diff --git a/workers/src/index.js b/workers/src/index.js
index d154af4..a4ce9d7 100644
--- a/workers/src/index.js
+++ b/workers/src/index.js
@@ -86,6 +86,11 @@ async function handleRequest(request) {
return response;
}
+ // 添加 OpenAI 兼容接口路由
+ if (path === '/v1/audio/speech' || path === '/audio/speech') {
+ return await handleOpenAITTS(request);
+ }
+
if(path === '/voices') {
const l = (requestUrl.searchParams.get('l') || '').toLowerCase();
const f = requestUrl.searchParams.get('f');
@@ -260,7 +265,7 @@ async function handleRequest(request) {
@@ -301,7 +306,7 @@ async function handleRequest(request) {
- 支持 SSML 标签,可以精确控制语音合成效果
+ 支持 SSML 标签和 OpenAI 兼容接口
@@ -325,6 +330,31 @@ async function handleRequest(request) {
p: 音调调整 (-100~100) [可选]
+ OpenAI 兼容接口
+ /v1/audio/speech 或 /audio/speech
+ 使用方式与 OpenAI TTS API 相同,支持以下参数:
+
+ - model: 模型名称 [必填]
+ - input: 文本内容 [必填]
+ - voice: 声音类型 (alloy, echo, fable, onyx, nova, shimmer)
+ - speed: 语速 (0.25~4.0)
+ - response_format: 输出格式 (mp3, opus)
+
+
+
+
示例请求:
+
+curl ${baseUrl}/v1/audio/speech \\
+ -H "Authorization: Bearer your-secret-api-key" \\
+ -H "Content-Type: application/json" \\
+ -d '{
+ "model": "tts-1",
+ "input": "这是一个语音合成测试",
+ "voice": "alloy"
+ }'
+
+
+
获取语音列表 API
/voices?l={locale}&f={format}
@@ -720,4 +750,89 @@ async function getVoice(text, voiceName = 'zh-CN-XiaoxiaoMultilingualNeural', ra
} else {
return new Response(response.statusText, { status: response.status });
}
+}
+
+// 处理 OpenAI 格式的文本转语音请求
+async function handleOpenAITTS(request) {
+ // 验证请求方法是否为 POST
+ if (request.method !== 'POST') {
+ return new Response(JSON.stringify({ error: 'Method not allowed' }), {
+ status: 405,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ // 验证 API 密钥
+ const authHeader = request.headers.get('Authorization');
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
+ return new Response(JSON.stringify({ error: 'Unauthorized: Missing or invalid API key' }), {
+ status: 401,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ const apiKey = authHeader.replace('Bearer ', '');
+ if (!validateApiKey(apiKey)) {
+ return new Response(JSON.stringify({ error: 'Unauthorized: Invalid API key' }), {
+ status: 401,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ try {
+ // 解析请求体 JSON
+ const requestData = await request.json();
+
+ // 验证必要参数
+ if (!requestData.model || !requestData.input) {
+ return new Response(JSON.stringify({ error: 'Bad request: Missing required parameters' }), {
+ status: 400,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
+
+ // 提取参数
+ const text = requestData.input;
+ // 映射 voice 参数 (可选择添加 model 到 voice 的映射逻辑)
+ let voiceName = 'zh-CN-XiaoxiaoMultilingualNeural'; // 默认声音
+ if (requestData.voice) {
+ // OpenAI的voice参数有alloy, echo, fable, onyx, nova, shimmer
+ // 可以根据需要进行映射
+ const voiceMap = {
+ 'alloy': 'zh-CN-XiaoxiaoMultilingualNeural',
+ 'echo': 'zh-CN-YunxiNeural',
+ 'fable': 'zh-CN-XiaomoNeural',
+ 'onyx': 'zh-CN-YunjianNeural',
+ 'nova': 'zh-CN-XiaochenNeural',
+ 'shimmer': 'en-US-AriaNeural'
+ };
+ voiceName = voiceMap[requestData.voice] || requestData.voice;
+ }
+
+ // 速度和音调映射 (OpenAI 使用 0.25-4.0,我们使用 -100 到 100)
+ let rate = 0;
+ if (requestData.speed) {
+ // 映射 0.25-4.0 到 -100 到 100 范围
+ // 1.0 是正常速度,对应 rate=0
+ rate = Math.round((requestData.speed - 1.0) * 100);
+ // 限制范围
+ rate = Math.max(-100, Math.min(100, rate));
+ }
+
+ // 设置输出格式
+ const outputFormat = requestData.response_format === 'opus' ?
+ 'audio-48khz-192kbitrate-mono-opus' :
+ 'audio-24khz-48kbitrate-mono-mp3';
+
+ // 调用 TTS API
+ const ttsResponse = await getVoice(text, voiceName, rate, 0, outputFormat, false);
+
+ return ttsResponse;
+ } catch (error) {
+ console.error('OpenAI TTS API error:', error);
+ return new Response(JSON.stringify({ error: 'Internal server error: ' + error.message }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' }
+ });
+ }
}
\ No newline at end of file