Files
cat-shark 01827224d6 fix: 修复主程序卡死与下载器集成,新增真实数据测试
- LookingWebMain: 强制 IPv4 避免 IPv6 不可达挂起;requests 加超时防卡死
- get_seed_from_element_detail: 修复限流页崩溃与 favicon 误作来源 URL,改用 canonical/传入 URL
- QbittorrentDownloader: 修复 main() 中模块/类误用导致的 download_by_seed AttributeError;请求加超时
- MySqlService: 修复数据库不可达时 query/execute/close 的 None 崩溃,close 后置空 connection
- 新增 cf_session: Cloudflare 人机认证 cookie 管理 + 限流退避重试 + 自动弹浏览器刷新验证
- 新增 refresh_cf_cookies.py: 弹出浏览器手动验证并保存 cf_clearance
- tests/: 真实网络/数据库测试(get_page、seed_is_exist、cf_session)+ run_tests.py runner
- 清理无用脚本(final_test2/verify_replace/REPLACEMENT_INFO),.idea 与 cookie 文件入 .gitignore

注: test_qbittorrent_downloader.py 11 个用例为既有失败(mock 目标 session 为实例属性,与实现错配),未在本次范围
2026-08-30 12:24:26 +08:00

157 lines
5.6 KiB
Python

import requests
from loguru import logger
from dataService.dto.SeedInfo import SeedInfo
# 请求超时: (连接超时, 读取超时),防止 qBittorrent 不可达时程序卡死
REQUEST_TIMEOUT = (5, 15)
class QbittorrentDownloader:
"""使用 qBittorrent Web API 进行下载"""
def __init__(self, host="192.168.123.199", api_port=8085, username="admin", password="GUOxy.5157"):
"""
初始化 qBittorrent 下载器
Args:
host: qBittorrent 主机地址
api_port: qBittorrent Web API 端口
username: 用户名(如果启用用户认证)
password: 密码(如果启用用户认证)
"""
self.host = host
self.api_port = api_port
self.username = username
self.password = password
self.api_base_url = f"http://{host}:{api_port}/api/v2"
# 创建 session 并进行登录
self.session = requests.Session()
if username and password:
self._login()
def _login(self):
"""登录 qBittorrent 并获取 session cookie"""
login_url = f"{self.api_base_url}/auth/login"
try:
response = self.session.post(login_url, data={"username": self.username, "password": self.password}, timeout=REQUEST_TIMEOUT)
if response.status_code == 200:
logger.debug("qBittorrent 登录成功")
else:
logger.warning(f"qBittorrent 登录失败,状态码: {response.status_code}")
except Exception as e:
logger.error(f"qBittorrent 登录异常: {str(e)}")
def download_by_magnet(self, magnet_url, save_path="", category="", tags=""):
"""
通过 magnet 链接添加下载任务
Args:
magnet_url: magnet 链接
save_path: 保存路径
category: 下载分类
tags: 下载标签
Returns:
str: "1"表示成功,"0"表示失败,"-1"表示请求异常
"""
add_url = f"{self.api_base_url}/torrents/add"
# 构造添加下载的参数
form_data = {
'urls': magnet_url
}
# 如果有保存路径、分类或标签,添加到表单数据
if save_path:
form_data['savepath'] = save_path
if category:
form_data['category'] = category
if tags:
form_data['tags'] = tags
logger.debug(f"开始向 qBittorrent 添加下载任务,magnet 链接: {magnet_url}")
try:
response = self.session.post(add_url, data=form_data, timeout=REQUEST_TIMEOUT)
if response.status_code == 200:
logger.info(f"请求成功,magnet 链接 {magnet_url} 已添加至下载队列")
return "1"
else:
logger.warning(
f"请求返回非200状态码,magnet 链接 {magnet_url} 添加失败。状态码: {response.status_code}, 响应: {response.text}"
)
return "-1" # 请求成功,但失败了
except Exception as e:
logger.error(
f"请求过程中出现异常,magnet 链接 {magnet_url} 添加失败。错误信息: {str(e)}"
)
return "0" # 请求过程中发生异常
def download_by_seed(self, seed_info: SeedInfo, save_path="", category="", tags=""):
"""
通过种子信息添加下载任务(主要处理 magnet 链接)
Args:
seed_info: 种子信息对象
save_path: 保存路径
category: 下载分类
tags: 下载标签
Returns:
str: "1"表示成功,"0"表示失败,"-1"表示请求异常
"""
# seedInfo 中的 seedUrl 应该是 magnet 链接
if not seed_info.seedUrl:
logger.error("种子信息中缺少种子链接 (seedUrl)")
return "0"
return self.download_by_magnet(
seed_info.seedUrl,
save_path=save_path,
category=category,
tags=tags
)
def test_connection(self):
"""
测试与 qBittorrent 的连接
Returns:
bool: 连接成功返回 True,否则返回 False
"""
try:
version_url = f"{self.api_base_url}/app/version"
response = self.session.get(version_url, timeout=REQUEST_TIMEOUT)
if response.status_code == 200:
logger.info(f"qBittorrent 连接成功,版本: {response.text}")
return True
else:
logger.error(f"qBittorrent 连接失败,状态码: {response.status_code}")
return False
except Exception as e:
logger.error(f"qBittorrent 连接测试异常: {str(e)}")
return False
def get_downloads(self):
"""
获取当前下载列表
Returns:
list: 下载任务列表
"""
try:
torrents_url = f"{self.api_base_url}/torrents/info"
response = self.session.get(torrents_url, timeout=REQUEST_TIMEOUT)
if response.status_code == 200:
return response.json()
else:
logger.error(f"获取下载列表失败,状态码: {response.status_code}")
return []
except Exception as e:
logger.error(f"获取下载列表异常: {str(e)}")
return []