Edge TTS Worker: Implementación de las API de síntesis de voz de Microsoft mediante Cloudflare, formato compatible con OpenAI e interfaz web empaquetada
Últimos recursos sobre IAActualizado hace 7 meses Círculo de intercambio de inteligencia artificial 2.4K 00
Introducción general
Edge TTS Worker (dependencias) borde-tts Edge TTS Worker es un servicio proxy desplegado en Cloudflare Worker, que encapsula el servicio Microsoft Edge TTS en una interfaz API compatible con el formato OpenAI. Con este proyecto, los usuarios pueden utilizar fácilmente el servicio de síntesis de voz de alta calidad de Microsoft sin la certificación de Microsoft.Edge TTS Worker ofrece compatibilidad con varios idiomas, incluidos chino, inglés, japonés, coreano, etc., y es completamente gratuito, basado en el plan gratuito de Cloudflare Worker. El servicio también admite claves API personalizadas para garantizar la seguridad y el control, y se puede implementar rápidamente, en cuestión de minutos.
API de prueba: https://tts.aishare.us.kg/ CLAVE: aisharenet
Un proyecto para empaquetar una interfaz sencilla para la API

Empaquetado de API e interfaces completo con despliegue de Cloudflare:
El código completo es generado por CHATGPT, el código se adjunta al final del artículo, usted puede optar por cambiar todos los Programación de IA Herramientas o uso Trickle (Generación con un solo clic de servicios accesibles en línea de generación de voz):

