mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-15 12:40:35 +08:00
feat: add downloader incomplete suffix toggles
This commit is contained in:
@@ -23,6 +23,7 @@ class Qbittorrent:
|
||||
apikey: Optional[str] = None,
|
||||
category: Optional[bool] = False, sequentail: Optional[bool] = False,
|
||||
force_resume: Optional[bool] = False, first_last_piece=False,
|
||||
incomplete_files_ext: Optional[bool] = True,
|
||||
**kwargs):
|
||||
"""
|
||||
若不设置参数,则创建配置文件设置的下载器
|
||||
@@ -42,6 +43,7 @@ class Qbittorrent:
|
||||
self._sequentail = sequentail
|
||||
self._force_resume = force_resume
|
||||
self._first_last_piece = first_last_piece
|
||||
self._incomplete_files_ext = incomplete_files_ext
|
||||
self.qbc = self.__login_qbittorrent()
|
||||
|
||||
@staticmethod
|
||||
@@ -154,18 +156,19 @@ class Qbittorrent:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def __enable_incomplete_file_suffix(qbt: Client) -> None:
|
||||
def __sync_incomplete_file_suffix(qbt: Client, enabled: bool) -> None:
|
||||
"""
|
||||
开启未完成文件后缀,避免监控流程提前整理仍在下载的媒体文件。
|
||||
同步未完成文件后缀开关,避免监控流程提前整理仍在下载的媒体文件。
|
||||
"""
|
||||
try:
|
||||
preferences = qbt.app_preferences() or {}
|
||||
if isinstance(preferences, dict) and preferences.get("incomplete_files_ext") is True:
|
||||
if isinstance(preferences, dict) and preferences.get("incomplete_files_ext") is enabled:
|
||||
return
|
||||
qbt.app_set_preferences({"incomplete_files_ext": True})
|
||||
logger.info("已开启 qbittorrent 未完成文件追加 .!qB 后缀")
|
||||
qbt.app_set_preferences({"incomplete_files_ext": enabled})
|
||||
action = "开启" if enabled else "关闭"
|
||||
logger.info(f"已{action} qbittorrent 未完成文件追加 .!qB 后缀")
|
||||
except Exception as err:
|
||||
logger.warning(f"开启 qbittorrent 未完成文件后缀失败:{str(err)}")
|
||||
logger.warning(f"同步 qbittorrent 未完成文件后缀失败:{str(err)}")
|
||||
|
||||
def is_inactive(self) -> bool:
|
||||
"""
|
||||
@@ -212,7 +215,7 @@ class Qbittorrent:
|
||||
stack_trace = "".join(traceback.format_exception(None, e, e.__traceback__))[:2000]
|
||||
logger.error(f"qbittorrent 登录失败:{str(e)}\n{stack_trace}")
|
||||
return None
|
||||
self.__enable_incomplete_file_suffix(qbt)
|
||||
self.__sync_incomplete_file_suffix(qbt, enabled=bool(self._incomplete_files_ext))
|
||||
return qbt
|
||||
except Exception as err:
|
||||
logger.error(f"qbittorrent 连接出错:{str(err)}")
|
||||
|
||||
@@ -20,7 +20,8 @@ class Transmission:
|
||||
"error", "errorString", "doneDate", "queuePosition", "activityDate", "trackers"]
|
||||
|
||||
def __init__(self, host: Optional[str] = None, port: Optional[int] = None,
|
||||
username: Optional[str] = None, password: Optional[str] = None, **kwargs):
|
||||
username: Optional[str] = None, password: Optional[str] = None,
|
||||
rename_partial_files: Optional[bool] = True, **kwargs):
|
||||
"""
|
||||
若不设置参数,则创建配置文件设置的下载器
|
||||
"""
|
||||
@@ -39,12 +40,13 @@ class Transmission:
|
||||
return
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._rename_partial_files = rename_partial_files
|
||||
self.trc = self.__login_transmission()
|
||||
|
||||
@staticmethod
|
||||
def __enable_incomplete_file_suffix(trt: Client) -> None:
|
||||
def __sync_incomplete_file_suffix(trt: Client, enabled: bool) -> None:
|
||||
"""
|
||||
开启未完成文件后缀,避免监控流程提前整理仍在下载的媒体文件。
|
||||
同步未完成文件后缀开关,避免监控流程提前整理仍在下载的媒体文件。
|
||||
"""
|
||||
try:
|
||||
session = trt.get_session()
|
||||
@@ -53,12 +55,13 @@ class Transmission:
|
||||
rename_partial_files = getter("rename-partial-files")
|
||||
else:
|
||||
rename_partial_files = getattr(session, "rename_partial_files", None)
|
||||
if rename_partial_files is True:
|
||||
if rename_partial_files is enabled:
|
||||
return
|
||||
trt.set_session(rename_partial_files=True)
|
||||
logger.info("已开启 transmission 未完成文件追加 .part 后缀")
|
||||
trt.set_session(rename_partial_files=enabled)
|
||||
action = "开启" if enabled else "关闭"
|
||||
logger.info(f"已{action} transmission 未完成文件追加 .part 后缀")
|
||||
except Exception as err:
|
||||
logger.warning(f"开启 transmission 未完成文件后缀失败:{str(err)}")
|
||||
logger.warning(f"同步 transmission 未完成文件后缀失败:{str(err)}")
|
||||
|
||||
def __login_transmission(self) -> Optional[Client]:
|
||||
"""
|
||||
@@ -76,7 +79,7 @@ class Transmission:
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
timeout=60)
|
||||
self.__enable_incomplete_file_suffix(trt)
|
||||
self.__sync_incomplete_file_suffix(trt, enabled=bool(self._rename_partial_files))
|
||||
return trt
|
||||
except Exception as err:
|
||||
logger.error(f"transmission 连接出错:{str(err)}")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import importlib.util
|
||||
import sys
|
||||
import types
|
||||
import unittest
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
@@ -192,187 +191,208 @@ Qbittorrent = qbittorrent_module.Qbittorrent
|
||||
QbittorrentModule = qbittorrent_package_module.QbittorrentModule
|
||||
|
||||
|
||||
class TestQbittorrentCompat(unittest.TestCase):
|
||||
def test_login_uses_api_key_header_without_auth_login(self):
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_version.return_value = "v5.2.0"
|
||||
def test_login_uses_api_key_header_without_auth_login():
|
||||
"""API Key 登录时应使用 Bearer Header 并跳过用户名密码登录。"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_version.return_value = "v5.2.0"
|
||||
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client) as client_cls:
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, apikey="secret-token")
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client) as client_cls:
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, apikey="secret-token")
|
||||
|
||||
self.assertIs(downloader.qbc, fake_client)
|
||||
fake_client.auth_log_in.assert_not_called()
|
||||
fake_client.app_version.assert_called_once_with()
|
||||
self.assertEqual(
|
||||
client_cls.call_args.kwargs["EXTRA_HEADERS"],
|
||||
{"Authorization": "Bearer secret-token"},
|
||||
assert downloader.qbc is fake_client
|
||||
fake_client.auth_log_in.assert_not_called()
|
||||
fake_client.app_version.assert_called_once_with()
|
||||
assert client_cls.call_args.kwargs["EXTRA_HEADERS"] == {"Authorization": "Bearer secret-token"}
|
||||
|
||||
|
||||
def test_login_enables_incomplete_file_suffix_by_default():
|
||||
"""
|
||||
登录成功后默认开启未完成文件后缀,避免下载中的媒体文件被提前整理。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_preferences.return_value = {"incomplete_files_ext": False}
|
||||
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
assert downloader.qbc is fake_client
|
||||
fake_client.app_set_preferences.assert_called_once_with({"incomplete_files_ext": True})
|
||||
|
||||
|
||||
def test_login_disables_incomplete_file_suffix_when_configured():
|
||||
"""
|
||||
用户关闭配置后应同步关闭 qBittorrent 未完成文件后缀偏好。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_preferences.return_value = {"incomplete_files_ext": True}
|
||||
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client):
|
||||
downloader = Qbittorrent(
|
||||
host="http://127.0.0.1",
|
||||
port=8080,
|
||||
username="admin",
|
||||
password="adminadmin",
|
||||
incomplete_files_ext=False,
|
||||
)
|
||||
|
||||
def test_login_enables_incomplete_file_suffix(self):
|
||||
"""
|
||||
登录成功后应开启未完成文件后缀,避免下载中的媒体文件被提前整理。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_preferences.return_value = {"incomplete_files_ext": False}
|
||||
assert downloader.qbc is fake_client
|
||||
fake_client.app_set_preferences.assert_called_once_with({"incomplete_files_ext": False})
|
||||
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
self.assertIs(downloader.qbc, fake_client)
|
||||
fake_client.app_set_preferences.assert_called_once_with({"incomplete_files_ext": True})
|
||||
def test_login_skips_incomplete_file_suffix_when_already_matches():
|
||||
"""
|
||||
远端未完成文件后缀状态已匹配配置时不重复写入全局偏好。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_preferences.return_value = {"incomplete_files_ext": True}
|
||||
|
||||
def test_login_skips_incomplete_file_suffix_when_already_enabled(self):
|
||||
"""
|
||||
远端已开启未完成文件后缀时不重复写入全局偏好。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_preferences.return_value = {"incomplete_files_ext": True}
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
with patch.object(qbittorrent_module.qbittorrentapi, "Client", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
assert downloader.qbc is fake_client
|
||||
fake_client.app_set_preferences.assert_not_called()
|
||||
|
||||
self.assertIs(downloader.qbc, fake_client)
|
||||
fake_client.app_set_preferences.assert_not_called()
|
||||
|
||||
def test_add_torrent_accepts_structured_success_response(self):
|
||||
fake_client = MagicMock()
|
||||
fake_client.torrents_add.return_value = {
|
||||
"success_count": 1,
|
||||
"failure_count": 0,
|
||||
"pending_count": 0,
|
||||
"added_torrent_ids": ["abc123"],
|
||||
def test_add_torrent_accepts_structured_success_response():
|
||||
"""新版 qBittorrent API 结构化成功响应应返回新增种子 ID。"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.torrents_add.return_value = {
|
||||
"success_count": 1,
|
||||
"failure_count": 0,
|
||||
"pending_count": 0,
|
||||
"added_torrent_ids": ["abc123"],
|
||||
}
|
||||
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
success, added_torrent_ids = downloader.add_torrent(content="https://example.com/test.torrent")
|
||||
assert success
|
||||
assert added_torrent_ids == ["abc123"]
|
||||
|
||||
|
||||
def test_add_torrent_accepts_pending_success_response_without_ids():
|
||||
"""新版 qBittorrent API 待处理成功响应没有 ID 时仍应视为添加成功。"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.torrents_add.return_value = {
|
||||
"success_count": 0,
|
||||
"failure_count": 0,
|
||||
"pending_count": 1,
|
||||
"added_torrent_ids": [],
|
||||
}
|
||||
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
success, added_torrent_ids = downloader.add_torrent(content="https://example.com/test.torrent")
|
||||
assert success
|
||||
assert added_torrent_ids == []
|
||||
|
||||
|
||||
def test_add_torrent_uses_cookie_api_for_qbittorrent_52():
|
||||
"""qBittorrent 5.2 对应 Web API 应通过 Cookie API 同步站点 Cookie。"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_web_api_version.return_value = "2.11.3"
|
||||
fake_client.app_cookies.return_value = [
|
||||
{
|
||||
"domain": "old.example.com",
|
||||
"path": "/",
|
||||
"name": "old",
|
||||
"value": "cookie",
|
||||
}
|
||||
]
|
||||
fake_client.torrents_add.return_value = "Ok."
|
||||
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
success, added_torrent_ids = downloader.add_torrent(content="https://example.com/test.torrent")
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(added_torrent_ids, ["abc123"])
|
||||
|
||||
def test_add_torrent_accepts_pending_success_response_without_ids(self):
|
||||
fake_client = MagicMock()
|
||||
fake_client.torrents_add.return_value = {
|
||||
"success_count": 0,
|
||||
"failure_count": 0,
|
||||
"pending_count": 1,
|
||||
"added_torrent_ids": [],
|
||||
}
|
||||
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
success, added_torrent_ids = downloader.add_torrent(content="https://example.com/test.torrent")
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(added_torrent_ids, [])
|
||||
|
||||
def test_add_torrent_uses_cookie_api_for_qbittorrent_52(self):
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_web_api_version.return_value = "2.11.3"
|
||||
fake_client.app_cookies.return_value = [
|
||||
{
|
||||
"domain": "old.example.com",
|
||||
"path": "/",
|
||||
"name": "old",
|
||||
"value": "cookie",
|
||||
}
|
||||
]
|
||||
fake_client.torrents_add.return_value = "Ok."
|
||||
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
success, added_torrent_ids = downloader.add_torrent(
|
||||
content="https://tracker.example.com/download?id=1",
|
||||
cookie="uid=1; passkey=abc",
|
||||
)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(added_torrent_ids, [])
|
||||
set_cookie_call = fake_client.app_set_cookies.call_args.kwargs["cookies"]
|
||||
self.assertIn(
|
||||
{
|
||||
"domain": "tracker.example.com",
|
||||
"path": "/",
|
||||
"name": "uid",
|
||||
"value": "1",
|
||||
},
|
||||
set_cookie_call,
|
||||
)
|
||||
self.assertIn(
|
||||
{
|
||||
"domain": "tracker.example.com",
|
||||
"path": "/",
|
||||
"name": "passkey",
|
||||
"value": "abc",
|
||||
},
|
||||
set_cookie_call,
|
||||
)
|
||||
self.assertIsNone(fake_client.torrents_add.call_args.kwargs["cookie"])
|
||||
|
||||
def test_add_torrent_keeps_legacy_cookie_param_for_old_webapi(self):
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_web_api_version.return_value = "2.11.2"
|
||||
fake_client.torrents_add.return_value = "Ok."
|
||||
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
success, added_torrent_ids = downloader.add_torrent(
|
||||
content="https://tracker.example.com/download?id=1",
|
||||
cookie="uid=1",
|
||||
)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(added_torrent_ids, [])
|
||||
fake_client.app_set_cookies.assert_not_called()
|
||||
self.assertEqual(fake_client.torrents_add.call_args.kwargs["cookie"], "uid=1")
|
||||
success, added_torrent_ids = downloader.add_torrent(
|
||||
content="https://tracker.example.com/download?id=1",
|
||||
cookie="uid=1; passkey=abc",
|
||||
)
|
||||
assert success
|
||||
assert added_torrent_ids == []
|
||||
set_cookie_call = fake_client.app_set_cookies.call_args.kwargs["cookies"]
|
||||
assert {
|
||||
"domain": "tracker.example.com",
|
||||
"path": "/",
|
||||
"name": "uid",
|
||||
"value": "1",
|
||||
} in set_cookie_call
|
||||
assert {
|
||||
"domain": "tracker.example.com",
|
||||
"path": "/",
|
||||
"name": "passkey",
|
||||
"value": "abc",
|
||||
} in set_cookie_call
|
||||
assert fake_client.torrents_add.call_args.kwargs["cookie"] is None
|
||||
|
||||
|
||||
class TestQbittorrentModuleCompat(unittest.TestCase):
|
||||
@staticmethod
|
||||
def _build_module(server):
|
||||
module = QbittorrentModule.__new__(QbittorrentModule)
|
||||
module.get_instance = MagicMock(return_value=server)
|
||||
module.normalize_path = MagicMock(side_effect=lambda path, _downloader: path)
|
||||
module.get_default_config_name = MagicMock(return_value="default-qb")
|
||||
return module
|
||||
def test_add_torrent_keeps_legacy_cookie_param_for_old_webapi():
|
||||
"""旧版 qBittorrent Web API 不支持 Cookie API 时保留添加种子 Cookie 参数。"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.app_web_api_version.return_value = "2.11.2"
|
||||
fake_client.torrents_add.return_value = "Ok."
|
||||
|
||||
def test_download_prefers_added_torrent_ids_before_tag_lookup(self):
|
||||
fake_server = MagicMock()
|
||||
fake_server.add_torrent.return_value = (True, ["abc123"])
|
||||
fake_server.get_content_layout.return_value = "Original"
|
||||
fake_server.is_force_resume.return_value = False
|
||||
with patch.object(Qbittorrent, "_Qbittorrent__login_qbittorrent", return_value=fake_client):
|
||||
downloader = Qbittorrent(host="http://127.0.0.1", port=8080, username="admin", password="adminadmin")
|
||||
|
||||
module = self._build_module(fake_server)
|
||||
result = module.download(
|
||||
content="magnet:?xt=urn:btih:123",
|
||||
download_dir=Path("/downloads"),
|
||||
cookie="",
|
||||
downloader="qb",
|
||||
)
|
||||
success, added_torrent_ids = downloader.add_torrent(
|
||||
content="https://tracker.example.com/download?id=1",
|
||||
cookie="uid=1",
|
||||
)
|
||||
assert success
|
||||
assert added_torrent_ids == []
|
||||
fake_client.app_set_cookies.assert_not_called()
|
||||
assert fake_client.torrents_add.call_args.kwargs["cookie"] == "uid=1"
|
||||
|
||||
self.assertEqual(result, ("qb", "abc123", "Original", "添加下载成功"))
|
||||
fake_server.delete_torrents_tag.assert_called_once_with("abc123", "tmp-tag-01")
|
||||
fake_server.get_torrent_id_by_tag.assert_not_called()
|
||||
self.assertEqual(
|
||||
fake_server.add_torrent.call_args.kwargs["tag"],
|
||||
["tmp-tag-01", "moviepilot-tag"],
|
||||
)
|
||||
|
||||
def test_download_falls_back_to_tag_lookup_when_added_ids_missing(self):
|
||||
fake_server = MagicMock()
|
||||
fake_server.add_torrent.return_value = (True, [])
|
||||
fake_server.get_content_layout.return_value = "Original"
|
||||
fake_server.get_torrent_id_by_tag.return_value = "def456"
|
||||
fake_server.is_force_resume.return_value = False
|
||||
def _build_module(server):
|
||||
"""构造仅包含下载所需方法的 QbittorrentModule 测试实例。"""
|
||||
module = QbittorrentModule.__new__(QbittorrentModule)
|
||||
module.get_instance = MagicMock(return_value=server)
|
||||
module.normalize_path = MagicMock(side_effect=lambda path, _downloader: path)
|
||||
module.get_default_config_name = MagicMock(return_value="default-qb")
|
||||
return module
|
||||
|
||||
module = self._build_module(fake_server)
|
||||
result = module.download(
|
||||
content="magnet:?xt=urn:btih:456",
|
||||
download_dir=Path("/downloads"),
|
||||
cookie="",
|
||||
downloader="qb",
|
||||
)
|
||||
|
||||
self.assertEqual(result, ("qb", "def456", "Original", "添加下载成功"))
|
||||
fake_server.delete_torrents_tag.assert_not_called()
|
||||
fake_server.get_torrent_id_by_tag.assert_called_once_with(tags="tmp-tag-01")
|
||||
def test_download_prefers_added_torrent_ids_before_tag_lookup():
|
||||
"""添加任务响应包含种子 ID 时应优先使用响应值。"""
|
||||
fake_server = MagicMock()
|
||||
fake_server.add_torrent.return_value = (True, ["abc123"])
|
||||
fake_server.get_content_layout.return_value = "Original"
|
||||
fake_server.is_force_resume.return_value = False
|
||||
|
||||
module = _build_module(fake_server)
|
||||
result = module.download(
|
||||
content="magnet:?xt=urn:btih:123",
|
||||
download_dir=Path("/downloads"),
|
||||
cookie="",
|
||||
downloader="qb",
|
||||
)
|
||||
|
||||
assert result == ("qb", "abc123", "Original", "添加下载成功")
|
||||
fake_server.delete_torrents_tag.assert_called_once_with("abc123", "tmp-tag-01")
|
||||
fake_server.get_torrent_id_by_tag.assert_not_called()
|
||||
assert fake_server.add_torrent.call_args.kwargs["tag"] == ["tmp-tag-01", "moviepilot-tag"]
|
||||
|
||||
|
||||
def test_download_falls_back_to_tag_lookup_when_added_ids_missing():
|
||||
"""添加任务响应缺少种子 ID 时应回退到临时标签查询。"""
|
||||
fake_server = MagicMock()
|
||||
fake_server.add_torrent.return_value = (True, [])
|
||||
fake_server.get_content_layout.return_value = "Original"
|
||||
fake_server.get_torrent_id_by_tag.return_value = "def456"
|
||||
fake_server.is_force_resume.return_value = False
|
||||
|
||||
module = _build_module(fake_server)
|
||||
result = module.download(
|
||||
content="magnet:?xt=urn:btih:456",
|
||||
download_dir=Path("/downloads"),
|
||||
cookie="",
|
||||
downloader="qb",
|
||||
)
|
||||
|
||||
assert result == ("qb", "def456", "Original", "添加下载成功")
|
||||
fake_server.delete_torrents_tag.assert_not_called()
|
||||
fake_server.get_torrent_id_by_tag.assert_called_once_with(tags="tmp-tag-01")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import importlib.util
|
||||
import sys
|
||||
import types
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -93,29 +92,43 @@ transmission_module = _load_transmission_client_module()
|
||||
Transmission = transmission_module.Transmission
|
||||
|
||||
|
||||
class TestTransmissionCompat(unittest.TestCase):
|
||||
def test_login_enables_incomplete_file_suffix(self):
|
||||
"""
|
||||
登录成功后应开启未完成文件后缀,避免下载中的媒体文件被提前整理。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.get_session.return_value = {"rename-partial-files": False}
|
||||
def test_login_enables_incomplete_file_suffix_by_default():
|
||||
"""
|
||||
登录成功后默认开启未完成文件后缀,避免下载中的媒体文件被提前整理。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.get_session.return_value = {"rename-partial-files": False}
|
||||
|
||||
with patch.object(transmission_module.transmission_rpc, "Client", return_value=fake_client):
|
||||
downloader = Transmission(host="127.0.0.1", port=9091)
|
||||
with patch.object(transmission_module.transmission_rpc, "Client", return_value=fake_client):
|
||||
downloader = Transmission(host="127.0.0.1", port=9091)
|
||||
|
||||
self.assertIs(downloader.trc, fake_client)
|
||||
fake_client.set_session.assert_called_once_with(rename_partial_files=True)
|
||||
assert downloader.trc is fake_client
|
||||
fake_client.set_session.assert_called_once_with(rename_partial_files=True)
|
||||
|
||||
def test_login_skips_incomplete_file_suffix_when_already_enabled(self):
|
||||
"""
|
||||
远端已开启未完成文件后缀时不重复写入全局会话配置。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.get_session.return_value = types.SimpleNamespace(rename_partial_files=True)
|
||||
|
||||
with patch.object(transmission_module.transmission_rpc, "Client", return_value=fake_client):
|
||||
downloader = Transmission(host="127.0.0.1", port=9091)
|
||||
def test_login_disables_incomplete_file_suffix_when_configured():
|
||||
"""
|
||||
用户关闭配置后应同步关闭 Transmission 未完成文件后缀偏好。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.get_session.return_value = types.SimpleNamespace(rename_partial_files=True)
|
||||
|
||||
self.assertIs(downloader.trc, fake_client)
|
||||
fake_client.set_session.assert_not_called()
|
||||
with patch.object(transmission_module.transmission_rpc, "Client", return_value=fake_client):
|
||||
downloader = Transmission(host="127.0.0.1", port=9091, rename_partial_files=False)
|
||||
|
||||
assert downloader.trc is fake_client
|
||||
fake_client.set_session.assert_called_once_with(rename_partial_files=False)
|
||||
|
||||
|
||||
def test_login_skips_incomplete_file_suffix_when_already_matches():
|
||||
"""
|
||||
远端未完成文件后缀状态已匹配配置时不重复写入全局会话配置。
|
||||
"""
|
||||
fake_client = MagicMock()
|
||||
fake_client.get_session.return_value = types.SimpleNamespace(rename_partial_files=True)
|
||||
|
||||
with patch.object(transmission_module.transmission_rpc, "Client", return_value=fake_client):
|
||||
downloader = Transmission(host="127.0.0.1", port=9091)
|
||||
|
||||
assert downloader.trc is fake_client
|
||||
fake_client.set_session.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user