This commit is contained in:
2025-09-02 08:23:33 +08:00
parent d68b23795e
commit 1512346ef0
8 changed files with 62 additions and 67 deletions

View File

@@ -1,23 +1,23 @@
import logging import logging
import uuid
from typing import Optional
from fastapi import APIRouter, HTTPException, Query # 添加Query导入 from fastapi import APIRouter, HTTPException, BackgroundTasks, Query
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional
import os
import uuid
import time
from Util.ObsUtil import ObsUploader
from Config.Config import OBS_BUCKET, OBS_SERVER
# 导入TTS生成类
from Util.GengerateAudio import ByteDanceTTS from Util.GengerateAudio import ByteDanceTTS
# 创建声音生成路由
router = APIRouter(prefix="/api/tts", tags=["声音生成"])
# 配置日志 # 配置日志
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/tts", tags=["文生音频"])
# 初始化TTS实例 # 初始化TTS实例(全局单例,避免重复创建)
tts_instance = ByteDanceTTS() tts_instance = ByteDanceTTS()
class TextToSpeechRequest(BaseModel): class TextToSpeechRequest(BaseModel):
"""文本转语音请求参数"""
text: str text: str
voice_type: Optional[str] = None voice_type: Optional[str] = None
speed_ratio: Optional[float] = 1.0 speed_ratio: Optional[float] = 1.0
@@ -26,16 +26,6 @@ class TextToSpeechRequest(BaseModel):
encoding: Optional[str] = "mp3" encoding: Optional[str] = "mp3"
class TextToSpeechResponse(BaseModel):
"""文本转语音响应"""
success: bool
message: str
audio_url: Optional[str] = None
audio_size: Optional[float] = None
audio_format: Optional[str] = None
request_id: Optional[str] = None
@router.get("/voice-categories") @router.get("/voice-categories")
async def get_voice_categories(): async def get_voice_categories():
try: try:
@@ -52,6 +42,7 @@ async def get_voice_categories():
detail=f"获取音色分类失败: {str(e)}" detail=f"获取音色分类失败: {str(e)}"
) )
# 恢复原始的音色列表接口路由 # 恢复原始的音色列表接口路由
@router.get("/voices") @router.get("/voices")
async def get_voices_by_category(category: str = Query(...)): # 现在Query已定义 async def get_voices_by_category(category: str = Query(...)): # 现在Query已定义
@@ -62,7 +53,7 @@ async def get_voices_by_category(category: str = Query(...)): # 现在Query已
"success": False, "success": False,
"message": f"未找到分类 '{category}' 下的音色" "message": f"未找到分类 '{category}' 下的音色"
} }
return { return {
"success": True, "success": True,
"data": voices, "data": voices,
@@ -93,61 +84,62 @@ async def get_all_voices():
logger.error(f"获取所有音色分类和列表错误: {e}") logger.error(f"获取所有音色分类和列表错误: {e}")
raise HTTPException( raise HTTPException(
status_code=500, status_code=500,
detail=f"获取所有音色分类和列表失败: {str(e)}" detail=f"获取所有音色分类和列表失败: {str(e)}")
)
@router.post("/generate", response_model=TextToSpeechResponse) @router.post("/generate")
async def generate_audio(request: TextToSpeechRequest): async def generate_audio(request: TextToSpeechRequest, background_tasks: BackgroundTasks):
"""
文本转语音接口
根据输入文本和语音参数生成音频文件
"""
try: try:
# 生成唯一文件名 # 1. 生成唯一文件名和路径
import os timestamp = int(time.time())
from datetime import datetime filename = f"tts_{uuid.uuid4().hex[:8]}_{timestamp}.mp3"
# 确保输出目录存在
output_dir = "static/audio" output_dir = "static/audio"
os.makedirs(output_dir, exist_ok=True) os.makedirs(output_dir, exist_ok=True)
# 生成唯一文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
unique_id = str(uuid.uuid4())[:8]
filename = f"tts_{timestamp}_{unique_id}.{request.encoding}"
output_path = os.path.join(output_dir, filename) output_path = os.path.join(output_dir, filename)
# 调用TTS工具生成音频 # 2. 使用ByteDanceTTS生成音频文件
# ------------------- TTS生成逻辑开始 -------------------
audio_data = tts_instance.generate_audio( audio_data = tts_instance.generate_audio(
text=request.text, text=request.text,
output_path=output_path,
voice_type=request.voice_type, voice_type=request.voice_type,
speed_ratio=request.speed_ratio, speed_ratio=request.speed_ratio,
volume_ratio=request.volume_ratio, volume_ratio=request.volume_ratio,
pitch_ratio=request.pitch_ratio, pitch_ratio=request.pitch_ratio,
encoding=request.encoding, encoding=request.encoding
output_path=output_path
) )
if audio_data: if not audio_data:
# 构建可访问的URL raise HTTPException(status_code=500, detail="TTS音频生成失败")
audio_url = f"/{output_path}" # ------------------- TTS生成逻辑结束 -------------------
return TextToSpeechResponse( # 3. 上传到OBS
success=True, obs_uploader = ObsUploader()
message="音频生成成功", obs_object_key = f"HuangHai/tts/{filename}"
audio_url=audio_url, upload_success, upload_result = obs_uploader.upload_file(obs_object_key, output_path)
audio_format=request.encoding,
request_id=str(uuid.uuid4()) if not upload_success:
) # 上传失败,清理文件并抛出异常
else: background_tasks.add_task(os.remove, output_path) if os.path.exists(output_path) else None
return TextToSpeechResponse( raise HTTPException(status_code=500, detail=f"OBS上传失败: {upload_result.get('errorMessage', '未知错误')}")
success=False,
message="音频生成失败" # 4. 构造OBS访问URL
) obs_url = f"https://{OBS_BUCKET}.{OBS_SERVER}/{obs_object_key}"
# 5. 安排后台任务删除本地文件
background_tasks.add_task(os.remove, output_path) if os.path.exists(output_path) else None
# 6. 返回OBS URL
return {
"status": "success",
"message": "音频生成并上传成功",
"audio_url": obs_url, # 确保此处没有添加任何引号或反引号
"filename": filename,
"audio_size": len(audio_data) / 1024 # 音频大小(KB)
}
except Exception as e: except Exception as e:
logger.error(f"文本转语音接口错误: {e}") # 捕获所有异常并清理文件
raise HTTPException( if 'output_path' in locals() and os.path.exists(output_path):
status_code=500, background_tasks.add_task(os.remove, output_path)
detail=f"音频生成失败: {str(e)}" raise HTTPException(status_code=500, detail=f"处理失败: {str(e)}")
)

View File

@@ -64,7 +64,7 @@ app.add_middleware(
) )
# 挂载静态文件目录 # 挂载静态文件目录
app.mount("/static", StaticFiles(directory="Static"), name="static") app.mount("/static", StaticFiles(directory="static"), name="static")
# 加载路由 # 加载路由
app.include_router(zuowen_router) # 作文批阅路由 app.include_router(zuowen_router) # 作文批阅路由

View File

@@ -486,11 +486,14 @@
const data = await response.json(); const data = await response.json();
console.log('音频生成结果:', data); console.log('音频生成结果:', data);
if (data.success) { if (data.status==='success') {
// 显示成功消息 // 显示成功消息
showSuccess('语音生成成功'); showSuccess('语音生成成功');
// 设置音频播放器 // 设置音频播放器
if(data.audio_url){
console.log('音频文件地址:', data.audio_url)
}
audioPlayer.src = data.audio_url; audioPlayer.src = data.audio_url;
audioResult.style.display = 'block'; audioResult.style.display = 'block';