From ce89154952eb3294aee761cb71163cec7a89e70a Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:22:48 -0700 Subject: [PATCH] Fix hybrid source toggle/reorder not saving and skip unconfigured sources - Add debouncedAutoSaveSettings() to moveHybridSource and toggleHybridSource - Skip unconfigured sources at search time with is_configured() check - Add get_source_status() to orchestrator, include in settings API response - Auto-disable unconfigured sources in UI on settings load --- core/download_orchestrator.py | 20 ++++++++++++++++++-- web_server.py | 8 +++++++- webui/static/script.js | 17 +++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/core/download_orchestrator.py b/core/download_orchestrator.py index e205d891..65073bb8 100644 --- a/core/download_orchestrator.py +++ b/core/download_orchestrator.py @@ -91,6 +91,17 @@ class DownloadOrchestrator: return False + def get_source_status(self) -> dict: + """Return configured status for each download source.""" + clients = { + 'soulseek': self.soulseek, + 'youtube': self.youtube, + 'tidal': self.tidal, + 'qobuz': self.qobuz, + 'hifi': self.hifi, + } + return {name: client.is_configured() for name, client in clients.items()} + async def check_connection(self) -> bool: """ Test if download sources are accessible. @@ -176,15 +187,20 @@ class DownloadOrchestrator: logger.info(f"🔍 Hybrid search ({' → '.join(source_order)}): {query}") - # Try each source in priority order + # Try each source in priority order (skip unconfigured ones) for i, source_name in enumerate(source_order): + client = clients[source_name] + if hasattr(client, 'is_configured') and not client.is_configured(): + logger.info(f"⏭️ Skipping {source_name} (not configured)") + continue + try: if i == 0: logger.info(f"🔍 Trying {source_name} (priority {i+1}): {query}") else: logger.info(f"🔄 Trying {source_name} (priority {i+1}): {query}") - tracks, albums = await clients[source_name].search(query, timeout, progress_callback) + tracks, albums = await client.search(query, timeout, progress_callback) if tracks: logger.info(f"✅ {source_name} found {len(tracks)} tracks") return (tracks, albums) diff --git a/web_server.py b/web_server.py index f4f6316e..4bcf135e 100644 --- a/web_server.py +++ b/web_server.py @@ -4530,7 +4530,13 @@ def handle_settings(): return jsonify({"success": False, "error": str(e)}), 500 else: # GET request try: - return jsonify(config_manager.config_data) + data = dict(config_manager.config_data) + # Include which download sources are configured so the UI can auto-disable unconfigured ones + try: + data['_source_status'] = download_orchestrator.get_source_status() + except Exception: + pass + return jsonify(data) except Exception as e: return jsonify({"error": str(e)}), 500 diff --git a/webui/static/script.js b/webui/static/script.js index c26914bc..c981c7a1 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -5319,6 +5319,7 @@ function moveHybridSource(srcId, direction) { _hybridSourceOrder = _hybridVisualOrder.filter(id => _hybridSourceEnabled[id] !== false); buildHybridSourceList(); updateDownloadSourceUI(); + debouncedAutoSaveSettings(); } function toggleHybridSource(srcId, enabled) { @@ -5329,6 +5330,7 @@ function toggleHybridSource(srcId, enabled) { } buildHybridSourceList(); updateDownloadSourceUI(); + debouncedAutoSaveSettings(); } function _syncHybridOrderFromDOM() { @@ -5359,6 +5361,8 @@ function getHybridOrder() { function loadHybridSourceOrder(settings) { const order = settings.download_source?.hybrid_order; + const sourceStatus = settings._source_status || {}; + if (order && Array.isArray(order) && order.length > 0) { _hybridSourceOrder = order; _hybridSourceEnabled = {}; @@ -5375,6 +5379,19 @@ function loadHybridSourceOrder(settings) { _hybridSourceEnabled[src.id] = src.id === primary || src.id === secondary; } } + + // Auto-disable sources that aren't configured on the server + let changed = false; + for (const src of HYBRID_SOURCES) { + if (_hybridSourceEnabled[src.id] && sourceStatus[src.id] === false) { + _hybridSourceEnabled[src.id] = false; + changed = true; + } + } + if (changed) { + _hybridSourceOrder = _hybridSourceOrder.filter(id => _hybridSourceEnabled[id] !== false); + } + _hybridVisualOrder = null; // Reset so buildHybridSourceList rebuilds it buildHybridSourceList(); }