Third commit in the torrent + usenet rollout. SoulSync now also
speaks the two big usenet downloaders through a sibling adapter
contract that mirrors the torrent adapter set. All three layers are
now stood up — Prowlarr finds releases, the torrent adapter and the
usenet adapter each know how to ship work to the underlying client.
A later commit wires Prowlarr search results through the adapters
and through the archive-extract-match pipeline.
- core/usenet_clients/base.py: UsenetClientAdapter Protocol +
UsenetStatus dataclass. Uniform state set covers usenet-specific
phases (queued / downloading / extracting / verifying / repairing /
completed / failed / paused).
- core/usenet_clients/__init__.py: adapter_for_type factory +
get_active_adapter that reads usenet_client.type each call.
- core/usenet_clients/sabnzbd.py: REST adapter. ?apikey=... auth,
mode=addurl and mode=addfile (multipart) for add_nzb. Reads both
the active queue and the recent history so completed / failed
jobs surface in get_all. Parses SAB's HH:MM:SS ``timeleft`` into
seconds.
- core/usenet_clients/nzbget.py: JSON-RPC adapter. HTTP Basic auth,
``append`` method for add_nzb (auto-detects URL vs base64 NZB),
``editqueue`` with GroupPause/GroupResume/GroupDelete/GroupFinalDelete
for state changes. Reads NZBGet's 64-bit split size fields
(FileSizeHi + FileSizeLo) preferentially over the legacy
FileSizeMB aggregate.
- core/connection_test.py: 'usenet_client' branch picks the right
adapter, runs check_connection, surfaces per-client error
messages (different credentials needed).
- config/settings.py: usenet_client.{type, url, api_key, username,
password, category} defaults + both api_key and password marked
encrypted-at-rest.
- web_server.py: 'usenet_client' added to the /api/settings POST
allow-list.
- webui/index.html: new Usenet Client panel on the Indexers &
Downloaders tab. Type picker swaps the credential fields between
API-key (SABnzbd) and username+password (NZBGet).
- webui/static/settings.js: load/save wiring, updateUsenetClientUI
for the credential field swap, testUsenetClientConnection.
- webui/static/helper.js: WHATS_NEW + VERSION_MODAL_SECTIONS entry.
49 lines
1.5 KiB
Python
49 lines
1.5 KiB
Python
"""Usenet client adapters.
|
|
|
|
Each adapter wraps one Usenet downloader (SABnzbd, NZBGet) behind
|
|
the ``UsenetClientAdapter`` Protocol so the rest of SoulSync can
|
|
talk to whichever client the user picked through one uniform
|
|
surface.
|
|
|
|
The active adapter is selected at runtime by the
|
|
``usenet_client.type`` config key. See ``get_active_adapter()``
|
|
for the factory.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from config.settings import config_manager
|
|
|
|
from core.usenet_clients.base import UsenetClientAdapter, UsenetStatus
|
|
from core.usenet_clients.nzbget import NZBGetAdapter
|
|
from core.usenet_clients.sabnzbd import SABnzbdAdapter
|
|
|
|
__all__ = [
|
|
"UsenetClientAdapter",
|
|
"UsenetStatus",
|
|
"SABnzbdAdapter",
|
|
"NZBGetAdapter",
|
|
"get_active_adapter",
|
|
"adapter_for_type",
|
|
]
|
|
|
|
|
|
def adapter_for_type(client_type: str) -> Optional[UsenetClientAdapter]:
|
|
"""Build a fresh adapter instance for the given client type string.
|
|
``None`` for unknown types."""
|
|
if client_type == "sabnzbd":
|
|
return SABnzbdAdapter()
|
|
if client_type == "nzbget":
|
|
return NZBGetAdapter()
|
|
return None
|
|
|
|
|
|
def get_active_adapter() -> Optional[UsenetClientAdapter]:
|
|
"""Return an adapter for whichever usenet client the user has
|
|
selected in Settings. Reads ``usenet_client.type`` each call."""
|
|
client_type = (config_manager.get('usenet_client.type', '') or '').strip().lower()
|
|
if not client_type:
|
|
return None
|
|
return adapter_for_type(client_type)
|