From cf3c719c357f0cdbfce3a5fe7e28bef819317999 Mon Sep 17 00:00:00 2001 From: lanhao Date: Thu, 2 Jul 2026 21:11:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=85=A5=E8=B1=86=E5=8C=85=20?= =?UTF-8?q?Seed-ICL=202.0=20=E8=AF=AD=E9=9F=B3=E5=90=88=E6=88=90=E5=BC=95?= =?UTF-8?q?=E6=93=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 doubao_service.py 调用火山引擎 TTS API - 路由加 doubao 引擎分支 - 前端引擎下拉菜单加豆包选项 Co-Authored-By: Claude Opus 4.7 --- backend/app/routes/tts_synthesize.py | 6 +++ backend/app/services/doubao_service.py | 52 ++++++++++++++++++++++++++ frontend/app.js | 5 +++ frontend/index.html | 1 + 4 files changed, 64 insertions(+) create mode 100644 backend/app/services/doubao_service.py diff --git a/backend/app/routes/tts_synthesize.py b/backend/app/routes/tts_synthesize.py index ffe5da2..e4b2c1b 100644 --- a/backend/app/routes/tts_synthesize.py +++ b/backend/app/routes/tts_synthesize.py @@ -3,6 +3,7 @@ import base64 import os from app.services.minimax_service import synthesize_minimax from app.services.cosyvoice_service import synthesize_cosyvoice +from app.services.doubao_service import synthesize_doubao from app.routes.auth import login_required from app.db import get_db @@ -37,6 +38,11 @@ def synthesize(): text=text, speed=speed, pitch=pitch, volume=volume, instruction=instruction ) + elif engine == 'doubao': + audio_data = synthesize_doubao( + text=text, speed=speed, pitch=pitch, + volume=volume, emotion=emotion + ) else: audio_data = synthesize_minimax( text=text, speed=speed, pitch=pitch, diff --git a/backend/app/services/doubao_service.py b/backend/app/services/doubao_service.py new file mode 100644 index 0000000..7eece74 --- /dev/null +++ b/backend/app/services/doubao_service.py @@ -0,0 +1,52 @@ +import os +import uuid +import requests + +def synthesize_doubao(text, speed=1.0, pitch=0, volume=1.0, emotion='neutral'): + """调用火山引擎豆包 Seed-ICL 2.0 语音合成 API""" + api_key = os.getenv('DOUBAO_API_KEY', '') + if not api_key: + raise ValueError('DOUBAO_API_KEY未设置') + + url = 'https://openspeech.bytedance.com/api/v1/tts' + headers = { + 'x-api-key': api_key, + 'Content-Type': 'application/json' + } + + payload = { + 'app': { + 'cluster': 'volcano_icl' + }, + 'user': { + 'uid': 'military_tech_voice' + }, + 'audio': { + 'voice_type': 'S_MK0j18H72', + 'encoding': 'mp3', + 'speed_ratio': speed, + }, + 'request': { + 'reqid': str(uuid.uuid4()), + 'text': text, + 'operation': 'query' + } + } + + response = requests.post(url, json=payload, headers=headers, timeout=60) + + if response.status_code != 200: + raise Exception(f'豆包API错误: {response.status_code} - {response.text}') + + result = response.json() + + # 豆包返回格式: {"code": 3000, "data": "base64编码的音频"} + if result.get('code') == 3000 and result.get('data'): + import base64 + audio_bytes = base64.b64decode(result['data']) + return audio_bytes + + # 错误处理 + error_msg = result.get('message', '未知错误') + error_code = result.get('code', 'unknown') + raise Exception(f'豆包返回错误 [{error_code}]: {error_msg}') diff --git a/frontend/app.js b/frontend/app.js index 8476c9c..b9e9cca 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -17,6 +17,11 @@ const ENGINE_CONFIG = { name: 'CosyVoice', emotionMode: 'global', color: '#ff6b6b' + }, + doubao: { + name: '豆包', + emotionMode: 'global', + color: '#7c5cfc' } }; diff --git a/frontend/index.html b/frontend/index.html index 3373a3c..38ccadc 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -40,6 +40,7 @@ 情绪: