diff --git a/web_server.py b/web_server.py index b6f3ea6a..a53bd02b 100644 --- a/web_server.py +++ b/web_server.py @@ -35272,6 +35272,7 @@ def _emit_discovery_progress_loop(): """Push discovery progress to subscribed rooms every 1 second.""" platform_states = { 'tidal': lambda: tidal_discovery_states, + 'qobuz': lambda: qobuz_discovery_states, 'deezer': lambda: deezer_discovery_states, 'youtube': lambda: youtube_playlist_states, 'beatport': lambda: beatport_chart_states, diff --git a/webui/static/sync-services.js b/webui/static/sync-services.js index b70ce0e7..41ab7761 100644 --- a/webui/static/sync-services.js +++ b/webui/static/sync-services.js @@ -8418,6 +8418,8 @@ function openYouTubeDiscoveryModal(urlHash) { console.log('🔄 Modal opened in syncing phase - starting immediate polling...'); if (state.is_tidal_playlist) { startTidalSyncPolling(urlHash); + } else if (state.is_qobuz_playlist) { + startQobuzSyncPolling(urlHash); } else if (state.is_deezer_playlist) { startDeezerSyncPolling(urlHash); } else if (state.is_spotify_public_playlist) { @@ -9011,13 +9013,14 @@ function closeYouTubeDiscoveryModal(urlHash) { const state = youtubePlaylistStates[urlHash]; if (state) { const isTidal = state.is_tidal_playlist; + const isQobuz = state.is_qobuz_playlist; const isDeezer = state.is_deezer_playlist; const isSpotifyPublic = state.is_spotify_public_playlist; const isBeatport = state.is_beatport_playlist; // Reset to 'discovered' phase if modal is closed after completion (like Tidal does) if (state.phase === 'sync_complete' || state.phase === 'download_complete') { - console.log(`🧹 [Modal Close] Resetting ${isSpotifyPublic ? 'Spotify Public' : (isDeezer ? 'Deezer' : (isBeatport ? 'Beatport' : (isTidal ? 'Tidal' : 'YouTube')))} state after completion`); + console.log(`🧹 [Modal Close] Resetting ${isSpotifyPublic ? 'Spotify Public' : (isDeezer ? 'Deezer' : (isQobuz ? 'Qobuz' : (isBeatport ? 'Beatport' : (isTidal ? 'Tidal' : 'YouTube'))))} state after completion`); if (isSpotifyPublic) { // Spotify Public: Extract url_hash and reset state @@ -9113,6 +9116,37 @@ function closeYouTubeDiscoveryModal(urlHash) { console.warn('⚠️ Error updating backend Tidal phase:', error); } } + } else if (isQobuz) { + // Qobuz: same shape as Tidal — preserve discovery data, + // reset phase to 'discovered', push update to backend. + const qobuzPlaylistId = state.qobuz_playlist_id || null; + if (qobuzPlaylistId && qobuzPlaylistStates[qobuzPlaylistId]) { + const preservedData = { + playlist: qobuzPlaylistStates[qobuzPlaylistId].playlist, + discovery_results: qobuzPlaylistStates[qobuzPlaylistId].discovery_results, + spotify_matches: qobuzPlaylistStates[qobuzPlaylistId].spotify_matches, + discovery_progress: qobuzPlaylistStates[qobuzPlaylistId].discovery_progress, + convertedSpotifyPlaylistId: qobuzPlaylistStates[qobuzPlaylistId].convertedSpotifyPlaylistId + }; + + delete qobuzPlaylistStates[qobuzPlaylistId].download_process_id; + delete qobuzPlaylistStates[qobuzPlaylistId].phase; + + Object.assign(qobuzPlaylistStates[qobuzPlaylistId], preservedData); + qobuzPlaylistStates[qobuzPlaylistId].phase = 'discovered'; + + updateQobuzCardPhase(qobuzPlaylistId, 'discovered'); + + try { + fetch(`/api/qobuz/update_phase/${qobuzPlaylistId}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ phase: 'discovered' }) + }); + } catch (error) { + console.warn('⚠️ Error updating backend Qobuz phase:', error); + } + } } else if (isBeatport) { // Beatport: Reset chart state const chartHash = state.beatport_chart_hash || urlHash;