Simplify service status and labels

- Flatten the Spotify service-status rendering so it shows rate-limit and recovery states explicitly, while otherwise displaying the active metadata provider directly.
- Keep the Spotify auth controls and metadata-source picker aligned with the real session state after authenticate and disconnect flows.
- Return "Unmapped" for unknown metadata source labels instead of implying iTunes.
- Update the metadata registry tests to cover the new label fallback.
This commit is contained in:
Antti Kettunen 2026-05-01 12:05:46 +03:00
parent 55603be14c
commit 74e3cc460c
No known key found for this signature in database
GPG key ID: C6B2A3D250359BD7
4 changed files with 83 additions and 34 deletions

View file

@ -309,7 +309,7 @@ def get_spotify_disconnect_source(configured_source: Optional[str] = None) -> st
def get_metadata_source_label(source: str) -> str:
"""Return a human-readable label for a metadata source."""
return METADATA_SOURCE_LABELS.get(source, source.replace("_", " ").title())
return METADATA_SOURCE_LABELS.get(source, "Unmapped")
def get_source_priority(preferred_source: str):

View file

@ -22,5 +22,5 @@ def test_metadata_source_label_maps_known_sources():
assert registry.get_metadata_source_label("hydrabase") == "Hydrabase"
def test_metadata_source_label_falls_back_to_title_case():
assert registry.get_metadata_source_label("apple_music") == "Apple Music"
def test_metadata_source_label_falls_back_to_unmapped():
assert registry.get_metadata_source_label("apple_music") == "Unmapped"

View file

