chore: sync code and project files
This commit is contained in:
212
api/tasks/audio_tasks.py
Normal file
212
api/tasks/audio_tasks.py
Normal file
@@ -0,0 +1,212 @@
|
||||
"""
|
||||
音频处理 Celery 任务
|
||||
TTS 生成、花字渲染等
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from celery import shared_task
|
||||
|
||||
import config
|
||||
from modules import factory, ffmpeg_utils
|
||||
from modules.text_renderer import renderer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@shared_task(bind=True, name="audio.generate_tts")
|
||||
def generate_tts_task(
|
||||
self,
|
||||
text: str,
|
||||
voice_type: str = "zh_female_santongyongns_saturn_bigtts",
|
||||
target_duration: Optional[float] = None,
|
||||
output_path: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
生成 TTS 音频(异步任务)
|
||||
|
||||
Args:
|
||||
text: 要转换的文本
|
||||
voice_type: TTS 音色
|
||||
target_duration: 目标时长(秒),如果指定会调整音频速度
|
||||
output_path: 输出路径
|
||||
|
||||
Returns:
|
||||
{"status": "success", "path": "...", "url": "..."}
|
||||
"""
|
||||
task_id = self.request.id
|
||||
logger.info(f"[Task {task_id}] 生成 TTS: {text[:30]}...")
|
||||
|
||||
if not output_path:
|
||||
timestamp = int(time.time())
|
||||
output_path = str(config.TEMP_DIR / f"tts_{timestamp}.mp3")
|
||||
|
||||
try:
|
||||
# 生成 TTS
|
||||
audio_path = factory.generate_voiceover_volcengine(
|
||||
text=text,
|
||||
voice_type=voice_type,
|
||||
output_path=output_path
|
||||
)
|
||||
|
||||
if not audio_path or not os.path.exists(audio_path):
|
||||
raise RuntimeError("TTS 生成失败")
|
||||
|
||||
# 如果需要调整时长
|
||||
if target_duration:
|
||||
adjusted_path = str(config.TEMP_DIR / f"tts_adj_{int(time.time())}.mp3")
|
||||
ffmpeg_utils.adjust_audio_duration(audio_path, target_duration, adjusted_path)
|
||||
|
||||
# 删除原始文件
|
||||
if audio_path != output_path:
|
||||
os.remove(audio_path)
|
||||
|
||||
audio_path = adjusted_path
|
||||
|
||||
output_url = f"/static/temp/{Path(audio_path).name}"
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"path": audio_path,
|
||||
"url": output_url,
|
||||
"task_id": task_id
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Task {task_id}] TTS 生成失败: {e}")
|
||||
raise
|
||||
|
||||
|
||||
@shared_task(bind=True, name="audio.generate_fancy_text")
|
||||
def generate_fancy_text_task(
|
||||
self,
|
||||
text: str,
|
||||
style: Optional[Dict[str, Any]] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
生成花字图片(异步任务)
|
||||
|
||||
Args:
|
||||
text: 花字文本
|
||||
style: 样式配置
|
||||
|
||||
Returns:
|
||||
{"status": "success", "path": "...", "url": "..."}
|
||||
"""
|
||||
task_id = self.request.id
|
||||
logger.info(f"[Task {task_id}] 生成花字: {text}")
|
||||
|
||||
if not style:
|
||||
style = {
|
||||
"font_size": 72,
|
||||
"font_color": "#FFFFFF",
|
||||
"stroke": {"color": "#000000", "width": 5}
|
||||
}
|
||||
|
||||
try:
|
||||
img_path = renderer.render(
|
||||
text=text,
|
||||
style=style,
|
||||
cache=False
|
||||
)
|
||||
|
||||
if not img_path or not os.path.exists(img_path):
|
||||
raise RuntimeError("花字生成失败")
|
||||
|
||||
output_url = f"/static/temp/{Path(img_path).name}"
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"path": img_path,
|
||||
"url": output_url,
|
||||
"task_id": task_id
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Task {task_id}] 花字生成失败: {e}")
|
||||
raise
|
||||
|
||||
|
||||
@shared_task(bind=True, name="audio.batch_generate_tts")
|
||||
def batch_generate_tts_task(
|
||||
self,
|
||||
items: list,
|
||||
voice_type: str = "zh_female_santongyongns_saturn_bigtts"
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
批量生成 TTS 音频(异步任务)
|
||||
|
||||
Args:
|
||||
items: [{"text": "...", "target_duration": 3.0}, ...]
|
||||
voice_type: TTS 音色
|
||||
|
||||
Returns:
|
||||
{"status": "success", "results": [...]}
|
||||
"""
|
||||
task_id = self.request.id
|
||||
logger.info(f"[Task {task_id}] 批量生成 TTS: {len(items)} 条")
|
||||
|
||||
results = []
|
||||
timestamp = int(time.time())
|
||||
|
||||
for i, item in enumerate(items):
|
||||
text = item.get("text", "")
|
||||
target_duration = item.get("target_duration")
|
||||
|
||||
if not text:
|
||||
results.append({"index": i, "status": "skipped", "reason": "空文本"})
|
||||
continue
|
||||
|
||||
try:
|
||||
output_path = str(config.TEMP_DIR / f"tts_batch_{timestamp}_{i}.mp3")
|
||||
|
||||
audio_path = factory.generate_voiceover_volcengine(
|
||||
text=text,
|
||||
voice_type=voice_type,
|
||||
output_path=output_path
|
||||
)
|
||||
|
||||
if target_duration and audio_path:
|
||||
adjusted_path = str(config.TEMP_DIR / f"tts_batch_adj_{timestamp}_{i}.mp3")
|
||||
ffmpeg_utils.adjust_audio_duration(audio_path, target_duration, adjusted_path)
|
||||
audio_path = adjusted_path
|
||||
|
||||
if audio_path:
|
||||
results.append({
|
||||
"index": i,
|
||||
"status": "success",
|
||||
"path": audio_path,
|
||||
"url": f"/static/temp/{Path(audio_path).name}"
|
||||
})
|
||||
else:
|
||||
results.append({"index": i, "status": "failed", "reason": "生成失败"})
|
||||
|
||||
except Exception as e:
|
||||
results.append({"index": i, "status": "failed", "reason": str(e)})
|
||||
|
||||
# 更新进度
|
||||
progress = (i + 1) / len(items)
|
||||
self.update_state(state="PROGRESS", meta={"progress": progress, "completed": i + 1, "total": len(items)})
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"results": results,
|
||||
"task_id": task_id
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user