hs-video-api/routes.py
2025-06-07 14:57:21 +08:00

349 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from flask import Blueprint, request, jsonify
import os
from datetime import datetime
from video_service import get_video_service
from task_queue_manager import get_queue_manager
# 创建蓝图
api_bp = Blueprint('api', __name__)
@api_bp.route('/api/video/create', methods=['POST'])
def create_video_task():
"""创建视频生成任务"""
content_type = request.content_type
# print(f"Content-Type: {content_type}")
# print(f"Files: {request.files}")
# print(f"Form: {request.form}")
# 检查是否为文件上传模式
if content_type and 'multipart/form-data' in content_type:
# 文件上传模式
if 'image_file' not in request.files:
return jsonify({
'success': False,
'error': '缺少必需的参数: image_file'
}), 400
image_file = request.files['image_file']
if image_file.filename == '':
return jsonify({
'success': False,
'error': '未选择文件'
}), 400
# 读取图片文件并进行Base64编码
import base64
image_data = image_file.read()
image_base64 = base64.b64encode(image_data).decode('utf-8')
# 构建image_url
image_url = f"data:image/{image_file.filename.split('.')[-1]};base64,{image_base64}"
# 从表单数据获取其他参数
prompt = request.form.get('prompt', '')
duration = request.form.get('duration')
callback_url = request.form.get('callback_url')
elif content_type and 'application/json' in content_type:
# JSON请求模式
data = request.get_json()
if not data:
return jsonify({
'success': False,
'error': '无效的JSON数据'
}), 400
image_url = data.get('image_url')
prompt = data.get('prompt', '')
duration = data.get('duration')
callback_url = data.get('callback_url')
if not image_url:
return jsonify({
'success': False,
'error': '缺少必需的参数: image_url'
}), 400
else:
return jsonify({
'success': False,
'error': '不支持的请求类型请使用multipart/form-data或application/json'
}), 400
# 验证必需参数
if not prompt:
return jsonify({
'success': False,
'error': '缺少必需的参数: prompt'
}), 400
# 构建content
content = {
'image_url': image_url,
'prompt': prompt
}
# 构建parameters
parameters = {}
if duration is not None and duration != '':
try:
parameters['duration'] = float(duration)
except (ValueError, TypeError):
return jsonify({
'success': False,
'error': 'duration参数必须是数字'
}), 400
try:
# 获取队列管理器
queue_manager = get_queue_manager()
# 检查是否可以直接创建任务
if queue_manager.can_create_task():
# 直接创建任务
video_service = get_video_service()
result = video_service.create_video_generation_task(content, callback_url, parameters)
if result['success']:
task_data = result['data']
task_data['status'] = 'running' # 设置初始状态
# 添加到运行队列
queue_manager.add_task_to_queue(task_data)
return jsonify({
'success': True,
'data': task_data,
'queue_status': 'running'
})
else:
return jsonify({
'success': False,
'error': result['error']
}), 500
else:
# 队列已满,创建任务但加入等待队列
video_service = get_video_service()
result = video_service.create_video_generation_task(content, callback_url, parameters)
if result['success']:
task_data = result['data']
task_data['status'] = 'waiting' # 设置等待状态
# 添加到等待队列
queue_manager.add_task_to_queue(task_data)
return jsonify({
'success': True,
'data': task_data,
'queue_status': 'waiting',
'message': '任务已创建并加入等待队列,将在有空闲位置时开始执行'
})
else:
return jsonify({
'success': False,
'error': result['error']
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'创建任务失败: {str(e)}'
}), 500
@api_bp.route('/api/video/status/<task_id>', methods=['GET'])
def get_task_status(task_id):
"""查询任务状态"""
video_service = get_video_service()
result = video_service.get_task_status(task_id)
if result['success']:
return jsonify({
'success': True,
'data': result['data']
})
else:
return jsonify({
'success': False,
'error': result['error']
}), 500
@api_bp.route('/api/video/result/<task_id>', methods=['GET'])
def get_task_result(task_id):
"""获取任务结果"""
try:
# 先从缓存查询
queue_manager = get_queue_manager()
cached_task = queue_manager.get_task_from_cache(task_id)
if cached_task:
# 从缓存返回结果
task_data = cached_task
print(f"从缓存获取结果: {task_id}")
else:
# 缓存中没有调用SDK查询
print(f"缓存中没有调用SDK查询: {task_id}")
video_service = get_video_service()
result = video_service.get_task_status(task_id)
if not result['success']:
return jsonify({
'success': False,
'error': result['error']
}), 500
task_data = result['data']
# 检查任务状态
if task_data['status'] == 'succeeded':
return jsonify({
'success': True,
'data': {
'task_id': task_data['task_id'],
'status': task_data['status'],
'video_url': task_data.get('content', {}).get('video_url') if task_data.get('content') else None,
'created_at': task_data.get('created_at'),
'updated_at': task_data.get('updated_at')
}
})
elif task_data['status'] == 'failed':
return jsonify({
'success': False,
'error': f"任务失败: {task_data.get('error', '未知错误')}"
}), 500
elif task_data['status'] == 'not_found':
return jsonify({
'success': False,
'error': '任务不存在或已被删除'
}), 404
else:
return jsonify({
'success': True,
'data': {
'task_id': task_data['task_id'],
'status': task_data['status'],
'message': '任务正在处理中,请稍后查询'
}
})
except Exception as e:
return jsonify({
'success': False,
'error': f'查询异常: {str(e)}'
}), 500
@api_bp.route('/api/video/tasks', methods=['GET'])
def get_task_list():
"""获取任务列表"""
limit = request.args.get('limit', 20, type=int)
offset = request.args.get('offset', 0, type=int)
try:
# 验证参数
if limit < 1 or limit > 100:
return jsonify({
'success': False,
'error': 'limit必须在1-100之间'
}), 400
if offset < 0:
return jsonify({
'success': False,
'error': 'offset必须大于等于0'
}), 400
# 调用火山引擎API获取任务列表
video_service = get_video_service()
result = video_service.get_task_list(limit, offset)
if result['success']:
task_data = result['data']
return jsonify({
'success': True,
'data': task_data
})
else:
return jsonify({
'success': False,
'error': result['error']
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'获取任务列表失败: {str(e)}'
}), 500
@api_bp.route('/api/video/cancel/<task_id>', methods=['DELETE'])
def cancel_task(task_id):
"""删除任务"""
try:
# 调用火山引擎API删除任务
video_service = get_video_service()
result = video_service.delete_task(task_id)
if result['success']:
# 同时从队列管理器中删除缓存
queue_manager = get_queue_manager()
queue_manager.remove_task_from_cache(task_id)
return jsonify({
'success': True,
'message': '任务删除成功'
})
else:
return jsonify({
'success': False,
'error': result['error']
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'删除任务失败: {str(e)}'
}), 500
@api_bp.route('/api/video/queue/status', methods=['GET'])
def get_queue_status():
"""获取队列状态"""
try:
queue_manager = get_queue_manager()
status = queue_manager.get_queue_status()
return jsonify({
'success': True,
'data': status
})
except Exception as e:
return jsonify({
'success': False,
'error': f'获取队列状态失败: {str(e)}'
}), 500
@api_bp.route('/health', methods=['GET'])
def health_check():
"""健康检查"""
return jsonify({
'status': 'healthy',
'timestamp': datetime.now().isoformat(),
'service': 'video-generation-api'
})
@api_bp.route('/queue', methods=['GET'])
def status_check():
"""状态检测"""
try:
queue_manager = get_queue_manager()
status = queue_manager.get_queue_status()
queue_running = status['running_task_ids']
queue_pending = status['waiting_task_ids']
return jsonify({
'queue_running': queue_running,
'queue_pending': queue_pending
})
except Exception as e:
return jsonify({
'queue_running': [],
'queue_pending': []
}), 500