@ -2414,8 +2414,8 @@ async function saveSettings(quiet = false) {
const metadataSourceSelect = document.getElementById('metadata-fallback-source');
let metadataSource = metadataSourceSelect?.value || 'itunes';
const spotifyDisconnected = _lastServiceStatus?.spotify?.connected === false;
if (metadataSource === 'spotify' && spotifyDisconnected) {
const spotifySessionActive = _lastServiceStatus?.spotify?.authenticated === true;
if (metadataSource === 'spotify' && !spotifySessionActive) {
metadataSource = 'deezer';
if (metadataSourceSelect) metadataSourceSelect.value = metadataSource;
if (!quiet) {

View file

@ -3186,33 +3186,86 @@ function syncSpotifyMetadataSourceAvailability(statusData) {
const spotifyOption = select.querySelector('option[value="spotify"]');
if (!spotifyOption) return;
const spotifyAvailable = statusData?.connected === true;
const spotifyAvailable = statusData?.authenticated === true;
spotifyOption.disabled = !spotifyAvailable;
spotifyOption.dataset.unavailable = spotifyAvailable ? 'false' : 'true';
}
function getMetadataSourceLabel(source) {
if (source === 'deezer') return 'Deezer';
if (source === 'discogs') return 'Discogs';
if (source === 'itunes') return 'iTunes';
if (source === 'spotify') return 'Spotify';
return 'Unmapped';
}
function getSpotifyStatusPresentation(statusData) {
const sourceLabel = getMetadataSourceLabel(statusData?.source);
const rateLimited = !!(statusData?.rate_limited && statusData?.rate_limit);
const cooldown = !!(statusData?.post_ban_cooldown > 0);
const sessionActive = statusData?.authenticated === true || (statusData?.authenticated === undefined && statusData?.source === 'spotify');
if (rateLimited) {
const remaining = statusData.rate_limit?.remaining_seconds || 0;
return {
statusClass: 'rate-limited',
statusText: `Spotify paused \u2014 ${formatRateLimitDuration(remaining)}`,
dotClass: 'rate-limited',
dotTitle: `Spotify paused \u2014 ${formatRateLimitDuration(remaining)} remaining`,
sessionActive
};
}
if (cooldown) {
const remaining = statusData.post_ban_cooldown;
return {
statusClass: 'rate-limited',
statusText: `Spotify recovering \u2014 ${formatRateLimitDuration(remaining)}`,
dotClass: 'rate-limited',
dotTitle: `Spotify recovering \u2014 ${formatRateLimitDuration(remaining)} cooldown`,
sessionActive
};
}
if (statusData?.source && statusData.source !== 'spotify') {
return {
statusClass: 'connected',
statusText: sourceLabel,
dotClass: 'connected',
dotTitle: sourceLabel,
sessionActive
};
}
return {
statusClass: 'connected',
statusText: `Connected (${statusData?.response_time}ms)`,
dotClass: 'connected',
dotTitle: '',
sessionActive
};
}
function updateServiceStatus(service, statusData) {
const indicator = document.getElementById(`${service}-status-indicator`);
const statusText = document.getElementById(`${service}-status-text`);
if (indicator && statusText) {
if (service === 'spotify' && (statusData.rate_limited || statusData.post_ban_cooldown)) {
indicator.className = 'service-card-indicator rate-limited';
const remaining = statusData.rate_limited
? formatRateLimitDuration(statusData.rate_limit?.remaining_seconds || 0)
: formatRateLimitDuration(statusData.post_ban_cooldown);
const phase = statusData.rate_limited ? 'paused' : 'recovering';
const fallbackLabel = statusData.source === 'deezer' ? 'Deezer' : 'iTunes';
statusText.textContent = `${fallbackLabel} (Spotify ${phase} \u2014 ${remaining})`;
statusText.className = 'service-card-status-text rate-limited';
} else if (statusData.connected) {
indicator.className = 'service-card-indicator connected';
statusText.textContent = `Connected (${statusData.response_time}ms)`;
statusText.className = 'service-card-status-text connected';
if (service === 'spotify') {
const presentation = getSpotifyStatusPresentation(statusData || {});
indicator.className = `service-card-indicator ${presentation.statusClass}`;
statusText.textContent = presentation.statusText;
statusText.className = `service-card-status-text ${presentation.statusClass}`;
} else {
indicator.className = 'service-card-indicator disconnected';
statusText.textContent = 'Disconnected';
statusText.className = 'service-card-status-text disconnected';
if (statusData.connected) {
indicator.className = 'service-card-indicator connected';
statusText.textContent = `Connected (${statusData.response_time}ms)`;
statusText.className = 'service-card-status-text connected';
} else {
indicator.className = 'service-card-indicator disconnected';
statusText.textContent = 'Disconnected';
statusText.className = 'service-card-status-text disconnected';
}
}
}
@ -3220,13 +3273,13 @@ function updateServiceStatus(service, statusData) {
if (service === 'spotify' && statusData.source) {
const musicSourceTitleElement = document.getElementById('music-source-title');
if (musicSourceTitleElement) {
const sourceName = statusData.source === 'spotify' ? 'Spotify' : statusData.source === 'deezer' ? 'Deezer' : statusData.source === 'discogs' ? 'Discogs' : 'iTunes';
const sourceName = getMetadataSourceLabel(statusData.source);
musicSourceTitleElement.textContent = sourceName;
currentMusicSourceName = sourceName;
}
// Keep the Spotify action buttons aligned with the actual auth session.
const spotifySessionActive = statusData.authenticated === true || (statusData.authenticated === undefined && statusData.source === 'spotify');
const spotifySessionActive = getSpotifyStatusPresentation(statusData || {}).sessionActive;
const authBtn = document.querySelector('button[onclick="authenticateSpotify()"]');
const disconnectBtn = document.getElementById('spotify-disconnect-btn');
if (authBtn) {
@ -3255,16 +3308,12 @@ function updateSidebarServiceStatus(service, statusData) {
const nameElement = indicator.querySelector('.status-name');
if (dot) {
if (service === 'spotify' && (statusData.rate_limited || statusData.post_ban_cooldown)) {
dot.className = 'status-dot rate-limited';
dot.title = statusData.rate_limited
? `Spotify paused \u2014 ${formatRateLimitDuration(statusData.rate_limit?.remaining_seconds || 0)} remaining`
: `Spotify recovering \u2014 ${formatRateLimitDuration(statusData.post_ban_cooldown)} cooldown`;
} else if (statusData.connected) {
dot.className = 'status-dot connected';
dot.title = '';
if (service === 'spotify') {
const presentation = getSpotifyStatusPresentation(statusData || {});
dot.className = `status-dot ${presentation.dotClass}`;
dot.title = presentation.dotTitle;
} else {
dot.className = 'status-dot disconnected';
dot.className = statusData?.connected ? 'status-dot connected' : 'status-dot disconnected';
dot.title = '';
}
}
@ -3282,7 +3331,7 @@ function updateSidebarServiceStatus(service, statusData) {
if (service === 'spotify' && statusData.source) {
const musicSourceNameElement = document.getElementById('music-source-name');
if (musicSourceNameElement) {
const sourceName = statusData.source === 'spotify' ? 'Spotify' : statusData.source === 'deezer' ? 'Deezer' : statusData.source === 'discogs' ? 'Discogs' : 'iTunes';
const sourceName = getMetadataSourceLabel(statusData.source);
musicSourceNameElement.textContent = sourceName;
}
}