This commit is contained in:
2025-09-02 08:58:30 +08:00
parent 044312e500
commit dab80a61d2
3 changed files with 87 additions and 19 deletions

View File

@@ -1,11 +1,16 @@
import datetime
import logging
import uuid
import os
import time
import requests
from typing import Optional
from fastapi import APIRouter, HTTPException, Query, Request
from fastapi import APIRouter, HTTPException, Query, Request, BackgroundTasks
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from Util.ObsUtil import ObsUploader
from Config.Config import OBS_BUCKET, OBS_SERVER
from Config import Config
from Util.VideoRetalk import VideoRetalk
@@ -41,7 +46,7 @@ class VideoRetalkResponse(BaseModel):
@router.post("/generate", response_model=VideoRetalkResponse)
async def generate_video(request: VideoRetalkRequest):
async def generate_video(request: VideoRetalkRequest, background_tasks: BackgroundTasks):
"""
生成人物朗读视频接口
根据输入的人物图片和音频,生成口型匹配的朗读视频
@@ -62,21 +67,53 @@ async def generate_video(request: VideoRetalkRequest):
head_move_strength=request.head_move_strength
)
if video_result and video_result['video_url']:
if not video_result or not video_result['video_url']:
return VideoRetalkResponse(
success=False,
message="视频生成失败"
)
# 下载视频到本地临时文件
timestamp = int(time.time())
filename = f"video_{uuid.uuid4().hex[:8]}_{timestamp}.mp4"
output_dir = "static/video"
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, filename)
# 从API下载视频文件
response = requests.get(video_result['video_url'], stream=True)
if response.status_code != 200:
raise HTTPException(status_code=500, detail="视频文件下载失败")
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
# 上传到OBS
obs_uploader = ObsUploader()
obs_object_key = f"HuangHai/video/{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', '未知错误')}")
# 构造OBS访问URL
obs_url = f"https://{OBS_BUCKET}.{OBS_SERVER}/{obs_object_key}"
# 安排后台任务删除本地文件
background_tasks.add_task(os.remove, output_path) if os.path.exists(output_path) else None
return VideoRetalkResponse(
success=True,
message="视频生成成功",
video_url=video_result['video_url'],
video_url=obs_url,
task_id=str(uuid.uuid4()),
video_duration=video_result['video_duration'],
video_ratio=video_result['video_ratio'],
request_id=str(uuid.uuid4())
)
else:
return VideoRetalkResponse(
success=False,
message="视频生成失败"
)
except Exception as e:
logger.error(f"视频生成接口错误: {e}")

View File

@@ -314,11 +314,20 @@
});
// 生成按钮点击事件
document.getElementById('generate-btn').addEventListener('click', function() {
document.getElementById('generate-btn').addEventListener('click', async function() {
const generateBtn = this;
const loading = document.getElementById('loading');
const videoResult = document.getElementById('video-result');
const emptyResult = document.getElementById('empty-result');
const videoPlayer = document.getElementById('video-player');
// 获取参数
const imageUrl = document.getElementById('image-url').value.trim();
const audioUrl = document.getElementById('audio-url').value.trim();
const eyeMovement = document.getElementById('eye-movement').value;
// 映射眼睛移动频率
const eyeMoveFreqMap = {low: 0.3, medium: 0.5, high: 0.7};
// 显示加载状态
generateBtn.disabled = true;
@@ -326,13 +335,35 @@
videoResult.style.display = 'none';
emptyResult.style.display = 'none';
// 模拟生成过程
setTimeout(() => {
loading.classList.remove('active');
try {
// 实际API调用
const response = await fetch('/api/video/generate', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
image_url: imageUrl,
audio_url: audioUrl,
eye_move_freq: eyeMoveFreqMap[eyeMovement]
})
});
const data = await response.json();
if (data.success && data.video_url) {
videoPlayer.src = data.video_url;
videoResult.style.display = 'block';
generateBtn.disabled = false;
layer.msg('视频合成成功', {icon: 1});
}, 2000);
} else {
emptyResult.style.display = 'block';
layer.msg('视频合成失败: ' + data.message, {icon: 2});
}
} catch (error) {
console.error('API调用失败:', error);
emptyResult.style.display = 'block';
layer.msg('网络错误,无法连接服务器', {icon: 2});
} finally {
loading.classList.remove('active');
generateBtn.disabled = false;
}
});
});
</script>