添加打包下载
This commit is contained in:
56
main.py
56
main.py
@@ -389,6 +389,62 @@ def serve_video_frames(job_id, filename):
|
|||||||
logging.error(f"提供文件时出错: {e}")
|
logging.error(f"提供文件时出错: {e}")
|
||||||
return jsonify({"error": "文件访问失败"}), 500
|
return jsonify({"error": "文件访问失败"}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/framepack', methods=['POST'])
|
||||||
|
def create_framepack():
|
||||||
|
"""接收一组相对路径(如 /frames/abc123/frame_0001.png),打包返回二进制"""
|
||||||
|
import struct
|
||||||
|
from video_frame_utils import FRAMES_ROOT
|
||||||
|
import os
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
if not data or 'urls' not in data or not isinstance(data['urls'], list):
|
||||||
|
return jsonify({'error': 'Missing urls list'}), 400
|
||||||
|
|
||||||
|
# 构建完整本地路径,验证安全性
|
||||||
|
file_paths = []
|
||||||
|
for url in data['urls']:
|
||||||
|
# 只允许以 /frames/ 开头的路径
|
||||||
|
if not url.startswith('/frames/'):
|
||||||
|
return jsonify({'error': f'Invalid URL prefix: {url}'}), 400
|
||||||
|
# 提取 job_id 和 filename
|
||||||
|
parts = url[len('/frames/'):].split('/', 1)
|
||||||
|
if len(parts) != 2:
|
||||||
|
return jsonify({'error': f'Malformed URL: {url}'}), 400
|
||||||
|
job_id, filename = parts
|
||||||
|
# 安全路径拼接
|
||||||
|
safe_job = os.path.basename(job_id)
|
||||||
|
safe_file = os.path.basename(filename)
|
||||||
|
if not safe_file.endswith('.png'):
|
||||||
|
return jsonify({'error': f'Only .png allowed: {filename}'}), 400
|
||||||
|
full_path = os.path.join(FRAMES_ROOT, safe_job, safe_file)
|
||||||
|
if not os.path.isfile(full_path):
|
||||||
|
return jsonify({'error': f'File not found: {full_path}'}), 404
|
||||||
|
file_paths.append(full_path)
|
||||||
|
|
||||||
|
# 构建 FramePack 二进制
|
||||||
|
pack_data = bytearray()
|
||||||
|
frame_count = len(file_paths)
|
||||||
|
pack_data.extend(struct.pack('>I', frame_count)) # big-endian uint32
|
||||||
|
|
||||||
|
# 先读所有文件到内存(小批量安全)
|
||||||
|
frames = []
|
||||||
|
for path in file_paths:
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
frames.append(f.read())
|
||||||
|
|
||||||
|
for frame in frames:
|
||||||
|
pack_data.extend(struct.pack('>I', len(frame)))
|
||||||
|
pack_data.extend(frame)
|
||||||
|
|
||||||
|
response = app.response_class(
|
||||||
|
response=bytes(pack_data),
|
||||||
|
status=200,
|
||||||
|
mimetype='application/octet-stream'
|
||||||
|
)
|
||||||
|
response.headers['Content-Disposition'] = 'inline; filename="framepack.bin"'
|
||||||
|
return response
|
||||||
|
|
||||||
@app.route('/health', methods=['GET'])
|
@app.route('/health', methods=['GET'])
|
||||||
def health_check():
|
def health_check():
|
||||||
"""健康检查接口,直接返回 'ok'"""
|
"""健康检查接口,直接返回 'ok'"""
|
||||||
|
|||||||
@@ -109,9 +109,6 @@ def process_video_frame_extraction(data, file_registry, file_lock, task_id=None,
|
|||||||
sys.executable, '-m', 'yt_dlp',
|
sys.executable, '-m', 'yt_dlp',
|
||||||
video_url,
|
video_url,
|
||||||
'-o', temp_base,
|
'-o', temp_base,
|
||||||
'-f', 'bv*[height<=720]+ba/b',
|
|
||||||
'--no-warnings',
|
|
||||||
'--progress', # 显式启用进度(可选)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# 使用 Popen 实时捕获 stderr
|
# 使用 Popen 实时捕获 stderr
|
||||||
|
|||||||
Reference in New Issue
Block a user