90 lines
3.4 KiB
Python
90 lines
3.4 KiB
Python
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
from pathlib import Path
|
|
import uuid
|
|
import traceback
|
|
from app.services.tts_service import TTSService
|
|
from app.services.video_service import VideoService
|
|
from app.services.lipsync_service import LipSyncService
|
|
from app.core.config import settings
|
|
|
|
router = APIRouter()
|
|
|
|
class GenerateRequest(BaseModel):
|
|
text: str
|
|
voice: str = "zh-CN-YunxiNeural"
|
|
material_path: str
|
|
|
|
tasks = {} # In-memory task store
|
|
|
|
async def _process_video_generation(task_id: str, req: GenerateRequest):
|
|
try:
|
|
# Resolve path if it's relative
|
|
input_material_path = Path(req.material_path)
|
|
if not input_material_path.is_absolute():
|
|
input_material_path = settings.BASE_DIR.parent / req.material_path
|
|
|
|
tasks[task_id]["status"] = "processing"
|
|
tasks[task_id]["progress"] = 5
|
|
tasks[task_id]["message"] = "Initializing generation..."
|
|
|
|
# 1. TTS
|
|
tasks[task_id]["message"] = "Generating Audio (TTS)..."
|
|
tts = TTSService()
|
|
audio_path = settings.OUTPUT_DIR / f"{task_id}_audio.mp3"
|
|
await tts.generate_audio(req.text, req.voice, str(audio_path))
|
|
|
|
tasks[task_id]["progress"] = 30
|
|
|
|
# 2. LipSync
|
|
tasks[task_id]["message"] = "Synthesizing Video (MuseTalk)..."
|
|
lipsync = LipSyncService()
|
|
lipsync_video_path = settings.OUTPUT_DIR / f"{task_id}_lipsync.mp4"
|
|
|
|
# Check health and generate
|
|
health = await lipsync.check_health()
|
|
print(f"[LipSync] Health check: {health}")
|
|
if health.get("ready", False):
|
|
print(f"[LipSync] Starting MuseTalk inference...")
|
|
await lipsync.generate(str(input_material_path), str(audio_path), str(lipsync_video_path))
|
|
else:
|
|
# Skip lipsync if not available
|
|
print(f"[LipSync] MuseTalk not ready, copying original video")
|
|
import shutil
|
|
shutil.copy(str(input_material_path), lipsync_video_path)
|
|
|
|
tasks[task_id]["progress"] = 80
|
|
|
|
# 3. Composition
|
|
tasks[task_id]["message"] = "Final compositing..."
|
|
video = VideoService()
|
|
final_output = settings.OUTPUT_DIR / f"{task_id}_output.mp4"
|
|
await video.compose(str(lipsync_video_path), str(audio_path), str(final_output))
|
|
|
|
tasks[task_id]["status"] = "completed"
|
|
tasks[task_id]["progress"] = 100
|
|
tasks[task_id]["message"] = "Generation Complete!"
|
|
tasks[task_id]["output"] = str(final_output)
|
|
tasks[task_id]["download_url"] = f"/outputs/{final_output.name}"
|
|
|
|
except Exception as e:
|
|
tasks[task_id]["status"] = "failed"
|
|
tasks[task_id]["message"] = f"Error: {str(e)}"
|
|
tasks[task_id]["error"] = traceback.format_exc()
|
|
|
|
@router.post("/generate")
|
|
async def generate_video(req: GenerateRequest, background_tasks: BackgroundTasks):
|
|
task_id = str(uuid.uuid4())
|
|
tasks[task_id] = {"status": "pending", "task_id": task_id}
|
|
background_tasks.add_task(_process_video_generation, task_id, req)
|
|
return {"task_id": task_id}
|
|
|
|
@router.get("/tasks/{task_id}")
|
|
async def get_task(task_id: str):
|
|
return tasks.get(task_id, {"status": "not_found"})
|
|
|
|
@router.get("/tasks")
|
|
async def list_tasks():
|
|
return {"tasks": list(tasks.values())}
|