Fix download cleanup after wishlist runs
- ignore unconfigured backends when clearing completed downloads - keep the post-download cleanup route best-effort after a successful wishlist run - add regression coverage for the orchestrator clear step
This commit is contained in:
parent
a7c1bb96a1
commit
f75c180cb6
3 changed files with 81 additions and 6 deletions
|
|
@ -457,12 +457,25 @@ class DownloadOrchestrator:
|
||||||
True if successful
|
True if successful
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
for client in [self.soulseek, self.youtube, self.tidal, self.qobuz, self.hifi, self.deezer_dl, self.lidarr]:
|
for name, client in [
|
||||||
if client:
|
("soulseek", self.soulseek),
|
||||||
try:
|
("youtube", self.youtube),
|
||||||
results.append(await client.clear_all_completed_downloads())
|
("tidal", self.tidal),
|
||||||
except Exception:
|
("qobuz", self.qobuz),
|
||||||
pass
|
("hifi", self.hifi),
|
||||||
|
("deezer_dl", self.deezer_dl),
|
||||||
|
("lidarr", self.lidarr),
|
||||||
|
]:
|
||||||
|
if not client:
|
||||||
|
continue
|
||||||
|
if hasattr(client, "is_configured") and not client.is_configured():
|
||||||
|
logger.debug("Skipping %s clear_all_completed_downloads (not configured)", name)
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
results.append(await client.clear_all_completed_downloads())
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("%s clear_all_completed_downloads failed: %s", name, exc)
|
||||||
|
results.append(False)
|
||||||
|
|
||||||
return all(results) if results else True
|
return all(results) if results else True
|
||||||
|
|
||||||
|
|
|
||||||
61
tests/downloads/test_download_orchestrator.py
Normal file
61
tests/downloads/test_download_orchestrator.py
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
from core.download_orchestrator import DownloadOrchestrator
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeClient:
|
||||||
|
def __init__(self, configured=True, clear_result=True):
|
||||||
|
self.configured = configured
|
||||||
|
self.clear_result = clear_result
|
||||||
|
self.clear_calls = 0
|
||||||
|
|
||||||
|
def is_configured(self):
|
||||||
|
return self.configured
|
||||||
|
|
||||||
|
async def clear_all_completed_downloads(self):
|
||||||
|
self.clear_calls += 1
|
||||||
|
return self.clear_result
|
||||||
|
|
||||||
|
|
||||||
|
def _build_orchestrator(**clients):
|
||||||
|
orch = DownloadOrchestrator.__new__(DownloadOrchestrator)
|
||||||
|
orch.soulseek = clients.get("soulseek")
|
||||||
|
orch.youtube = clients.get("youtube")
|
||||||
|
orch.tidal = clients.get("tidal")
|
||||||
|
orch.qobuz = clients.get("qobuz")
|
||||||
|
orch.hifi = clients.get("hifi")
|
||||||
|
orch.deezer_dl = clients.get("deezer_dl")
|
||||||
|
orch.lidarr = clients.get("lidarr")
|
||||||
|
return orch
|
||||||
|
|
||||||
|
|
||||||
|
def _run_async(coro):
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
try:
|
||||||
|
return loop.run_until_complete(coro)
|
||||||
|
finally:
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_all_completed_downloads_ignores_unconfigured_clients():
|
||||||
|
orch = _build_orchestrator(
|
||||||
|
soulseek=_FakeClient(configured=True, clear_result=True),
|
||||||
|
youtube=_FakeClient(configured=False, clear_result=False),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = _run_async(orch.clear_all_completed_downloads())
|
||||||
|
|
||||||
|
assert result is True
|
||||||
|
assert orch.soulseek.clear_calls == 1
|
||||||
|
assert orch.youtube.clear_calls == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_all_completed_downloads_propagates_configured_failures():
|
||||||
|
orch = _build_orchestrator(
|
||||||
|
soulseek=_FakeClient(configured=True, clear_result=False),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = _run_async(orch.clear_all_completed_downloads())
|
||||||
|
|
||||||
|
assert result is False
|
||||||
|
assert orch.soulseek.clear_calls == 1
|
||||||
|
|
@ -20583,6 +20583,7 @@ def _build_master_deps():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _run_full_missing_tracks_process(batch_id, playlist_id, tracks_json):
|
def _run_full_missing_tracks_process(batch_id, playlist_id, tracks_json):
|
||||||
return _downloads_master.run_full_missing_tracks_process(
|
return _downloads_master.run_full_missing_tracks_process(
|
||||||
batch_id, playlist_id, tracks_json, _build_master_deps()
|
batch_id, playlist_id, tracks_json, _build_master_deps()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue