Files
JieXi/routes/parser.py
2025-11-28 21:20:40 +08:00

203 lines
6.0 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
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/<task_id>', 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