From 77d20e9aa8ee29487ef9b9622637288b16e6e1af Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Thu, 23 Apr 2026 18:01:34 -0700 Subject: [PATCH] Fix Clean Search History automation AttributeError on DownloadOrchestrator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hourly `clean_search_history` automation was crashing with `'DownloadOrchestrator' object has no attribute 'base_url'`. The guard was written before the orchestrator refactor — `soulseek_client` is now a DownloadOrchestrator that wraps individual download clients, with the real Soulseek client sitting at `.soulseek`. Two other call sites in web_server.py (lines 2634, 3092) already used the correct `soulseek_client.soulseek.base_url` pattern with a getattr guard. This call site was missed during the refactor. Fix: reach through the orchestrator the same way the other sites do. --- web_server.py | 5 ++++- webui/static/helper.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/web_server.py b/web_server.py index 32ba02dc..aa4ee8ea 100644 --- a/web_server.py +++ b/web_server.py @@ -1631,7 +1631,10 @@ def _register_automation_handlers(): hybrid_order = config_manager.get('download_source.hybrid_order', ['hifi', 'youtube', 'soulseek']) soulseek_active = (dl_mode == 'soulseek' or (dl_mode == 'hybrid' and 'soulseek' in hybrid_order)) - if not soulseek_active or not soulseek_client or not soulseek_client.base_url: + # soulseek_client is a DownloadOrchestrator; the real client lives on + # .soulseek. Match the getattr pattern used at the other call sites. + slskd = getattr(soulseek_client, 'soulseek', None) if soulseek_client else None + if not soulseek_active or not slskd or not slskd.base_url: _update_automation_progress(automation_id, log_line='Soulseek not active — skipped', log_type='skip') return {'status': 'skipped'} diff --git a/webui/static/helper.js b/webui/static/helper.js index 03a6cc7a..de289e17 100644 --- a/webui/static/helper.js +++ b/webui/static/helper.js @@ -3455,6 +3455,7 @@ const WHATS_NEW = { { title: 'Artist Detail Back Button Fallback', desc: 'The back button on the Artists-page inline detail view used to dump users on an empty "Search for an artist..." screen when they arrived from outside the Artists page — a dead end now that Artists isn\'t in the sidebar. If you searched inside the Artists page, back still returns to your results list. Otherwise (arriving from Search, Discover, Watchlist, etc.), back uses the browser history to land you on whichever page you came from. Falls back to the Search page only when there\'s no browser history to go back to (the natural place to find another artist)', page: 'search', unreleased: true }, { title: 'Interactive Help Updated for Unified Search', desc: 'The click-for-help annotations and the "Your First Download" guided tour were rewritten for the new Search page. Stale annotations pointing at removed elements (Basic/Enhanced toggle button, side-panel queues, download-manager controls) are deleted. The first-download tour now runs on /search and opens with the source picker. PAGE_TOUR_MAP accepts both "search" and the legacy "downloads" id so old bookmarks still match a tour. Retired the standalone "Browse Artists" tour', page: 'help', unreleased: true }, { title: 'Unified Source-Picker Controller (Search Page + Global Widget)', desc: 'Internal refactor — the source picker state machine (query, active source, per-query cache, fallbacks, loading state, configured-source discovery) is now a single createSearchController factory in shared-helpers.js. Both the full Search page and the sidebar global search popover consume the same controller with per-surface wiring (DOM elements, Soulseek handoff, unconfigured-source click). About 380 lines of near-duplicated state + fetch + render code consolidated into one implementation, so a bug fix or behavior tweak to the picker lands everywhere at once. Zero UX change — every keystroke, icon click, cache hit, rate-limit fallback, and unconfigured-source redirect behaves identically to before', page: 'search', unreleased: true }, + { title: 'Fix Clean Search History Automation Failing with AttributeError', desc: 'The hourly Clean Search History maintenance automation was crashing with "DownloadOrchestrator object has no attribute base_url". Root cause: the check `soulseek_client.base_url` was written before the orchestrator refactor — `soulseek_client` is now a DownloadOrchestrator that wraps individual download clients, with the real Soulseek client at `.soulseek`. Two other call sites in web_server.py already used the correct `soulseek_client.soulseek.base_url` pattern; this one was missed. Now matches the same getattr-guarded pattern and the hourly cleanup runs again', page: 'stats' }, ], '2.39': [ // --- April 22, 2026 ---