Fix Usenet settings reload without restart

Refresh registry-backed download plugins when settings are saved so cached Prowlarr clients pick up new indexer credentials immediately. This preserves active download state by reloading existing plugin instances instead of rebuilding the registry.

Add regression coverage for orchestrator reload fanout and the Usenet plugin's cached ProwlarrClient refresh path.
pull/679/head
Broque Thomas 3 days ago
parent 048e4e85d5
commit a41eccbe3c

@ -113,6 +113,22 @@ class DownloadOrchestrator:
if hasattr(amazon, '_client') and amazon._client:
amazon._client.preferred_codec = quality
# Let registry-backed plugins refresh any config they cache at
# construction time. This covers Prowlarr-backed torrent / usenet
# clients without rebuilding the registry and losing active downloads.
for name, client in self.registry.all_plugins():
if not hasattr(client, 'reload_settings'):
continue
try:
client.reload_settings()
logger.info("%s client settings reloaded", self.registry.display_name(name))
except Exception as exc:
logger.warning(
"%s client settings reload failed: %s",
self.registry.display_name(name),
exc,
)
# Reload download path for all clients that cache it.
# Soulseek owns the path config and is reloaded above; every
# other source mirrors that path so files all land in one

@ -198,6 +198,37 @@ def test_reload_instances_with_no_args_reloads_every_source():
assert b.reload_called is True
def test_reload_settings_refreshes_registry_plugins(monkeypatch):
"""Settings saves should refresh plugins that cache config at init.
Prowlarr-backed torrent / usenet clients keep a ProwlarrClient
instance, so without this hook newly-saved indexer settings only
took effect after process restart.
"""
class _ReloadSettingsClient(_FakeClient):
def __init__(self):
super().__init__()
self.reload_calls = 0
def reload_settings(self):
self.reload_calls += 1
torrent = _ReloadSettingsClient()
usenet = _ReloadSettingsClient()
orch = _build_orchestrator(torrent=torrent, usenet=usenet)
monkeypatch.setattr(
'core.download_orchestrator.config_manager.get',
lambda _key, default=None: default,
)
orch.reload_settings()
assert torrent.reload_calls == 1
assert usenet.reload_calls == 1
# ---------------------------------------------------------------------------
# Singleton factory (matches Cin's get_metadata_engine pattern)
# ---------------------------------------------------------------------------

@ -381,6 +381,34 @@ def test_usenet_is_configured_requires_both_sides() -> None:
# ---------------------------------------------------------------------------
def test_usenet_reload_settings_refreshes_cached_prowlarr_config(monkeypatch) -> None:
"""Settings saves must update the plugin's held ProwlarrClient.
The active usenet adapter is rebuilt from config on each call, but
ProwlarrClient is cached inside the plugin. This is the path that
used to require a process restart after entering Prowlarr settings.
"""
settings = {
'prowlarr.url': '',
'prowlarr.api_key': '',
}
monkeypatch.setattr(
'core.prowlarr_client.config_manager.get',
lambda key, default=None: settings.get(key, default),
)
plugin = UsenetDownloadPlugin()
assert plugin._prowlarr.is_configured() is False
settings.update({
'prowlarr.url': 'http://prowlarr:9696',
'prowlarr.api_key': 'secret',
})
plugin.reload_settings()
assert plugin._prowlarr.is_configured() is True
def test_plugins_conform_to_protocol() -> None:
from core.download_plugins.base import DownloadSourcePlugin
assert isinstance(TorrentDownloadPlugin(), DownloadSourcePlugin)

Loading…
Cancel
Save