""" 音频处理 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 }