fix(downloader): 添加种子失败提示改用种子名称

qBittorrent、Transmission、rTorrent 添加任务失败,或添加后查不到任务时,
提示文本直接拼接 content。经过 _get_torrent_info 之后,种子文件的 content
已经是 bytes,于是日志、消息通知和下载失败记录里都会出现整段二进制 repr
(种子较大时有数万字符),失败原因无法阅读。

改为种子文件使用种子名称,磁力链接保留原文。提示前缀不变,现有多语言模板
仍能匹配。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
LIN student
2026-10-04 19:01:22 +08:00
parent c8869d4b89
commit bdef005588
4 changed files with 90 additions and 5 deletions

View File

@@ -120,6 +120,9 @@ class QbittorrentModule(_DownloaderModuleBase[Qbittorrent]):
if not torrent_from_file and not is_magnet:
return None, None, None, f"添加种子任务失败:无法读取种子文件"
# 失败提示使用种子名称(磁力链接保留原文),不拼接种子文件的二进制内容
torrent_desc = torrent_from_file.name if torrent_from_file else content
# 获取下载器
server: Qbittorrent = self.get_instance(downloader)
if not server:
@@ -176,7 +179,7 @@ class QbittorrentModule(_DownloaderModuleBase[Qbittorrent]):
finally:
torrents.clear()
del torrents
return None, None, None, f"添加种子任务失败:{content}"
return None, None, None, f"添加种子任务失败:{torrent_desc}"
else:
# 获取种子Hash
torrent_hash = next(iter(added_torrent_ids), None)
@@ -185,7 +188,7 @@ class QbittorrentModule(_DownloaderModuleBase[Qbittorrent]):
if torrent_hash:
temporary_tag_cleaned = server.delete_torrents_tag(torrent_hash, tag)
if not torrent_hash:
return None, None, None, f"下载任务添加成功,但获取Qbittorrent任务信息失败:{content}"
return None, None, None, f"下载任务添加成功,但获取Qbittorrent任务信息失败:{torrent_desc}"
else:
# 获取种子内容布局: `Original: 原始, Subfolder: 创建子文件夹, NoSubfolder: 不创建子文件夹`
torrent_layout = server.get_content_layout()

View File

@@ -102,6 +102,9 @@ class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
if not torrent_from_file and not is_magnet:
return None, None, None, f"添加种子任务失败:无法读取种子文件"
# 失败提示使用种子名称(磁力链接保留原文),不拼接种子文件的二进制内容
torrent_desc = torrent_from_file.name if torrent_from_file else content
# 获取下载器
server: Rtorrent = self.get_instance(downloader)
if not server:
@@ -176,7 +179,7 @@ class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
finally:
torrents.clear()
del torrents
return None, None, None, f"添加种子任务失败:{content}"
return None, None, None, f"添加种子任务失败:{torrent_desc}"
else:
# 获取种子Hash
torrent_hash = server.get_torrent_id_by_tag(tags=tag)
@@ -185,7 +188,7 @@ class RtorrentModule(_DownloaderModuleBase[Rtorrent]):
None,
None,
None,
f"下载任务添加成功,但获取rTorrent任务信息失败:{content}",
f"下载任务添加成功,但获取rTorrent任务信息失败:{torrent_desc}",
)
else:
if is_paused:

View File

@@ -99,6 +99,9 @@ class TransmissionModule(_DownloaderModuleBase[Transmission]):
if not torrent_from_file and not is_magnet:
return None, None, None, f"添加种子任务失败:无法读取种子文件"
# 失败提示使用种子名称(磁力链接保留原文),不拼接种子文件的二进制内容
torrent_desc = torrent_from_file.name if torrent_from_file else content
# 获取下载器
server: Transmission = self.get_instance(downloader)
if not server:
@@ -153,7 +156,7 @@ class TransmissionModule(_DownloaderModuleBase[Transmission]):
finally:
torrents.clear()
del torrents
return None, None, None, f"添加种子任务失败:{content}"
return None, None, None, f"添加种子任务失败:{torrent_desc}"
else:
torrent_hash = added_torrent.hashString
if is_paused:

View File

@@ -0,0 +1,76 @@
"""下载器添加任务失败时的提示文本。"""
from pathlib import Path
from unittest.mock import MagicMock
import pytest
from app.modules.qbittorrent import QbittorrentModule
from app.modules.rtorrent import RtorrentModule
from app.modules.transmission import TransmissionModule
_ANNOUNCE = b"https://tracker.invalid/announce?passkey=0123456789abcdef"
_PIECES = bytes(range(20))
_TORRENT = (
b"d8:announce" + str(len(_ANNOUNCE)).encode() + b":" + _ANNOUNCE
+ b"4:infod6:lengthi1024e4:name8:test.mkv12:piece lengthi16384e"
+ b"6:pieces" + str(len(_PIECES)).encode() + b":" + _PIECES + b"ee"
)
_MAGNET = "magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567"
def _build_module(module_cls, server):
"""构造只包含下载所需依赖的下载器模块。"""
module = module_cls.__new__(module_cls)
module.get_instance = MagicMock(return_value=server)
module.normalize_path = MagicMock(side_effect=lambda path, _downloader: str(path))
module.get_default_config_name = MagicMock(return_value="default")
return module
def _rejecting_server(module_cls):
"""下载器拒绝添加,且没有同名同大小的已有任务。"""
server = MagicMock()
if module_cls is QbittorrentModule:
server.add_torrent.return_value = (False, [])
elif module_cls is TransmissionModule:
server.add_torrent.return_value = None
else:
server.add_torrent.return_value = False
server.get_torrents.return_value = ([], False)
return server
@pytest.mark.parametrize("module_cls", [QbittorrentModule, TransmissionModule, RtorrentModule])
@pytest.mark.parametrize(
("content", "expected"),
[(_TORRENT, "添加种子任务失败:test.mkv"), (_MAGNET, f"添加种子任务失败:{_MAGNET}")],
ids=["torrent", "magnet"],
)
def test_rejected_download_reports_torrent_name_or_magnet(module_cls, content, expected):
"""种子文件被拒时提示种子名称,磁力链接保留原文,不拼接种子二进制内容。"""
module = _build_module(module_cls, _rejecting_server(module_cls))
result = module.download(content=content, download_dir=Path("/downloads"), cookie="")
assert result == (None, None, None, expected)
@pytest.mark.parametrize(
("module_cls", "expected"),
[
(QbittorrentModule, "下载任务添加成功,但获取Qbittorrent任务信息失败:test.mkv"),
(RtorrentModule, "下载任务添加成功,但获取rTorrent任务信息失败:test.mkv"),
],
ids=["qbittorrent", "rtorrent"],
)
def test_added_download_without_hash_reports_torrent_name(module_cls, expected):
"""添加成功但查不到任务时,提示同样使用种子名称。"""
server = MagicMock()
server.add_torrent.return_value = (True, []) if module_cls is QbittorrentModule else True
server.get_torrent_id_by_tag.return_value = None
module = _build_module(module_cls, server)
result = module.download(content=_TORRENT, download_dir=Path("/downloads"), cookie="")
assert result == (None, None, None, expected)