File size: 6,554 Bytes
3a02865 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
from flask import Blueprint, request, jsonify, send_file
import os
import threading
import time
import uuid
from src.mock_scraper import MockVideoScraper
import logging
# Cấu hình logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
video_bp = Blueprint('video', __name__)
# Dictionary để lưu trạng thái các job
video_jobs = {}
class VideoJob:
def __init__(self, job_id, prompt):
self.job_id = job_id
self.prompt = prompt
self.status = "pending" # pending, processing, completed, failed
self.video_path = None
self.error_message = None
self.created_at = time.time()
def generate_video_async(job_id, prompt):
"""Tạo video bất đồng bộ"""
job = video_jobs[job_id]
job.status = "processing"
scraper = None
try:
logger.info(f"Bắt đầu tạo video cho job {job_id} với prompt: {prompt}")
scraper = MockVideoScraper(headless=True)
video_path = scraper.generate_video_simple(prompt, timeout=300)
if video_path and os.path.exists(video_path):
job.video_path = video_path
job.status = "completed"
logger.info(f"Job {job_id} hoàn thành thành công")
else:
job.status = "failed"
job.error_message = "Không thể tạo video"
logger.error(f"Job {job_id} thất bại: Không thể tạo video")
except Exception as e:
job.status = "failed"
job.error_message = str(e)
logger.error(f"Job {job_id} thất bại với lỗi: {e}")
finally:
if scraper:
scraper.close()
@video_bp.route('/generate/<path:prompt>', methods=['GET'])
def generate_video_endpoint(prompt):
"""
Endpoint để tạo video từ prompt
URL: /api/video/generate/your_prompt_here
"""
try:
# Tạo job ID duy nhất
job_id = str(uuid.uuid4())
# Tạo job mới
job = VideoJob(job_id, prompt)
video_jobs[job_id] = job
# Bắt đầu tạo video trong thread riêng
thread = threading.Thread(target=generate_video_async, args=(job_id, prompt))
thread.daemon = True
thread.start()
return jsonify({
"success": True,
"job_id": job_id,
"message": "Video generation started",
"status_url": f"/api/video/status/{job_id}",
"download_url": f"/api/video/download/{job_id}"
}), 202
except Exception as e:
logger.error(f"Lỗi khi tạo job: {e}")
return jsonify({
"success": False,
"error": str(e)
}), 500
@video_bp.route('/status/<job_id>', methods=['GET'])
def get_job_status(job_id):
"""Kiểm tra trạng thái job"""
if job_id not in video_jobs:
return jsonify({
"success": False,
"error": "Job not found"
}), 404
job = video_jobs[job_id]
response = {
"success": True,
"job_id": job_id,
"status": job.status,
"prompt": job.prompt,
"created_at": job.created_at
}
if job.status == "failed" and job.error_message:
response["error"] = job.error_message
if job.status == "completed" and job.video_path:
response["download_url"] = f"/api/video/download/{job_id}"
response["video_ready"] = True
return jsonify(response)
@video_bp.route('/download/<job_id>', methods=['GET'])
def download_video(job_id):
"""Tải xuống video"""
if job_id not in video_jobs:
return jsonify({
"success": False,
"error": "Job not found"
}), 404
job = video_jobs[job_id]
if job.status != "completed" or not job.video_path:
return jsonify({
"success": False,
"error": "Video not ready",
"status": job.status
}), 400
if not os.path.exists(job.video_path):
return jsonify({
"success": False,
"error": "Video file not found"
}), 404
try:
return send_file(
job.video_path,
as_attachment=True,
download_name=f"video_{job_id}.mp4",
mimetype='video/mp4'
)
except Exception as e:
logger.error(f"Lỗi khi tải xuống video: {e}")
return jsonify({
"success": False,
"error": "Failed to download video"
}), 500
@video_bp.route('/jobs', methods=['GET'])
def list_jobs():
"""Liệt kê tất cả jobs"""
jobs_list = []
for job_id, job in video_jobs.items():
job_info = {
"job_id": job_id,
"prompt": job.prompt,
"status": job.status,
"created_at": job.created_at
}
if job.status == "completed":
job_info["download_url"] = f"/api/video/download/{job_id}"
jobs_list.append(job_info)
return jsonify({
"success": True,
"jobs": jobs_list,
"total": len(jobs_list)
})
@video_bp.route('/health', methods=['GET'])
def health_check():
"""Health check endpoint"""
return jsonify({
"success": True,
"message": "Video generation API is running",
"active_jobs": len([j for j in video_jobs.values() if j.status == "processing"]),
"total_jobs": len(video_jobs)
})
# Cleanup old jobs (older than 1 hour)
def cleanup_old_jobs():
"""Dọn dẹp các job cũ"""
current_time = time.time()
to_remove = []
for job_id, job in video_jobs.items():
if current_time - job.created_at > 3600: # 1 hour
to_remove.append(job_id)
# Xóa file video nếu có
if job.video_path and os.path.exists(job.video_path):
try:
os.remove(job.video_path)
except:
pass
for job_id in to_remove:
del video_jobs[job_id]
logger.info(f"Cleaned up {len(to_remove)} old jobs")
# Chạy cleanup mỗi 30 phút
import atexit
cleanup_timer = None
def start_cleanup_timer():
global cleanup_timer
cleanup_timer = threading.Timer(1800, cleanup_old_jobs) # 30 minutes
cleanup_timer.daemon = True
cleanup_timer.start()
def stop_cleanup_timer():
global cleanup_timer
if cleanup_timer:
cleanup_timer.cancel()
atexit.register(stop_cleanup_timer)
start_cleanup_timer()
|