diff --git a/deploy/production/lithium/compose.yaml b/deploy/production/lithium/compose.yaml index 80504ff..42471b9 100644 --- a/deploy/production/lithium/compose.yaml +++ b/deploy/production/lithium/compose.yaml @@ -2,7 +2,7 @@ name: archive-control-archive services: archive-client: - image: sodium/archive-clients:v0.1.1 + image: sodium/archive-clients:v0.1.2 user: "1000:1000" restart: unless-stopped command: ["--config", "/etc/archive-control/client.toml"] diff --git a/deploy/production/x1/compose.yaml b/deploy/production/x1/compose.yaml index e81d3e4..209bbf0 100644 --- a/deploy/production/x1/compose.yaml +++ b/deploy/production/x1/compose.yaml @@ -2,7 +2,7 @@ name: archive-control-cache services: archive-client: - image: sodium/archive-clients:v0.1.1 + image: sodium/archive-clients:v0.1.2 user: "1001:1001" restart: unless-stopped network_mode: host diff --git a/deploy/production/x2/compose.yaml b/deploy/production/x2/compose.yaml index 7a4603c..4c99de7 100644 --- a/deploy/production/x2/compose.yaml +++ b/deploy/production/x2/compose.yaml @@ -2,7 +2,7 @@ name: archive-control-cache services: archive-client: - image: sodium/archive-clients:v0.1.1 + image: sodium/archive-clients:v0.1.2 user: "1001:1001" restart: unless-stopped network_mode: host diff --git a/docs/deployment-and-usage.md b/docs/deployment-and-usage.md index dbf9a26..3e235ef 100644 --- a/docs/deployment-and-usage.md +++ b/docs/deployment-and-usage.md @@ -213,7 +213,7 @@ cache/archive routes according to policy. ```yaml services: archive-client: - image: sodium/archive-clients:v0.1.1 + image: sodium/archive-clients:v0.1.2 user: "1001:1001" restart: unless-stopped command: ["archive-client", "--config", "/etc/archive-control/client.toml"] diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index 614f29f..a74b417 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -105,7 +105,8 @@ follow-up task. ### Actions 1. Implement qBittorrent cookie authentication and capability adapters for - supported 4.4–4.6 and 5.x APIs. + supported 4.5–4.6 and 5.x APIs; reject older releases because they cannot + export exact torrent metainfo through the public API. 2. Implement on-demand torrent summaries, scoped hash lookup, lazy complete content trees, v1/v2 identity validation, torrent export, stopped add, selection application, recheck monitoring, and entry-only deletion. diff --git a/docs/service-apis.md b/docs/service-apis.md index c6b5367..b0d3355 100644 --- a/docs/service-apis.md +++ b/docs/service-apis.md @@ -6,7 +6,7 @@ shapes. ## qBittorrent Web API -The implementation targets supported qBittorrent 4.4–4.6 and 5.x releases and +The implementation targets qBittorrent 4.5–4.6 and 5.x releases and detects the application and Web API versions at startup. The authoritative references are the official [5.0 WebUI API](https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-%28qBittorrent-5.0%29) and [4.1-compatible WebUI API](https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-%28qBittorrent-4.1%29). @@ -16,7 +16,7 @@ and [4.1-compatible WebUI API](https://github.com/qbittorrent/qBittorrent/wiki/W | Need | Web API family | Archive Control rule | | --- | --- | --- | | Authenticate | `auth/login`, cookie session | Log no credentials/cookies; reauthenticate once on expiry | -| Detect compatibility | `app/version`, `app/webapiVersion`, build info | Advertise exact versions and select adapter | +| Detect compatibility | `app/version`, `app/webapiVersion`, build info | Require qBittorrent 4.5.0+ because exact metainfo export is mandatory | | List/lookup | `torrents/info`, properties | On-demand and hash-scoped where possible | | Read file state | `torrents/files` | Normalize indices, paths, selected/skipped, size, progress | | Export metainfo | `torrents/export` | Required before staging; fail if exact metainfo unavailable | diff --git a/docs/testing.md b/docs/testing.md index c47e306..704171c 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -89,8 +89,8 @@ overlapping selections, reordered/duplicated events, and partial journals. ### Adapter contract tests -Pinned qBittorrent and Syncthing container versions cover every supported API -family. Assertions include stopped add, exact selection, export, full recheck, +Pinned qBittorrent 4.5–4.6 and 5.x and Syncthing container versions cover every +supported API family. Assertions include stopped add, exact selection, export, full recheck, download-attempt detection, entry-only delete, folder/device idempotency, events fallback, need/completion proof, and redacted errors. Partfile adapters are tested only against explicitly supported qBittorrent/libtorrent fixtures. diff --git a/pyproject.toml b/pyproject.toml index a349f9d..40cd1f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "archive-clients" -version = "0.1.1" +version = "0.1.2" requires-python = ">=3.11" dependencies = ["protobuf==7.35.1", "websockets==16.0"] diff --git a/src/archive_clients/services.py b/src/archive_clients/services.py index 7f54552..650ea02 100644 --- a/src/archive_clients/services.py +++ b/src/archive_clients/services.py @@ -159,7 +159,9 @@ def _supported_qb_version(version: str) -> bool: if match is None: return False major, minor = int(match.group(1)), int(match.group(2)) - return major == 5 or (major == 4 and minor in {4, 5, 6}) + # torrents/export, required to preserve exact metainfo before staging, + # was introduced in qBittorrent 4.5.0. + return major == 5 or (major == 4 and minor in {5, 6}) def _text_get( diff --git a/tests/test_services.py b/tests/test_services.py index 84a9414..4401239 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -43,11 +43,11 @@ class _Opener: class ServiceProbeTests(unittest.TestCase): - def test_supported_qbittorrent_versions_include_live_4_4_api(self): - for version in ("v4.4.5", "v4.5.5", "v4.6.7", "v5.2.3"): + def test_supported_qbittorrent_versions_require_export_capability(self): + for version in ("v4.5.0", "v4.6.7", "v5.2.3"): with self.subTest(version=version): self.assertTrue(_supported_qb_version(version)) - for version in ("v4.3.9", "v6.0.0", "invalid"): + for version in ("v4.3.9", "v4.4.5", "v6.0.0", "invalid"): with self.subTest(version=version): self.assertFalse(_supported_qb_version(version))