Experiencia: https://edgetts.aishare.us.kg/
Lista de funciones
- Proporciona un formato de interfaz compatible con OpenAI
- Eludir las restricciones de acceso al continente y eliminar el paso de autenticación de los servicios de Microsoft
- Compatibilidad con varios idiomas: chino, inglés, japonés, coreano, etc.
- Totalmente gratuito, basado en Cloudflare Worker Free Plan
- Compatibilidad con claves API personalizadas para garantizar la seguridad y el control
- Despliegue rápido, listo en minutos
- Proporcionar guiones de prueba para probar diferentes efectos de voz
Utilizar la ayuda
Proceso de instalación
- Creación de un trabajador
- Iniciar sesión en Cloudflare Dashboard
- Vaya a Trabajadores y Páginas y haga clic en Crear Trabajador.
- Dar un nombre al trabajador (por ejemplo, edge-tts)
- Código de despliegue
- Eliminar el código por defecto del editor
- haga una copia de
worker.js
y pegue el código en el campo - Haga clic en Guardar y desplegar
- Configuración de la clave API (opcional)
- Busca Configuración -> Variables en la página de configuración del Trabajador.
- Haga clic en Añadir variable, rellene API_KEY para el nombre y el valor de la clave que desee.
- Haga clic en Guardar y desplegar
- Configurar un nombre de dominio personalizado (opcional)
- Requisitos previos: Su dominio ya está alojado en Cloudflare y los registros DNS del dominio se han proxyado a través de Cloudflare (el estado del proxy es nube naranja).
- Pasos de configuración:
- Haga clic en la pestaña Configuración de la página Detalles del trabajador.
- Localice la sección Dominio y Enrutamiento y pulse el botón Añadir.
- Seleccione Dominio personalizado e introduzca el nombre de dominio que desea utilizar (por ejemplo, tts.example.com).
- Haga clic en Añadir dominio y espere a que se complete la implementación del certificado (normalmente en unos minutos).
Utilización
- TTY (interfaz de texto a voz)
- Ejemplo de discurso chino:
curl -X POST https://你的worker地址/v1/audio/speech \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{ "model": "tts-1", "input": "你好,世界!", "voice": "zh-CN-XiaoxiaoNeural", "response_format": "mp3", "speed": 1.0, "pitch": 1.0, "style":"general" }' --output chinese.mp3
- Ejemplo de discurso en inglés:
curl -X POST https://你的worker地址/v1/audio/speech \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{ "model": "tts-1", "input": "Hello, World!", "voice": "en-US-JennyNeural", "response_format": "mp3", "speed": 1.0, "pitch": 1.0, "style":"general" }' --output english.mp3
- Uso del script de prueba
- Descargar script de prueba
test_voices.sh
- Añade permisos de ejecución al script:
bash
chmod +x test_voices.sh - Ejecuta el script:
bash
./test_voices.sh <Worker地址> [API密钥] - Ejemplo:
bash
# 使用 API 密钥
./test_voices.sh https://your-worker.workers.dev your-api-key
# 不使用 API 密钥
./test_voices.sh https://your-worker.workers.dev - El script genera archivos de audio de prueba para cada voz admitida, que puedes reproducir para seleccionar la voz más adecuada.
- Descargar script de prueba
API Parámetro Descripción
model
(cadena): Nombre del modelo (valor fijo), por ejemplotts-1
input
(cadena): el texto que debe convertirse, por ejemplo"你好,世界!"
voice
(cadena): nombre de la voz, por ejemplozh-CN-XiaoxiaoNeural
response_format
(cadena, opcional): formato de salida, el valor por defecto esmp3
speed
(número, opcional): velocidad de voz (0,5-2,0), por defecto es1.0
pitch
(número, opcional): tono (0,5-2,0), por defecto es1.0
style
(cadena, opcional): emoción, por defectogeneral
Lista de voces admitidas
Asegúrese de utilizar el texto en el idioma correspondiente a la voz; por ejemplo, la voz en chino debe utilizarse con el texto en chino. A continuación se muestran ejemplos de las voces más utilizadas:
zh-CN-XiaoxiaoNeural
: Xiaoxiao - Cálida y animadazh-CN-XiaoyiNeural
: Xiaoyi - Calidez y amabilidadzh-CN-YunxiNeural
:: Yumshi - voz masculina, establezh-CN-YunyangNeural
:: Yun Yang - voz masculina, profesionalzh-CN-XiaohanNeural
: Xiaohan - Flujo naturalzh-CN-XiaomengNeural
: Xiaomeng - Dulce y vibrantezh-CN-XiaochenNeural
Xiaochen - Suave y fácil- Espera...
código workers.js
const API_KEY = 'aisharenet'; // 替换为你的实际 API key const TOKEN_REFRESH_BEFORE_EXPIRY = 3 * 60; // Token 刷新时间(秒) const DEFAULT_VOICE = 'zh-CN-XiaoxiaoNeural'; // 默认语音 const DEFAULT_SPEED = 1.0; // 默认语速 const DEFAULT_PITCH = 1.0; // 默认音调 let tokenInfo = { endpoint: null, token: null, expiredAt: null }; // 处理请求 addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); }); async function handleRequest(request) { const url = new URL(request.url); // 如果请求根路径,返回 Web UI if (url.pathname === '/') { return new Response(getWebUI(), { headers: { 'Content-Type': 'text/html' }, }); } // 处理 TTS 请求 if (url.pathname === '/v1/audio/speech') { return handleTTSRequest(request); } // 默认返回 404 return new Response('Not Found', { status: 404 }); } // 处理 TTS 请求 async function handleTTSRequest(request) { if (request.method === 'OPTIONS') { return handleOptions(request); } // 验证 API Key const authHeader = request.headers.get('authorization') || request.headers.get('x-api-key'); const apiKey = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null; if (API_KEY && apiKey !== API_KEY) { return createErrorResponse('Invalid API key. Use \'Authorization: Bearer your-api-key\' header', 'invalid_api_key', 401); } try { const requestBody = await request.json(); const { model = "tts-1", input, voice = DEFAULT_VOICE, speed = DEFAULT_SPEED, pitch = DEFAULT_PITCH } = requestBody; // 验证参数范围 validateParameterRange('speed', speed, 0.5, 2.0); validateParameterRange('pitch', pitch, 0.5, 2.0); const rate = calculateRate(speed); const numPitch = calculatePitch(pitch); const audioResponse = await getVoice( input, voice, rate >= 0 ? `+${rate}%` : `${rate}%`, numPitch >= 0 ? `+${numPitch}%` : `${numPitch}%`, '+0%', 'general', // 固定风格为通用 'audio-24khz-48kbitrate-mono-mp3' // 固定音频格式为 MP3 ); return audioResponse; } catch (error) { return createErrorResponse(error.message, 'edge_tts_error', 500); } } // 处理 OPTIONS 请求 function handleOptions(request) { return new Response(null, { headers: { ...makeCORSHeaders(), 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', 'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') || 'Authorization', }, }); } // 创建错误响应 function createErrorResponse(message, code, status) { return new Response( JSON.stringify({ error: { message, code } }), { status, headers: { 'Content-Type': 'application/json', ...makeCORSHeaders() }, } ); } // 验证参数范围 function validateParameterRange(name, value, min, max) { if (value < min || value > max) { throw new Error(`${name} must be between ${min} and ${max}`); } } // 计算语速 function calculateRate(speed) { return parseInt(String((parseFloat(speed) - 1.0) * 100)); } // 计算音调 function calculatePitch(pitch) { return parseInt(String((parseFloat(pitch) - 1.0) * 100)); } // 生成 CORS 头 function makeCORSHeaders() { return { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, x-api-key', 'Access-Control-Max-Age': '86400', }; } // 获取语音 async function getVoice(text, voiceName, rate, pitch, volume, style, outputFormat) { try { const chunks = text.trim().split("\n"); const audioChunks = await Promise.all(chunks.map(chunk => getAudioChunk(chunk, voiceName, rate, pitch, volume, style, outputFormat))); // 将音频片段拼接起来 const concatenatedAudio = new Blob(audioChunks, { type: `audio/${outputFormat.split('-').pop()}` }); return new Response(concatenatedAudio, { headers: { 'Content-Type': `audio/${outputFormat.split('-').pop()}`, ...makeCORSHeaders(), }, }); } catch (error) { console.error("语音合成失败:", error); return createErrorResponse(error.message, 'edge_tts_error', 500); } } // 获取单个音频片段 async function getAudioChunk(text, voiceName, rate, pitch, volume, style, outputFormat) { const endpoint = await getEndpoint(); const url = `https://${endpoint.r}.tts.speech.microsoft.com/cognitiveservices/v1`; const slien = extractSilenceDuration(text); const response = await fetch(url, { method: "POST", headers: { "Authorization": endpoint.t, "Content-Type": "application/ssml+xml", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0", "X-Microsoft-OutputFormat": outputFormat, }, body: getSsml(text, voiceName, rate, pitch, volume, style, slien), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Edge TTS API error: ${response.status} ${errorText}`); } return response.blob(); } // 提取静音时长 function extractSilenceDuration(text) { const match = text.match(/\[(\d+)\]\s*?$/); return match && match.length === 2 ? parseInt(match[1]) : 0; } // 生成 SSML function getSsml(text, voiceName, rate, pitch, volume, style, slien) { const slienStr = slien > 0 ? `` : ''; return ` ${text} ${slienStr} `; } // 获取 Endpoint async function getEndpoint() { const now = Date.now() / 1000; if (tokenInfo.token && tokenInfo.expiredAt && now < tokenInfo.expiredAt - TOKEN_REFRESH_BEFORE_EXPIRY) { return tokenInfo.endpoint; } // 获取新 Token const endpointUrl = "https://dev.microsofttranslator.com/apps/endpoint?api-version=1.0"; const clientId = crypto.randomUUID().replace(/-/g, ""); try { const response = await fetch(endpointUrl, { method: "POST", headers: { "Accept-Language": "zh-Hans", "X-ClientVersion": "4.0.530a 5fe1dc6c", "X-UserId": "0f04d16a175c411e", "X-HomeGeographicRegion": "zh-Hans-CN", "X-ClientTraceId": clientId, "X-MT-Signature": await sign(endpointUrl), "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0", "Content-Type": "application/json; charset=utf-8", "Content-Length": "0", "Accept-Encoding": "gzip", }, }); if (!response.ok) { throw new Error(`获取 Endpoint 失败: ${response.status}`); } const data = await response.json(); const jwt = data.t.split(".")[1]; const decodedJwt = JSON.parse(atob(jwt)); tokenInfo = { endpoint: data, token: data.t, expiredAt: decodedJwt.exp, }; return data; } catch (error) { console.error("获取 Endpoint 失败:", error); if (tokenInfo.token) { console.log("使用过期的缓存 Token"); return tokenInfo.endpoint; } throw error; } } // 签名 async function sign(urlStr) { const url = urlStr.split("://")[1]; const encodedUrl = encodeURIComponent(url); const uuidStr = uuid(); const formattedDate = dateFormat(); const bytesToSign = `MSTranslatorAndroidApp${encodedUrl}${formattedDate}${uuidStr}`.toLowerCase(); const decode = await base64ToBytes("oik6PdDdMnOXemTbwvMn9de/h9lFnfBaCWbGMMZqqoSaQaqUOqjVGm5NqsmjcBI1x+sS9ugjB55HEJWRiFXYFw=="); const signData = await hmacSha256(decode, bytesToSign); const signBase64 = await bytesToBase64(signData); return `MSTranslatorAndroidApp::${signBase64}::${formattedDate}::${uuidStr}`; } // 格式化日期 function dateFormat() { return (new Date()).toUTCString().replace(/GMT/, "").trim() + " GMT"; } // HMAC SHA-256 签名 async function hmacSha256(key, data) { const cryptoKey = await crypto.subtle.importKey( "raw", key, { name: "HMAC", hash: { name: "SHA-256" } }, false, ["sign"] ); const signature = await crypto.subtle.sign("HMAC", cryptoKey, new TextEncoder().encode(data)); return new Uint8Array(signature); } // Base64 转字节数组 async function base64ToBytes(base64) { const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } // 字节数组转 Base64 async function bytesToBase64(bytes) { return btoa(String.fromCharCode.apply(null, bytes)); } // 生成 UUID function uuid() { return crypto.randomUUID().replace(/-/g, ""); } // 获取 Web UI function getWebUI() { return `
Herramienta de conversión de texto a voz
Por favor, espere mientras se genera la voz...
`; }
© declaración de copyright
El artículo está protegido por derechos de autor y no debe reproducirse sin autorización.
Puestos relacionados
Sin comentarios...