'commit'
This commit is contained in:
@@ -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,22 +67,54 @@ async def generate_video(request: VideoRetalkRequest):
|
||||
head_move_strength=request.head_move_strength
|
||||
)
|
||||
|
||||
if video_result and video_result['video_url']:
|
||||
return VideoRetalkResponse(
|
||||
success=True,
|
||||
message="视频生成成功",
|
||||
video_url=video_result['video_url'],
|
||||
task_id=str(uuid.uuid4()),
|
||||
video_duration=video_result['video_duration'],
|
||||
video_ratio=video_result['video_ratio'],
|
||||
request_id=str(uuid.uuid4())
|
||||
)
|
||||
else:
|
||||
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=obs_url,
|
||||
task_id=str(uuid.uuid4()),
|
||||
video_duration=video_result['video_duration'],
|
||||
video_ratio=video_result['video_ratio'],
|
||||
request_id=str(uuid.uuid4())
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"视频生成接口错误: {e}")
|
||||
raise HTTPException(
|
||||
|
Binary file not shown.
@@ -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(() => {
|
||||
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';
|
||||
layer.msg('视频合成成功', {icon: 1});
|
||||
} 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');
|
||||
videoResult.style.display = 'block';
|
||||
generateBtn.disabled = false;
|
||||
layer.msg('视频合成成功', {icon: 1});
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user