Gate Discogs primary source by token
- Show Discogs with a lock icon until a personal access token is present. - Prevent selecting locked Discogs and steer users to the Discogs settings section. - Keep metadata-source availability and selection state synced as the token changes.
This commit is contained in:
parent
5ff20fbfec
commit
4e40bce3e9
3 changed files with 106 additions and 25 deletions
|
|
@ -476,6 +476,12 @@ function handleServiceStatusUpdate(data) {
|
||||||
if (typeof syncSpotifySettingsAuthState === 'function') {
|
if (typeof syncSpotifySettingsAuthState === 'function') {
|
||||||
syncSpotifySettingsAuthState(data?.spotify || null);
|
syncSpotifySettingsAuthState(data?.spotify || null);
|
||||||
}
|
}
|
||||||
|
if (typeof syncPrimaryMetadataSourceAvailability === 'function') {
|
||||||
|
syncPrimaryMetadataSourceAvailability(data?.spotify || null);
|
||||||
|
}
|
||||||
|
if (typeof sanitizeMetadataSourceSelection === 'function') {
|
||||||
|
sanitizeMetadataSourceSelection({ quiet: true });
|
||||||
|
}
|
||||||
|
|
||||||
// Same logic as fetchAndUpdateServiceStatus response handler
|
// Same logic as fetchAndUpdateServiceStatus response handler
|
||||||
updateServiceStatus('spotify', data.spotify);
|
updateServiceStatus('spotify', data.spotify);
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,25 @@ function syncMetadataSourceSelection(source) {
|
||||||
select.dataset.lastValidSource = source;
|
select.dataset.lastValidSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusSpotifySettingsSection() {
|
function _isMetadataSourceSelectable(source) {
|
||||||
const card = document.querySelector('#settings-page .stg-service[data-service="spotify"]');
|
if (source === 'spotify') {
|
||||||
|
return _lastServiceStatus?.spotify?.authenticated === true;
|
||||||
|
}
|
||||||
|
if (source === 'discogs') {
|
||||||
|
const token = document.getElementById('discogs-token');
|
||||||
|
return !!token?.value?.trim();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _metadataSourceFallback(source) {
|
||||||
|
if (source === 'spotify') return 'deezer';
|
||||||
|
if (source === 'discogs') return 'itunes';
|
||||||
|
return 'itunes';
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusServiceSettingsSection(service, message) {
|
||||||
|
const card = document.querySelector(`#settings-page .stg-service[data-service="${service}"]`);
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
const header = card.querySelector('.stg-service-header');
|
const header = card.querySelector('.stg-service-header');
|
||||||
|
|
@ -74,7 +91,39 @@ function focusSpotifySettingsSection() {
|
||||||
firstControl.focus({ preventScroll: true });
|
firstControl.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
showToast('Spotify must be authenticated before it can be selected as the primary metadata source.', 'warning');
|
if (message) {
|
||||||
|
showToast(message, 'warning');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeMetadataSourceSelection({ quiet = true } = {}) {
|
||||||
|
const select = document.getElementById('metadata-fallback-source');
|
||||||
|
if (!select) return false;
|
||||||
|
|
||||||
|
const selectedSource = select.value || 'itunes';
|
||||||
|
if (_isMetadataSourceSelectable(selectedSource)) {
|
||||||
|
select.dataset.lastValidSource = selectedSource;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastValid = select.dataset.lastValidSource;
|
||||||
|
const fallbackSource = lastValid && lastValid !== selectedSource && _isMetadataSourceSelectable(lastValid)
|
||||||
|
? lastValid
|
||||||
|
: _metadataSourceFallback(selectedSource);
|
||||||
|
|
||||||
|
if (fallbackSource && fallbackSource !== selectedSource) {
|
||||||
|
select.value = fallbackSource;
|
||||||
|
}
|
||||||
|
select.dataset.lastValidSource = fallbackSource;
|
||||||
|
|
||||||
|
if (!quiet) {
|
||||||
|
const message = selectedSource === 'discogs'
|
||||||
|
? 'Discogs requires a personal access token before it can be selected as the primary metadata source.'
|
||||||
|
: 'Spotify must be authenticated before it can be selected as the primary metadata source.';
|
||||||
|
focusServiceSettingsSection(selectedSource, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMetadataSourceChange(event) {
|
function handleMetadataSourceChange(event) {
|
||||||
|
|
@ -82,22 +131,12 @@ function handleMetadataSourceChange(event) {
|
||||||
if (!select || select.id !== 'metadata-fallback-source') return;
|
if (!select || select.id !== 'metadata-fallback-source') return;
|
||||||
|
|
||||||
const selectedSource = select.value;
|
const selectedSource = select.value;
|
||||||
if (selectedSource !== 'spotify') {
|
if (_isMetadataSourceSelectable(selectedSource)) {
|
||||||
select.dataset.lastValidSource = selectedSource;
|
select.dataset.lastValidSource = selectedSource;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spotifySessionActive = _lastServiceStatus?.spotify?.authenticated === true;
|
sanitizeMetadataSourceSelection({ quiet: false });
|
||||||
if (spotifySessionActive) {
|
|
||||||
select.dataset.lastValidSource = selectedSource;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fallbackSource = select.dataset.lastValidSource || _lastServiceStatus?.spotify?.source || 'deezer';
|
|
||||||
if (fallbackSource && fallbackSource !== 'spotify') {
|
|
||||||
select.value = fallbackSource;
|
|
||||||
}
|
|
||||||
focusSpotifySettingsSection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeSettings() {
|
function initializeSettings() {
|
||||||
|
|
@ -130,6 +169,15 @@ function initializeSettings() {
|
||||||
if (metadataSourceSelect) {
|
if (metadataSourceSelect) {
|
||||||
metadataSourceSelect.addEventListener('change', handleMetadataSourceChange);
|
metadataSourceSelect.addEventListener('change', handleMetadataSourceChange);
|
||||||
}
|
}
|
||||||
|
const discogsTokenInput = document.getElementById('discogs-token');
|
||||||
|
if (discogsTokenInput) {
|
||||||
|
discogsTokenInput.addEventListener('input', () => {
|
||||||
|
if (typeof syncPrimaryMetadataSourceAvailability === 'function') {
|
||||||
|
syncPrimaryMetadataSourceAvailability(_lastServiceStatus?.spotify || null);
|
||||||
|
}
|
||||||
|
sanitizeMetadataSourceSelection({ quiet: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Server toggle buttons
|
// Server toggle buttons
|
||||||
const plexToggle = document.getElementById('plex-toggle');
|
const plexToggle = document.getElementById('plex-toggle');
|
||||||
|
|
@ -158,11 +206,12 @@ function initializeSettings() {
|
||||||
// Test connection buttons
|
// Test connection buttons
|
||||||
// Test button event listeners removed - they use onclick attributes in HTML to avoid double firing
|
// Test button event listeners removed - they use onclick attributes in HTML to avoid double firing
|
||||||
|
|
||||||
if (typeof syncSpotifyMetadataSourceAvailability === 'function') {
|
if (typeof syncPrimaryMetadataSourceAvailability === 'function') {
|
||||||
syncSpotifyMetadataSourceAvailability(_lastServiceStatus?.spotify || null);
|
syncPrimaryMetadataSourceAvailability(_lastServiceStatus?.spotify || null);
|
||||||
}
|
}
|
||||||
syncSpotifySettingsAuthState(_lastServiceStatus?.spotify || null);
|
syncSpotifySettingsAuthState(_lastServiceStatus?.spotify || null);
|
||||||
syncMetadataSourceSelection(_lastServiceStatus?.spotify?.source);
|
syncMetadataSourceSelection(_lastServiceStatus?.spotify?.source);
|
||||||
|
sanitizeMetadataSourceSelection({ quiet: true });
|
||||||
if (metadataSourceSelect) {
|
if (metadataSourceSelect) {
|
||||||
metadataSourceSelect.dataset.lastValidSource = metadataSourceSelect.value;
|
metadataSourceSelect.dataset.lastValidSource = metadataSourceSelect.value;
|
||||||
}
|
}
|
||||||
|
|
@ -2491,6 +2540,8 @@ async function saveSettings(quiet = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const metadataSourceSelect = document.getElementById('metadata-fallback-source');
|
const metadataSourceSelect = document.getElementById('metadata-fallback-source');
|
||||||
|
const discogsTokenInput = document.getElementById('discogs-token');
|
||||||
|
const discogsTokenPresent = !!discogsTokenInput?.value?.trim();
|
||||||
let metadataSource = metadataSourceSelect?.value || 'itunes';
|
let metadataSource = metadataSourceSelect?.value || 'itunes';
|
||||||
const spotifySessionActive = _lastServiceStatus?.spotify?.authenticated === true;
|
const spotifySessionActive = _lastServiceStatus?.spotify?.authenticated === true;
|
||||||
if (metadataSource === 'spotify' && !spotifySessionActive) {
|
if (metadataSource === 'spotify' && !spotifySessionActive) {
|
||||||
|
|
@ -2499,6 +2550,12 @@ async function saveSettings(quiet = false) {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
showToast('Spotify is disconnected, so Deezer is used as the primary metadata source.', 'warning');
|
showToast('Spotify is disconnected, so Deezer is used as the primary metadata source.', 'warning');
|
||||||
}
|
}
|
||||||
|
} else if (metadataSource === 'discogs' && !discogsTokenPresent) {
|
||||||
|
metadataSource = 'itunes';
|
||||||
|
if (metadataSourceSelect) metadataSourceSelect.value = metadataSource;
|
||||||
|
if (!quiet) {
|
||||||
|
showToast('Discogs requires a personal access token before it can be selected as the primary metadata source.', 'warning');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
|
|
|
||||||
|
|
@ -3148,6 +3148,12 @@ async function fetchAndUpdateServiceStatus() {
|
||||||
if (typeof syncSpotifySettingsAuthState === 'function') {
|
if (typeof syncSpotifySettingsAuthState === 'function') {
|
||||||
syncSpotifySettingsAuthState(data?.spotify || null);
|
syncSpotifySettingsAuthState(data?.spotify || null);
|
||||||
}
|
}
|
||||||
|
if (typeof syncPrimaryMetadataSourceAvailability === 'function') {
|
||||||
|
syncPrimaryMetadataSourceAvailability(data?.spotify || null);
|
||||||
|
}
|
||||||
|
if (typeof sanitizeMetadataSourceSelection === 'function') {
|
||||||
|
sanitizeMetadataSourceSelection({ quiet: true });
|
||||||
|
}
|
||||||
|
|
||||||
// Update service status indicators and text (dashboard)
|
// Update service status indicators and text (dashboard)
|
||||||
updateServiceStatus('spotify', data.spotify);
|
updateServiceStatus('spotify', data.spotify);
|
||||||
|
|
@ -3190,20 +3196,32 @@ async function fetchAndUpdateServiceStatus() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncSpotifyMetadataSourceAvailability(statusData) {
|
function syncPrimaryMetadataSourceAvailability(statusData) {
|
||||||
const select = document.getElementById('metadata-fallback-source');
|
const select = document.getElementById('metadata-fallback-source');
|
||||||
if (!select) return;
|
if (!select) return;
|
||||||
if (!statusData) return;
|
if (!statusData) return;
|
||||||
|
|
||||||
const spotifyOption = select.querySelector('option[value="spotify"]');
|
const spotifyOption = select.querySelector('option[value="spotify"]');
|
||||||
if (!spotifyOption) return;
|
const discogsOption = select.querySelector('option[value="discogs"]');
|
||||||
|
|
||||||
const spotifyAvailable = statusData?.authenticated === true;
|
const spotifyAvailable = statusData?.authenticated === true;
|
||||||
spotifyOption.dataset.unavailable = spotifyAvailable ? 'false' : 'true';
|
if (spotifyOption) {
|
||||||
spotifyOption.textContent = spotifyAvailable ? 'Spotify' : '🔒 Spotify';
|
spotifyOption.dataset.unavailable = spotifyAvailable ? 'false' : 'true';
|
||||||
spotifyOption.title = spotifyAvailable
|
spotifyOption.textContent = spotifyAvailable ? 'Spotify' : '🔒 Spotify';
|
||||||
? 'Spotify'
|
spotifyOption.title = spotifyAvailable
|
||||||
: 'Spotify authentication is required before this source can be selected.';
|
? 'Spotify'
|
||||||
|
: 'Spotify authentication is required before this source can be selected.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discogsOption) {
|
||||||
|
const discogsToken = document.getElementById('discogs-token');
|
||||||
|
const discogsAvailable = !!discogsToken?.value?.trim();
|
||||||
|
discogsOption.dataset.unavailable = discogsAvailable ? 'false' : 'true';
|
||||||
|
discogsOption.textContent = discogsAvailable ? 'Discogs' : '🔒 Discogs';
|
||||||
|
discogsOption.title = discogsAvailable
|
||||||
|
? 'Discogs'
|
||||||
|
: 'Discogs personal access token is required before this source can be selected.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMetadataSourceLabel(source) {
|
function getMetadataSourceLabel(source) {
|
||||||
|
|
@ -3304,7 +3322,7 @@ function updateServiceStatus(service, statusData) {
|
||||||
disconnectBtn.style.display = spotifySessionActive ? '' : 'none';
|
disconnectBtn.style.display = spotifySessionActive ? '' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
syncSpotifyMetadataSourceAvailability(statusData);
|
syncPrimaryMetadataSourceAvailability(statusData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update download source title on dashboard card
|
// Update download source title on dashboard card
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue