fix: support v2 and hybrid qBittorrent identities

This commit is contained in:
2026-07-23 11:48:08 +00:00
parent 69709c778b
commit 6ca94a546e
10 changed files with 240 additions and 74 deletions
+108 -1
View File
@@ -76,7 +76,114 @@ class QBittorrentReaderTests(unittest.TestCase):
resource = QBittorrentReader(config).get_resource(torrent_hash)
self.assertIsNotNone(resource)
self.assertEqual(resource.summary.resource_id.info_hash_v1_hex, torrent_hash)
self.assertIn("hashes=", opener.calls[1])
lookup_url = getattr(
opener.calls[1], "full_url", opener.calls[1]
)
self.assertTrue(lookup_url.endswith("/api/v2/torrents/info"))
def test_full_v2_lookup_accepts_qbittorrent_truncated_hash(self):
info = {
b"file tree": {
b"a.txt": {
b"": {
b"length": 3,
b"pieces root": hashlib.sha256(b"abc").digest(),
}
}
},
b"meta version": 2,
b"name": b"a.txt",
b"piece length": 16384,
}
torrent_bytes = encode({b"info": info})
full_hash = hashlib.sha256(encode(info)).hexdigest()
qb_hash = full_hash[:40]
responses = [
b"Ok.",
json.dumps([{
"hash": qb_hash, "name": "a.txt", "state": "uploading",
}]).encode(),
b'[{"index":0,"name":"a.txt","size":3,"progress":1,"priority":1}]',
torrent_bytes,
]
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
password = root / "password"
password.write_text("secret", encoding="utf-8")
os.chmod(password, 0o600)
config = ServiceConfig(
"http://qb", PurePosixPath("/downloads"), root,
username="admin", password_file=password,
)
opener = _Opener(responses)
with patch(
"archive_clients.qbittorrent.request.build_opener",
return_value=opener,
):
resource = QBittorrentReader(config).get_resource(full_hash)
self.assertIsNotNone(resource)
self.assertEqual(resource.summary.qb_torrent_id, qb_hash)
self.assertEqual(
resource.summary.resource_id.info_hash_v2_hex, full_hash
)
self.assertNotIn("hashes=", opener.calls[1])
def test_hybrid_lookup_accepts_v1_alias_of_v2_primary_hash(self):
info = {
b"file tree": {
b"a.txt": {
b"": {
b"length": 3,
b"pieces root": hashlib.sha256(b"abc").digest(),
}
}
},
b"length": 3,
b"meta version": 2,
b"name": b"a.txt",
b"piece length": 16384,
b"pieces": hashlib.sha1(b"abc").digest(),
}
torrent_bytes = encode({b"info": info})
encoded_info = encode(info)
v1_hash = hashlib.sha1(encoded_info).hexdigest()
v2_hash = hashlib.sha256(encoded_info).hexdigest()
qb_hash = v2_hash[:40]
responses = [
b"Ok.",
json.dumps([{
"hash": qb_hash,
"infohash_v1": v1_hash,
"infohash_v2": v2_hash,
"name": "a.txt",
"state": "uploading",
}]).encode(),
b'[{"index":0,"name":"a.txt","size":3,"progress":1,"priority":1}]',
torrent_bytes,
]
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
password = root / "password"
password.write_text("secret", encoding="utf-8")
os.chmod(password, 0o600)
config = ServiceConfig(
"http://qb", PurePosixPath("/downloads"), root,
username="admin", password_file=password,
)
opener = _Opener(responses)
with patch(
"archive_clients.qbittorrent.request.build_opener",
return_value=opener,
):
resource = QBittorrentReader(config).get_resource(v1_hash)
self.assertIsNotNone(resource)
self.assertEqual(resource.summary.qb_torrent_id, qb_hash)
self.assertEqual(
resource.summary.resource_id.info_hash_v1_hex, v1_hash
)
self.assertEqual(
resource.summary.resource_id.info_hash_v2_hex, v2_hash
)
def test_current_empty_204_login_is_accepted(self):
responses = [(b"", 204), b"[]"]