mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-10-05 22:05:50 +01:00
Add torrent post-import category action (#1148)
## Summary - add a **Change Category** torrent completion action and conditionally show its post-import category/label setting - update kept torrents only after a successful library import - support qBittorrent categories, Transmission labels, Deluge's Label plugin, and rTorrent's `custom1` label - preserve existing Keep/Remove behavior and document the new environment setting ## Behavior Category changes happen from `post_process_cleanup`, after output transfer and post-processing complete. This keeps the existing hardlink flow unchanged. An empty post-import category is a no-op, and client API failures are logged without turning a successful library import into a failure. ## Validation - `pytest -n 0 tests/prowlarr/test_qbittorrent_client.py tests/prowlarr/test_transmission_client.py tests/prowlarr/test_deluge_client.py tests/prowlarr/test_rtorrent_client.py tests/prowlarr/test_handler.py tests/config/test_generate_env_docs.py` — 161 passed - `ruff check` on all changed Python files — passed - `basedpyright` on the changed client implementation files — passed - full `basedpyright shelfmark/download/clients` currently reports two pre-existing errors in the new debrid connection-test code at `settings.py:546` and `settings.py:563`, outside this PR's diff - multi-architecture Docker images built successfully for `linux/amd64` and `linux/arm64`
This commit is contained in:
@@ -15,6 +15,17 @@ def make_config_getter(values):
|
||||
return getter
|
||||
|
||||
|
||||
def test_set_category_uses_label_plugin(monkeypatch):
|
||||
from shelfmark.download.clients.deluge import DelugeClient
|
||||
|
||||
client = DelugeClient.__new__(DelugeClient)
|
||||
monkeypatch.setattr(client, "_ensure_connected", MagicMock())
|
||||
monkeypatch.setattr(client, "_try_set_label", MagicMock(return_value=True))
|
||||
|
||||
assert client.set_category("abc123", "imported") is True
|
||||
client._try_set_label.assert_called_once_with("abc123", "imported")
|
||||
|
||||
|
||||
class TestDelugeClientAddDownload:
|
||||
"""Tests for DelugeClient.add_download()."""
|
||||
|
||||
|
||||
@@ -1456,6 +1456,61 @@ class TestProwlarrHandlerFileStaging:
|
||||
|
||||
|
||||
class TestProwlarrHandlerPostProcessCleanup:
|
||||
def test_torrent_change_category_sets_post_import_category(self):
|
||||
handler = ProwlarrHandler()
|
||||
task = DownloadTask(task_id="torrent-category", source="prowlarr", title="Test")
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.name = "qbittorrent"
|
||||
mock_client.set_category.return_value = True
|
||||
handler._cleanup_refs[task.task_id] = (mock_client, "abc123", "torrent")
|
||||
|
||||
config_values = {
|
||||
"PROWLARR_TORRENT_ACTION": "change_category",
|
||||
"PROWLARR_TORRENT_POST_IMPORT_CATEGORY": "imported",
|
||||
}
|
||||
with patch(
|
||||
"shelfmark.download.clients.base_handler.config.get",
|
||||
side_effect=lambda key, default="": config_values.get(key, default),
|
||||
):
|
||||
handler.post_process_cleanup(task, success=True)
|
||||
|
||||
mock_client.set_category.assert_called_once_with("abc123", "imported")
|
||||
mock_client.remove.assert_not_called()
|
||||
|
||||
def test_torrent_keep_does_not_change_category(self):
|
||||
handler = ProwlarrHandler()
|
||||
task = DownloadTask(task_id="torrent-no-category", source="prowlarr", title="Test")
|
||||
|
||||
mock_client = MagicMock()
|
||||
handler._cleanup_refs[task.task_id] = (mock_client, "abc123", "torrent")
|
||||
|
||||
with patch("shelfmark.download.clients.base_handler.config.get", return_value="keep"):
|
||||
handler.post_process_cleanup(task, success=True)
|
||||
|
||||
mock_client.set_category.assert_not_called()
|
||||
mock_client.remove.assert_not_called()
|
||||
|
||||
def test_torrent_change_category_ignores_empty_category(self):
|
||||
handler = ProwlarrHandler()
|
||||
task = DownloadTask(task_id="torrent-empty-category", source="prowlarr", title="Test")
|
||||
|
||||
mock_client = MagicMock()
|
||||
handler._cleanup_refs[task.task_id] = (mock_client, "abc123", "torrent")
|
||||
|
||||
config_values = {
|
||||
"PROWLARR_TORRENT_ACTION": "change_category",
|
||||
"PROWLARR_TORRENT_POST_IMPORT_CATEGORY": "",
|
||||
}
|
||||
with patch(
|
||||
"shelfmark.download.clients.base_handler.config.get",
|
||||
side_effect=lambda key, default="": config_values.get(key, default),
|
||||
):
|
||||
handler.post_process_cleanup(task, success=True)
|
||||
|
||||
mock_client.set_category.assert_not_called()
|
||||
mock_client.remove.assert_not_called()
|
||||
|
||||
def test_usenet_move_triggers_client_cleanup(self):
|
||||
handler = ProwlarrHandler()
|
||||
task = DownloadTask(task_id="cleanup-test", source="prowlarr", title="Test")
|
||||
|
||||
@@ -59,6 +59,20 @@ def create_mock_session_response(torrents, status_code=200):
|
||||
return mock_response
|
||||
|
||||
|
||||
def test_set_category_creates_and_assigns_category():
|
||||
from shelfmark.download.clients.qbittorrent import QBittorrentClient
|
||||
|
||||
client = QBittorrentClient.__new__(QBittorrentClient)
|
||||
client._client = MagicMock()
|
||||
|
||||
assert client.set_category("abc123", "imported") is True
|
||||
client._client.torrents_create_category.assert_called_once_with(name="imported")
|
||||
client._client.torrents_set_category.assert_called_once_with(
|
||||
torrent_hashes="abc123",
|
||||
category="imported",
|
||||
)
|
||||
|
||||
|
||||
class TestQBittorrentClientIsConfigured:
|
||||
"""Tests for QBittorrentClient.is_configured()."""
|
||||
|
||||
|
||||
@@ -27,6 +27,16 @@ def create_mock_xmlrpc_module():
|
||||
return mock_module
|
||||
|
||||
|
||||
def test_set_category_updates_custom1_label():
|
||||
from shelfmark.download.clients.rtorrent import RTorrentClient
|
||||
|
||||
client = RTorrentClient.__new__(RTorrentClient)
|
||||
client._rpc = MagicMock()
|
||||
|
||||
assert client.set_category("abc123", "imported") is True
|
||||
client._rpc.d.custom1.set.assert_called_once_with("abc123", "imported")
|
||||
|
||||
|
||||
class TestRTorrentClientIsConfigured:
|
||||
"""Tests for RTorrentClient.is_configured()."""
|
||||
|
||||
|
||||
@@ -68,6 +68,19 @@ def create_mock_transmission_rpc_module():
|
||||
return mock_module
|
||||
|
||||
|
||||
def test_set_category_replaces_labels():
|
||||
from shelfmark.download.clients.transmission import TransmissionClient
|
||||
|
||||
client = TransmissionClient.__new__(TransmissionClient)
|
||||
client._client = MagicMock()
|
||||
|
||||
assert client.set_category("abc123", "imported") is True
|
||||
client._client.change_torrent.assert_called_once_with(
|
||||
ids="abc123",
|
||||
labels=["imported"],
|
||||
)
|
||||
|
||||
|
||||
class TestTransmissionClientIsConfigured:
|
||||
"""Tests for TransmissionClient.is_configured()."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user