Ohm comme client de téléchargement Sonarr (API compatible qBittorrent)

- /api/v2/* : login SID (mot de passe = clé Torznab), app/version,
  torrents/info (progression temps réel), properties (content_path),
  add (rejoue le grab encodé dans le .torrent de service, dédupliqué
  par infohash SHA-1), delete (± fichiers), pause/resume
- Les grabs Sonarr sont marqués « sonarr:<hash>| » dans source_key →
  suivis de bout en bout : Sonarr importe, renomme et range les épisodes
  dans sa bibliothèque, puis retire le torrent de la file Ohm
- L'indexeur Torznab embarque les paramètres du grab dans l'announce
- README : nouveau mode « client de téléchargement » recommandé (Remote
  Path Mapping documenté), blackhole en variante minimale
- 3 nouveaux tests (flux complet add → suivi → import → delete)
This commit is contained in:
Roman
2026-09-22 16:42:12 +00:00
parent 1f326d34dd
commit 4e1faf4cd9
9 changed files with 536 additions and 33 deletions
+8 -6
View File
@@ -138,10 +138,12 @@ async def test_torznab_search_requires_query(client, apikey):
class _FakeManager:
def __init__(self) -> None:
self.calls: list[tuple[str, str, str]] = []
self.calls: list[tuple[str, str | None]] = []
async def enqueue(self, video_url: str, page_url: str, title: str) -> dict:
self.calls.append((video_url, page_url, title))
async def enqueue(
self, video_url: str, page_url: str, title: str, source_key: str | None = None
) -> dict:
self.calls.append((video_url, source_key))
return {"id": 7, "title": title, "status": "pending"}
@@ -172,9 +174,9 @@ async def test_torznab_download_enqueues_and_returns_torrent(client, apikey, fak
assert r.status_code == 200
assert r.headers["content-type"].startswith("application/x-bittorrent")
assert r.content.startswith(b"d") and b"OhmStreaming" in r.content # bencode valide
assert manager.calls == [
("https://cdn.example/video.mp4", "https://fake.example/ep2", "Frieren S01E02")
]
video_url, source_key = manager.calls[0]
assert video_url == "https://cdn.example/video.mp4"
assert source_key and source_key.startswith("sonarr:") and video_url in source_key
async def test_torznab_download_rejects_bad_key(client):