add banbanmini backend
This commit is contained in:
200
talkingq-url/test/volcano_tts_more.py
Normal file
200
talkingq-url/test/volcano_tts_more.py
Normal file
@@ -0,0 +1,200 @@
|
||||
import os
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import base64
|
||||
from pathlib import Path
|
||||
from pydub import AudioSegment
|
||||
import io
|
||||
import logging
|
||||
from dotenv import load_dotenv
|
||||
import shutil
|
||||
import uuid
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger("volcano_tts_test")
|
||||
|
||||
|
||||
def check_dependencies():
|
||||
dependencies = ["ffmpeg", "ffprobe"]
|
||||
missing = []
|
||||
for dep in dependencies:
|
||||
if not shutil.which(dep):
|
||||
missing.append(dep)
|
||||
if missing:
|
||||
logger.error(f"缺少必要依赖: {', '.join(missing)}")
|
||||
logger.error(
|
||||
"请安装缺失的依赖项。在Ubuntu上可以使用: sudo apt-get install ffmpeg"
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "volcano.env")
|
||||
if (os.path.exists(env_path)):
|
||||
load_dotenv(env_path)
|
||||
logger.info(f"已加载环境变量文件: {env_path}")
|
||||
else:
|
||||
logger.warning(f"环境变量文件不存在: {env_path}")
|
||||
|
||||
|
||||
async def test_volcano_tts():
|
||||
|
||||
if not check_dependencies():
|
||||
return
|
||||
api_access_token = os.getenv("VOLCANO_ACCESS_TOKEN", "")
|
||||
appid = os.getenv("VOLCANO_APP_ID", "")
|
||||
cluster = os.getenv("VOLCANO_CLUSTER", "volcano_tts")
|
||||
base_url = os.getenv(
|
||||
"VOLCANO_TTS_BASE_URL", "https://openspeech.bytedance.com/api/v1/tts"
|
||||
)
|
||||
role_name = os.getenv("ROLE_NAME", "Dundun Chicken")
|
||||
voice_type = os.getenv("VOICE_TYPE", "S_NkHcFJam1")
|
||||
speed_ratio = float(os.getenv("SPEED_RATIO", "1.0"))
|
||||
phrases = [
|
||||
"It's being upgraded. Please don't cut off the power.",
|
||||
"Enter the network configuration mode.",
|
||||
"Exit the network configuration mode.",
|
||||
"I'll speak louder.",
|
||||
"I'll speak softer.",
|
||||
"There's no internet connection. It's time to get off work.",
|
||||
f"Hello , I'm {role_name}",
|
||||
"Connected to the network.",
|
||||
"I'm out of power.",
|
||||
"The upgrade was successful.",
|
||||
"There seem to be some minor issues with the upgrade. Let's try it again.",
|
||||
"It's already at the maximum volume.",
|
||||
"You're so talkative.",
|
||||
]
|
||||
file_prefixes = [
|
||||
"upgrading",
|
||||
"enter_network_config",
|
||||
"exit_network_config",
|
||||
"volume_up",
|
||||
"volume_down",
|
||||
"network_lost",
|
||||
"wakeup",
|
||||
"network_connected",
|
||||
"low_energy",
|
||||
"upgrade_success",
|
||||
"upgrade_failed",
|
||||
"max_volume",
|
||||
"too_talkative"
|
||||
]
|
||||
assets_dir = os.getenv("ASSETS_DIR", "assets")
|
||||
output_dir = Path(assets_dir) / "tts_audio"
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
if not api_access_token or not appid:
|
||||
logger.error("缺少必要的配置: VOLCANO_ACCESS_TOKEN 或 VOLCANO_APP_ID")
|
||||
return
|
||||
logger.info(f"开始测试火山引擎TTS服务,角色: {role_name}, 音色: {voice_type}")
|
||||
for phrase, file_prefix in zip(phrases, file_prefixes):
|
||||
output_file_prefix = str(output_dir / file_prefix)
|
||||
unique_reqid = str(uuid.uuid4())
|
||||
payload = {
|
||||
"app": {
|
||||
"appid": appid,
|
||||
"token": "access_token",
|
||||
"cluster": cluster,
|
||||
},
|
||||
"user": {"uid": "test_user"},
|
||||
"audio": {
|
||||
"voice_type": voice_type,
|
||||
"encoding": "mp3",
|
||||
"speed_ratio": float(speed_ratio),
|
||||
},
|
||||
"request": {"reqid": unique_reqid, "text": phrase, "operation": "query"},
|
||||
}
|
||||
headers = {
|
||||
"Authorization": f"Bearer;{api_access_token}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
logger.info(f"准备发送请求,文本内容: {phrase}, 请求ID: {unique_reqid}")
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
base_url, headers=headers, json=payload
|
||||
) as response:
|
||||
logid = response.headers.get('X-Tt-Logid', 'unknown')
|
||||
logger.debug(f"服务端返回Logid: {logid}")
|
||||
|
||||
if response.status == 200:
|
||||
resp_json = await response.json()
|
||||
if "code" in resp_json:
|
||||
code = resp_json["code"]
|
||||
if code == 3000: # 成功状态码
|
||||
audio_base64 = resp_json.get("data")
|
||||
if audio_base64:
|
||||
audio_data = base64.b64decode(audio_base64)
|
||||
duration_ms = int(
|
||||
resp_json.get("addition", {}).get("duration", "0")
|
||||
)
|
||||
duration = duration_ms / 1000 # 转换为秒
|
||||
output_file = f"{output_file_prefix}.mp3"
|
||||
audio_stream = io.BytesIO(audio_data)
|
||||
sound = AudioSegment.from_file(
|
||||
audio_stream, format="mp3"
|
||||
)
|
||||
sound = (
|
||||
sound.set_frame_rate(16000)
|
||||
.set_sample_width(2)
|
||||
.set_channels(1)
|
||||
)
|
||||
sound.export(output_file, format="mp3", bitrate="16k")
|
||||
logger.info(f"TTS合成成功! 音频时长: {duration}秒")
|
||||
logger.info(f"保存音频到: {output_file}")
|
||||
else:
|
||||
error_msg = "音频数据不存在"
|
||||
logger.error(f"TTS合成失败: {error_msg}")
|
||||
else:
|
||||
error_code = resp_json.get('code')
|
||||
error_msg = resp_json.get('message', '未知错误')
|
||||
error_info = get_error_description(error_code, error_msg)
|
||||
logger.error(f"TTS合成失败: {error_info} (LogID: {logid})")
|
||||
else:
|
||||
logger.error(f"返回数据格式异常,缺少code字段: {resp_json}")
|
||||
else:
|
||||
error_text = await response.text()
|
||||
logger.error(
|
||||
f"TTS合成失败,状态码: {response.status}, 错误: {error_text} (LogID: {logid})"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"TTS请求出错: {str(e)}")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
def get_error_description(code, message):
|
||||
"""根据错误码返回详细的错误描述"""
|
||||
error_descriptions = {
|
||||
3001: "无效的请求,请检查参数",
|
||||
3003: "并发超限,请降低请求频率或增购并发",
|
||||
3005: "后端服务忙,请稍后重试",
|
||||
3006: "服务中断,请求已完成/失败之后,相同reqid再次请求",
|
||||
3010: "文本长度超限,请减少文本长度",
|
||||
3011: "无效文本,请检查文本内容",
|
||||
3030: "处理超时,请重试或检查文本",
|
||||
3031: "处理错误,后端出现异常",
|
||||
3032: "等待获取音频超时,请重试",
|
||||
3040: "后端链路连接错误,请重试",
|
||||
3050: "音色不存在,请检查voice_type参数"
|
||||
}
|
||||
|
||||
if "quota exceeded for types: xxxxxxxxx_lifetime" in message:
|
||||
return "试用版用量用完,需开通正式版才能继续使用"
|
||||
elif "quota exceeded for types: concurrency" in message:
|
||||
return "并发超过限定值,需减少并发调用或增购并发"
|
||||
elif "Init Engine Instance failed" in message:
|
||||
return "voice_type或cluster参数错误"
|
||||
elif "illegal input text" in message:
|
||||
return "文本无效,无可合成的有效内容"
|
||||
elif "requested grant not found" in message:
|
||||
return "鉴权失败,请检查appid和token是否正确"
|
||||
elif "access denied" in message:
|
||||
return "未拥有当前音色授权,请在控制台购买该音色"
|
||||
|
||||
return f"错误码: {code}, 错误信息: {message} - {error_descriptions.get(code, '未知错误')}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_volcano_tts())
|
||||
Reference in New Issue
Block a user