210 lines
6.2 KiB
Python
210 lines
6.2 KiB
Python
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
|
||
|
||
# 展开短链接
|
||
video_url = ParserFactory.expand_short_url(video_url)
|
||
|
||
# 生成任务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)
|
||
|
||
# 验证解析结果,video_url 不能为空
|
||
if not result.get('video_url'):
|
||
raise Exception('未能获取到视频链接')
|
||
|
||
# 计算响应时间
|
||
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
|