'commit'
This commit is contained in:
Binary file not shown.
@@ -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)}")
|
||||||
)
|
|
||||||
|
@@ -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) # 作文批阅路由
|
||||||
|
BIN
dsLightRag/static/audio/tts_20250902_074150_a7eb0d9a.mp3
Normal file
BIN
dsLightRag/static/audio/tts_20250902_074150_a7eb0d9a.mp3
Normal file
Binary file not shown.
BIN
dsLightRag/static/audio/tts_20250902_074443_0df4889a.mp3
Normal file
BIN
dsLightRag/static/audio/tts_20250902_074443_0df4889a.mp3
Normal file
Binary file not shown.
BIN
dsLightRag/static/audio/tts_20250902_074755_81ca2382.mp3
Normal file
BIN
dsLightRag/static/audio/tts_20250902_074755_81ca2382.mp3
Normal file
Binary file not shown.
BIN
dsLightRag/static/audio/tts_20250902_075228_90ccd2a8.mp3
Normal file
BIN
dsLightRag/static/audio/tts_20250902_075228_90ccd2a8.mp3
Normal file
Binary file not shown.
@@ -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';
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user