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)})
|
||||
|
||||
|
||||
@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
|
||||
# ===================================================================
|
||||
|
|
|
|||
|
|
@ -4825,6 +4825,11 @@
|
|||
Free lossless downloads via community-run hifi-api instances. No authentication or subscription needed.
|
||||
</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>
|
||||
|
||||
<!-- 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() {
|
||||
const statusEl = document.getElementById('deezer-download-status');
|
||||
if (!statusEl) return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue