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 uuid
from typing import Optional
from fastapi import APIRouter, HTTPException, Query # 添加Query导入
from fastapi import APIRouter, HTTPException, BackgroundTasks, Query
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
# 创建声音生成路由
router = APIRouter(prefix="/api/tts", tags=["声音生成"])
# 配置日志
logger = logging.getLogger(__name__)
# 初始化TTS实例
router = APIRouter(prefix="/api/tts", tags=["文生音频"])
# 初始化TTS实例(全局单例,避免重复创建)
tts_instance = ByteDanceTTS()
class TextToSpeechRequest(BaseModel):
"""文本转语音请求参数"""
text: str
voice_type: Optional[str] = None
speed_ratio: Optional[float] = 1.0
@@ -26,16 +26,6 @@ class TextToSpeechRequest(BaseModel):
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")
async def get_voice_categories():
try:
@@ -52,6 +42,7 @@ async def get_voice_categories():
detail=f"获取音色分类失败: {str(e)}"
)
# 恢复原始的音色列表接口路由
@router.get("/voices")
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,
"message": f"未找到分类 '{category}' 下的音色"
}
return {
"success": True,
"data": voices,
@@ -93,61 +84,62 @@ async def get_all_voices():
logger.error(f"获取所有音色分类和列表错误: {e}")
raise HTTPException(
status_code=500,
detail=f"获取所有音色分类和列表失败: {str(e)}"
)
detail=f"获取所有音色分类和列表失败: {str(e)}")
@router.post("/generate", response_model=TextToSpeechResponse)
async def generate_audio(request: TextToSpeechRequest):
"""
文本转语音接口
根据输入文本和语音参数生成音频文件
"""
@router.post("/generate")
async def generate_audio(request: TextToSpeechRequest, background_tasks: BackgroundTasks):
try:
# 生成唯一文件名
import os
from datetime import datetime
# 确保输出目录存在
# 1. 生成唯一文件名和路径
timestamp = int(time.time())
filename = f"tts_{uuid.uuid4().hex[:8]}_{timestamp}.mp3"
output_dir = "static/audio"
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)
# 调用TTS工具生成音频
# 2. 使用ByteDanceTTS生成音频文件
# ------------------- TTS生成逻辑开始 -------------------
audio_data = tts_instance.generate_audio(
text=request.text,
output_path=output_path,
voice_type=request.voice_type,
speed_ratio=request.speed_ratio,
volume_ratio=request.volume_ratio,
pitch_ratio=request.pitch_ratio,
encoding=request.encoding,
output_path=output_path
encoding=request.encoding
)
if audio_data:
# 构建可访问的URL
audio_url = f"/{output_path}"
return TextToSpeechResponse(
success=True,
message="音频生成成功",
audio_url=audio_url,
audio_format=request.encoding,
request_id=str(uuid.uuid4())
)
else:
return TextToSpeechResponse(
success=False,
message="音频生成失败"
)
if not audio_data:
raise HTTPException(status_code=500, detail="TTS音频生成失败")
# ------------------- TTS生成逻辑结束 -------------------
# 3. 上传到OBS
obs_uploader = ObsUploader()
obs_object_key = f"HuangHai/tts/{filename}"
upload_success, upload_result = obs_uploader.upload_file(obs_object_key, output_path)
if not upload_success:
# 上传失败,清理文件并抛出异常
background_tasks.add_task(os.remove, output_path) if os.path.exists(output_path) else None
raise HTTPException(status_code=500, detail=f"OBS上传失败: {upload_result.get('errorMessage', '未知错误')}")
# 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:
logger.error(f"文本转语音接口错误: {e}")
raise HTTPException(
status_code=500,
detail=f"音频生成失败: {str(e)}"
)
# 捕获所有异常并清理文件
if 'output_path' in locals() and os.path.exists(output_path):
background_tasks.add_task(os.remove, output_path)
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) # 作文批阅路由

View File

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