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:
parent
55603be14c
commit
74e3cc460c
4 changed files with 83 additions and 34 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue