Add HiFi API instance health check to Settings
New "Check All Instances" button in Settings > Downloads > HiFi shows each configured instance with color-coded status: green for fully working (can download), orange for search-only (downloads fail), red for offline/SSL error/timeout. Helps users understand why HiFi downloads aren't working when community instances go down.
This commit is contained in:
parent
6180135f94
commit
e1f7b8f5cc
3 changed files with 85 additions and 0 deletions
|
|
@ -31226,6 +31226,47 @@ def hifi_status():
|
||||||
return jsonify({"available": False, "error": str(e)})
|
return jsonify({"available": False, "error": str(e)})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/hifi/instances', methods=['GET'])
|
||||||
|
def hifi_instances():
|
||||||
|
"""Check availability of all HiFi API instances."""
|
||||||
|
import requests as req
|
||||||
|
try:
|
||||||
|
hifi = soulseek_client.hifi
|
||||||
|
instances = list(hifi._instances)
|
||||||
|
results = []
|
||||||
|
for url in instances:
|
||||||
|
entry = {'url': url, 'status': 'unknown', 'version': None, 'can_search': False, 'can_download': False}
|
||||||
|
try:
|
||||||
|
# Check root for version
|
||||||
|
r = req.get(f'{url}/', timeout=5, headers={'Accept': 'application/json'})
|
||||||
|
if r.ok:
|
||||||
|
data = r.json()
|
||||||
|
entry['version'] = data.get('version')
|
||||||
|
entry['status'] = 'online'
|
||||||
|
# Check search
|
||||||
|
sr = req.get(f'{url}/search', params={'s': 'test', 'limit': 1}, timeout=5)
|
||||||
|
entry['can_search'] = sr.ok
|
||||||
|
# Check track (download capability)
|
||||||
|
tr = req.get(f'{url}/track', params={'id': '1550546', 'quality': 'LOSSLESS'}, timeout=5)
|
||||||
|
entry['can_download'] = tr.ok
|
||||||
|
if not tr.ok:
|
||||||
|
entry['download_error'] = f'HTTP {tr.status_code}'
|
||||||
|
else:
|
||||||
|
entry['status'] = f'error (HTTP {r.status_code})'
|
||||||
|
except req.exceptions.SSLError:
|
||||||
|
entry['status'] = 'ssl_error'
|
||||||
|
except req.exceptions.ConnectTimeout:
|
||||||
|
entry['status'] = 'timeout'
|
||||||
|
except req.exceptions.ConnectionError:
|
||||||
|
entry['status'] = 'offline'
|
||||||
|
except Exception as e:
|
||||||
|
entry['status'] = f'error ({type(e).__name__})'
|
||||||
|
results.append(entry)
|
||||||
|
return jsonify({'instances': results, 'active': hifi._get_instance()})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
# DEEZER DOWNLOAD ENDPOINTS
|
# DEEZER DOWNLOAD ENDPOINTS
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
|
||||||
|
|
@ -4825,6 +4825,11 @@
|
||||||
Free lossless downloads via community-run hifi-api instances. No authentication or subscription needed.
|
Free lossless downloads via community-run hifi-api instances. No authentication or subscription needed.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>API Instances:</label>
|
||||||
|
<button class="test-button" onclick="checkHiFiInstances()" id="hifi-instances-check-btn" style="margin-bottom: 8px;">Check All Instances</button>
|
||||||
|
<div id="hifi-instances-panel" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Deezer Download Settings (shown only when deezer_dl mode is selected) -->
|
<!-- Deezer Download Settings (shown only when deezer_dl mode is selected) -->
|
||||||
|
|
|
||||||
|
|
@ -7738,6 +7738,45 @@ async function testHiFiConnection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkHiFiInstances() {
|
||||||
|
const panel = document.getElementById('hifi-instances-panel');
|
||||||
|
const btn = document.getElementById('hifi-instances-check-btn');
|
||||||
|
if (!panel) return;
|
||||||
|
panel.style.display = 'block';
|
||||||
|
panel.innerHTML = '<div style="color: rgba(255,255,255,0.4); font-size: 0.85em; padding: 8px 0;">Checking instances...</div>';
|
||||||
|
if (btn) { btn.disabled = true; btn.textContent = 'Checking...'; }
|
||||||
|
try {
|
||||||
|
const resp = await fetch('/api/hifi/instances');
|
||||||
|
const data = await resp.json();
|
||||||
|
if (!data.instances || data.instances.length === 0) {
|
||||||
|
panel.innerHTML = '<div style="color: #ff9800; font-size: 0.85em;">No instances configured.</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const _statusIcon = (inst) => {
|
||||||
|
if (inst.can_download) return '<span style="color:#4caf50">● Download</span>';
|
||||||
|
if (inst.can_search) return '<span style="color:#ff9800">● Search only</span>';
|
||||||
|
if (inst.status === 'online') return '<span style="color:#ff9800">● Online (limited)</span>';
|
||||||
|
if (inst.status === 'ssl_error') return '<span style="color:#f44336">● SSL error</span>';
|
||||||
|
if (inst.status === 'timeout') return '<span style="color:#f44336">● Timeout</span>';
|
||||||
|
if (inst.status === 'offline') return '<span style="color:#f44336">● Offline</span>';
|
||||||
|
return `<span style="color:#f44336">● ${escapeHtml(inst.status)}</span>`;
|
||||||
|
};
|
||||||
|
panel.innerHTML = data.instances.map(inst => {
|
||||||
|
const isActive = inst.url === data.active;
|
||||||
|
const ver = inst.version ? ` v${inst.version}` : '';
|
||||||
|
const activeTag = isActive ? ' <span style="color:rgb(var(--accent-rgb));font-weight:600;font-size:0.75em;">(ACTIVE)</span>' : '';
|
||||||
|
return `<div style="display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px solid rgba(255,255,255,0.04);font-size:0.82em;">
|
||||||
|
<span style="color:rgba(255,255,255,0.6);font-family:monospace;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(inst.url)}${ver}${activeTag}</span>
|
||||||
|
<span style="flex-shrink:0;margin-left:12px;">${_statusIcon(inst)}</span>
|
||||||
|
</div>`;
|
||||||
|
}).join('');
|
||||||
|
} catch (e) {
|
||||||
|
panel.innerHTML = `<div style="color:#f44336;font-size:0.85em;">Error checking instances: ${escapeHtml(e.message)}</div>`;
|
||||||
|
} finally {
|
||||||
|
if (btn) { btn.disabled = false; btn.textContent = 'Check All Instances'; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function testDeezerDownloadConnection() {
|
async function testDeezerDownloadConnection() {
|
||||||
const statusEl = document.getElementById('deezer-download-status');
|
const statusEl = document.getElementById('deezer-download-status');
|
||||||
if (!statusEl) return;
|
if (!statusEl) return;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue