Complete Hydrabase as selectable fallback metadata source
- Remove redundant enable checkbox — fallback dropdown is the enable - Hydrabase option only appears in dropdown when connected - Connect/disconnect dynamically adds/removes dropdown option - _is_hydrabase_active checks fallback_source == hydrabase (not config toggle) - Fallback client returns hydrabase_client when selected, iTunes if disconnected - Auto-reconnect respects fallback selection for dev_mode handling - hydrabase added to settings save service list for persistence - Status shows green Connected on page load when auto-connected
This commit is contained in:
parent
214985482d
commit
10361bb837
4 changed files with 73 additions and 24 deletions
|
|
@ -31,6 +31,19 @@ def _create_fallback_client():
|
||||||
if source == 'deezer':
|
if source == 'deezer':
|
||||||
from core.deezer_client import DeezerClient
|
from core.deezer_client import DeezerClient
|
||||||
return DeezerClient()
|
return DeezerClient()
|
||||||
|
if source == 'hydrabase':
|
||||||
|
try:
|
||||||
|
from core.hydrabase_client import HydrabaseClient
|
||||||
|
# Hydrabase client is managed globally — try to import the running instance
|
||||||
|
import importlib
|
||||||
|
ws_module = importlib.import_module('web_server')
|
||||||
|
client = getattr(ws_module, 'hydrabase_client', None)
|
||||||
|
if client and client.is_connected():
|
||||||
|
return client
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Hydrabase not available — fall back to iTunes
|
||||||
|
return iTunesClient()
|
||||||
return iTunesClient()
|
return iTunesClient()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4481,7 +4481,7 @@ def handle_settings():
|
||||||
if 'active_media_server' in new_settings:
|
if 'active_media_server' in new_settings:
|
||||||
config_manager.set_active_media_server(new_settings['active_media_server'])
|
config_manager.set_active_media_server(new_settings['active_media_server'])
|
||||||
|
|
||||||
for service in ['spotify', 'plex', 'jellyfin', 'navidrome', 'soulseek', 'download_source', 'settings', 'database', 'metadata_enhancement', 'file_organization', 'playlist_sync', 'tidal', 'tidal_download', 'qobuz', 'hifi_download', 'listenbrainz', 'acoustid', 'lastfm', 'genius', 'import', 'lossy_copy', 'ui_appearance', 'youtube', 'content_filter', 'itunes', 'm3u_export', 'musicbrainz', 'deezer', 'audiodb', 'metadata']:
|
for service in ['spotify', 'plex', 'jellyfin', 'navidrome', 'soulseek', 'download_source', 'settings', 'database', 'metadata_enhancement', 'file_organization', 'playlist_sync', 'tidal', 'tidal_download', 'qobuz', 'hifi_download', 'listenbrainz', 'acoustid', 'lastfm', 'genius', 'import', 'lossy_copy', 'ui_appearance', 'youtube', 'content_filter', 'itunes', 'm3u_export', 'musicbrainz', 'deezer', 'audiodb', 'metadata', 'hydrabase']:
|
||||||
if service in new_settings:
|
if service in new_settings:
|
||||||
for key, value in new_settings[service].items():
|
for key, value in new_settings[service].items():
|
||||||
config_manager.set(f'{service}.{key}', value)
|
config_manager.set(f'{service}.{key}', value)
|
||||||
|
|
@ -4560,15 +4560,15 @@ _comparison_lock = threading.Lock()
|
||||||
|
|
||||||
def _is_hydrabase_active():
|
def _is_hydrabase_active():
|
||||||
"""Check if Hydrabase should be used as the primary metadata source.
|
"""Check if Hydrabase should be used as the primary metadata source.
|
||||||
Active when: (dev_mode OR hydrabase.enabled config) AND client connected."""
|
Active when: (dev_mode OR selected as fallback source) AND client connected."""
|
||||||
try:
|
try:
|
||||||
if hydrabase_client is None or not hydrabase_client.is_connected():
|
if hydrabase_client is None or not hydrabase_client.is_connected():
|
||||||
return False
|
return False
|
||||||
# Dev mode always enables Hydrabase (legacy behavior)
|
# Dev mode always enables Hydrabase (legacy behavior)
|
||||||
if dev_mode_enabled:
|
if dev_mode_enabled:
|
||||||
return True
|
return True
|
||||||
# Config toggle: user enabled Hydrabase as metadata source
|
# Selected as the fallback metadata source
|
||||||
return config_manager.get('hydrabase.enabled', False)
|
return _get_metadata_fallback_source() == 'hydrabase'
|
||||||
except (NameError, Exception):
|
except (NameError, Exception):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -4650,8 +4650,6 @@ def _run_background_comparison(query, hydrabase_counts=None):
|
||||||
def hydrabase_connect():
|
def hydrabase_connect():
|
||||||
"""Connect to a Hydrabase instance via WebSocket."""
|
"""Connect to a Hydrabase instance via WebSocket."""
|
||||||
global _hydrabase_ws
|
global _hydrabase_ws
|
||||||
if not dev_mode_enabled:
|
|
||||||
return jsonify({"success": False, "error": "Dev mode not active"}), 403
|
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
url = data.get('url', '').strip()
|
url = data.get('url', '').strip()
|
||||||
api_key = data.get('api_key', '').strip()
|
api_key = data.get('api_key', '').strip()
|
||||||
|
|
@ -4694,8 +4692,10 @@ def hydrabase_disconnect():
|
||||||
pass
|
pass
|
||||||
_hydrabase_ws = None
|
_hydrabase_ws = None
|
||||||
config_manager.set('hydrabase.auto_connect', False)
|
config_manager.set('hydrabase.auto_connect', False)
|
||||||
dev_mode_enabled = False
|
# Only disable dev mode if not using Hydrabase as a regular fallback source
|
||||||
print("🧪 [Hydrabase] Disconnected — dev mode disabled")
|
if _get_metadata_fallback_source() != 'hydrabase':
|
||||||
|
dev_mode_enabled = False
|
||||||
|
print("🧪 [Hydrabase] Disconnected")
|
||||||
return jsonify({"success": True})
|
return jsonify({"success": True})
|
||||||
|
|
||||||
@app.route('/api/hydrabase/status')
|
@app.route('/api/hydrabase/status')
|
||||||
|
|
@ -27441,7 +27441,7 @@ def _get_deezer_client():
|
||||||
return _deezer_client_instance
|
return _deezer_client_instance
|
||||||
|
|
||||||
def _get_metadata_fallback_source():
|
def _get_metadata_fallback_source():
|
||||||
"""Get the configured metadata fallback source ('itunes' or 'deezer')."""
|
"""Get the configured metadata fallback source ('itunes', 'deezer', or 'hydrabase')."""
|
||||||
try:
|
try:
|
||||||
return config_manager.get('metadata.fallback_source', 'itunes') or 'itunes'
|
return config_manager.get('metadata.fallback_source', 'itunes') or 'itunes'
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -27449,10 +27449,16 @@ def _get_metadata_fallback_source():
|
||||||
|
|
||||||
def _get_metadata_fallback_client():
|
def _get_metadata_fallback_client():
|
||||||
"""Get the active metadata fallback client based on settings.
|
"""Get the active metadata fallback client based on settings.
|
||||||
Returns an iTunesClient or DeezerClient instance with identical interfaces."""
|
Returns an iTunesClient, DeezerClient, or HydrabaseClient instance with identical interfaces."""
|
||||||
source = _get_metadata_fallback_source()
|
source = _get_metadata_fallback_source()
|
||||||
if source == 'deezer':
|
if source == 'deezer':
|
||||||
return _get_deezer_client()
|
return _get_deezer_client()
|
||||||
|
if source == 'hydrabase':
|
||||||
|
if hydrabase_client and hydrabase_client.is_connected():
|
||||||
|
return hydrabase_client
|
||||||
|
# Hydrabase not connected — fall back to iTunes
|
||||||
|
from core.itunes_client import iTunesClient
|
||||||
|
return iTunesClient()
|
||||||
from core.itunes_client import iTunesClient
|
from core.itunes_client import iTunesClient
|
||||||
return iTunesClient()
|
return iTunesClient()
|
||||||
|
|
||||||
|
|
@ -40772,9 +40778,8 @@ try:
|
||||||
timeout=10
|
timeout=10
|
||||||
)
|
)
|
||||||
_hydrabase_ws = _auto_ws
|
_hydrabase_ws = _auto_ws
|
||||||
# Enable dev mode only if Hydrabase was previously in dev mode
|
# Enable dev mode only if not using Hydrabase as a regular fallback source
|
||||||
# The config toggle (hydrabase.enabled) handles non-dev usage
|
if _get_metadata_fallback_source() != 'hydrabase':
|
||||||
if not _hydra_cfg.get('enabled'):
|
|
||||||
dev_mode_enabled = True
|
dev_mode_enabled = True
|
||||||
print(f"✅ Hydrabase auto-connected to {_hydra_cfg['url']}")
|
print(f"✅ Hydrabase auto-connected to {_hydra_cfg['url']}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -3673,19 +3673,14 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="callback-info">
|
<div class="callback-info">
|
||||||
<div class="callback-help">When Spotify is not connected, this source provides artist, album, and track metadata. Both are free and require no API key.</div>
|
<div class="callback-help">When Spotify is not connected, this source provides artist, album, and track metadata. Hydrabase requires a connection configured above.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Hydrabase P2P Metadata -->
|
<!-- Hydrabase P2P Metadata -->
|
||||||
<div class="api-service-frame" data-stg="connections">
|
<div class="api-service-frame" data-stg="connections">
|
||||||
<h4 class="service-title" style="color: #00b4d8;">Hydrabase</h4>
|
<h4 class="service-title" style="color: #00b4d8;">Hydrabase</h4>
|
||||||
<div class="form-group" style="margin-bottom: 8px;">
|
<input type="hidden" id="hydrabase-enabled" value="false">
|
||||||
<label class="checkbox-label" style="display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 0;">
|
|
||||||
<input type="checkbox" id="hydrabase-enabled" style="width: 16px; height: 16px;">
|
|
||||||
<span>Enable Hydrabase as metadata source</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>WebSocket URL:</label>
|
<label>WebSocket URL:</label>
|
||||||
<input type="text" id="hydrabase-url" placeholder="ws://localhost:4545">
|
<input type="text" id="hydrabase-url" placeholder="ws://localhost:4545">
|
||||||
|
|
|
||||||
|
|
@ -5469,10 +5469,30 @@ async function loadSettingsData() {
|
||||||
|
|
||||||
// Populate Hydrabase settings
|
// Populate Hydrabase settings
|
||||||
const hbConfig = settings.hydrabase || {};
|
const hbConfig = settings.hydrabase || {};
|
||||||
document.getElementById('hydrabase-enabled').checked = hbConfig.enabled || false;
|
|
||||||
document.getElementById('hydrabase-url').value = hbConfig.url || '';
|
document.getElementById('hydrabase-url').value = hbConfig.url || '';
|
||||||
document.getElementById('hydrabase-api-key').value = hbConfig.api_key || '';
|
document.getElementById('hydrabase-api-key').value = hbConfig.api_key || '';
|
||||||
document.getElementById('hydrabase-auto-connect').checked = hbConfig.auto_connect || false;
|
document.getElementById('hydrabase-auto-connect').checked = hbConfig.auto_connect || false;
|
||||||
|
// Check live connection status + add Hydrabase to fallback dropdown if connected
|
||||||
|
fetch('/api/hydrabase/status').then(r => r.json()).then(s => {
|
||||||
|
const btn = document.getElementById('hydrabase-connect-btn');
|
||||||
|
const statusEl = document.getElementById('hydrabase-settings-status');
|
||||||
|
if (s.connected) {
|
||||||
|
if (btn) btn.textContent = 'Disconnect';
|
||||||
|
if (statusEl) { statusEl.textContent = 'Connected'; statusEl.style.color = '#4caf50'; }
|
||||||
|
// Add Hydrabase to fallback source dropdown
|
||||||
|
const fbSelect = document.getElementById('metadata-fallback-source');
|
||||||
|
if (fbSelect && !fbSelect.querySelector('option[value="hydrabase"]')) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = 'hydrabase';
|
||||||
|
opt.textContent = 'Hydrabase (P2P)';
|
||||||
|
fbSelect.appendChild(opt);
|
||||||
|
}
|
||||||
|
// Restore selection if it was hydrabase
|
||||||
|
if ((settings.metadata?.fallback_source) === 'hydrabase') {
|
||||||
|
fbSelect.value = 'hydrabase';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
|
||||||
// Populate Download settings (right column)
|
// Populate Download settings (right column)
|
||||||
document.getElementById('download-path').value = settings.soulseek?.download_path || './downloads';
|
document.getElementById('download-path').value = settings.soulseek?.download_path || './downloads';
|
||||||
|
|
@ -6107,7 +6127,16 @@ async function toggleHydrabaseFromSettings() {
|
||||||
// Disconnect
|
// Disconnect
|
||||||
await fetch('/api/hydrabase/disconnect', { method: 'POST' });
|
await fetch('/api/hydrabase/disconnect', { method: 'POST' });
|
||||||
if (btn) btn.textContent = 'Connect';
|
if (btn) btn.textContent = 'Connect';
|
||||||
if (statusEl) statusEl.textContent = 'Disconnected';
|
if (statusEl) { statusEl.textContent = 'Disconnected'; statusEl.style.color = 'rgba(255,255,255,0.4)'; }
|
||||||
|
// Remove from fallback dropdown + reset to iTunes if was selected
|
||||||
|
const fbSel2 = document.getElementById('metadata-fallback-source');
|
||||||
|
if (fbSel2) {
|
||||||
|
const hbOpt = fbSel2.querySelector('option[value="hydrabase"]');
|
||||||
|
if (hbOpt) {
|
||||||
|
if (fbSel2.value === 'hydrabase') fbSel2.value = 'itunes';
|
||||||
|
hbOpt.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
showToast('Hydrabase disconnected', 'info');
|
showToast('Hydrabase disconnected', 'info');
|
||||||
} else {
|
} else {
|
||||||
// Connect
|
// Connect
|
||||||
|
|
@ -6119,7 +6148,15 @@ async function toggleHydrabaseFromSettings() {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
if (btn) btn.textContent = 'Disconnect';
|
if (btn) btn.textContent = 'Disconnect';
|
||||||
if (statusEl) statusEl.textContent = 'Connected';
|
if (statusEl) { statusEl.textContent = 'Connected'; statusEl.style.color = '#4caf50'; }
|
||||||
|
// Add to fallback dropdown
|
||||||
|
const fbSel = document.getElementById('metadata-fallback-source');
|
||||||
|
if (fbSel && !fbSel.querySelector('option[value="hydrabase"]')) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = 'hydrabase';
|
||||||
|
opt.textContent = 'Hydrabase (P2P)';
|
||||||
|
fbSel.appendChild(opt);
|
||||||
|
}
|
||||||
showToast('Hydrabase connected', 'success');
|
showToast('Hydrabase connected', 'success');
|
||||||
} else {
|
} else {
|
||||||
if (statusEl) statusEl.textContent = data.error || 'Connection failed';
|
if (statusEl) statusEl.textContent = data.error || 'Connection failed';
|
||||||
|
|
@ -6440,7 +6477,6 @@ async function saveSettings(quiet = false) {
|
||||||
fallback_source: document.getElementById('metadata-fallback-source').value || 'itunes'
|
fallback_source: document.getElementById('metadata-fallback-source').value || 'itunes'
|
||||||
},
|
},
|
||||||
hydrabase: {
|
hydrabase: {
|
||||||
enabled: document.getElementById('hydrabase-enabled').checked,
|
|
||||||
url: document.getElementById('hydrabase-url').value,
|
url: document.getElementById('hydrabase-url').value,
|
||||||
api_key: document.getElementById('hydrabase-api-key').value,
|
api_key: document.getElementById('hydrabase-api-key').value,
|
||||||
auto_connect: document.getElementById('hydrabase-auto-connect').checked
|
auto_connect: document.getElementById('hydrabase-auto-connect').checked
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue