from flask import Blueprint, request, jsonify from flask_login import current_user from models import ParseLog from models import db from utils.security import get_client_ip from utils.limiter import RateLimiter from utils.queue import ParseQueue, ConcurrencyController from parsers.factory import ParserFactory import uuid import time from datetime import datetime parser_bp = Blueprint('parser', __name__) @parser_bp.route('/parse', methods=['POST']) def parse_video(): """解析视频""" data = request.get_json() video_url = data.get('url') if not video_url: return jsonify({'success': False, 'message': '请提供视频链接'}), 400 # 获取用户信息 user_id = current_user.id if current_user.is_authenticated else None ip_address = get_client_ip(request) # 检查限流 limit_check = RateLimiter.check_limit(user_id=user_id, ip_address=ip_address) if not limit_check['allowed']: return jsonify({ 'success': False, 'message': f"今日解析次数已达上限({limit_check['limit']}次)", 'limit_info': limit_check }), 429 # 检测平台 try: platform = ParserFactory.detect_platform(video_url) except ValueError as e: return jsonify({'success': False, 'message': str(e)}), 400 # 生成任务ID task_id = str(uuid.uuid4()) # 添加到队列 ParseQueue.add_task(task_id, video_url, user_id, ip_address) # 尝试立即处理 if ConcurrencyController.can_process(): result = _process_task(task_id, video_url, platform, user_id, ip_address) return jsonify(result) else: # 返回任务ID,让前端轮询 return jsonify({ 'success': True, 'status': 'queued', 'task_id': task_id, 'message': '任务已加入队列,请稍候...', 'queue_status': ParseQueue.get_queue_status() }) @parser_bp.route('/task/', methods=['GET']) def get_task_result(task_id): """获取任务结果""" result = ParseQueue.get_result(task_id) if result: return jsonify(result) else: # 检查是否还在队列中 queue_status = ParseQueue.get_queue_status() return jsonify({ 'success': False, 'status': 'processing', 'message': '任务处理中...', 'queue_status': queue_status }) @parser_bp.route('/queue-status', methods=['GET']) def queue_status(): """获取队列状态""" status = ParseQueue.get_queue_status() return jsonify({ 'success': True, 'queue_status': status }) def _process_task(task_id, video_url, platform, user_id, ip_address): """处理解析任务""" start_time = time.time() # 获取该平台所有可用的API from models import ParserAPI available_apis = ParserAPI.query.filter_by( platform=platform.lower(), is_enabled=True ).all() if not available_apis: return { 'success': False, 'status': 'failed', 'message': f'没有可用的{platform}解析接口', 'response_time': int((time.time() - start_time) * 1000) } last_error = None # 尝试所有可用的API for api_config in available_apis: try: # 创建解析器 parser = ParserFactory.create_parser(api_config) # 执行解析 result = parser.parse(video_url) # 计算响应时间 response_time = int((time.time() - start_time) * 1000) # 记录日志 log = ParseLog( user_id=user_id, ip_address=ip_address, platform=platform, video_url=video_url, parser_api_id=api_config.id, status='success', response_time=response_time ) db.session.add(log) # 更新API统计 api_config.total_calls += 1 api_config.success_calls += 1 api_config.avg_response_time = int( (api_config.avg_response_time * (api_config.total_calls - 1) + response_time) / api_config.total_calls ) api_config.fail_count = 0 # 重置失败计数 # 更新用户统计 if user_id: from models import User user = User.query.get(user_id) user.total_parse_count += 1 # 更新限流计数 RateLimiter.increment_count(user_id=user_id, ip_address=ip_address, success=True) db.session.commit() # 保存结果 response = { 'success': True, 'status': 'completed', 'data': result, 'response_time': response_time } ParseQueue.complete_task(task_id, response) return response except Exception as e: # 记录失败,继续尝试下一个API last_error = str(e) api_config.total_calls += 1 api_config.fail_count += 1 db.session.commit() continue # 所有API都失败了 # 计算响应时间 response_time = int((time.time() - start_time) * 1000) # 记录失败日志 log = ParseLog( user_id=user_id, ip_address=ip_address, platform=platform, video_url=video_url, status='failed', error_message=last_error or '所有接口都失败', response_time=response_time ) db.session.add(log) # 更新限流计数(失败也计数) RateLimiter.increment_count(user_id=user_id, ip_address=ip_address, success=False) db.session.commit() # 保存错误结果 response = { 'success': False, 'status': 'failed', 'message': last_error or '所有解析接口都失败', 'response_time': response_time } ParseQueue.complete_task(task_id, response) return response