diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 8bdb740a9..82afce4e1 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -1810,6 +1810,7 @@ "endpointLabel": "Endpunkt", "lastErrorLabel": "Letzter Fehler", "queueStateTitle": "Remote-Warteschlange", + "mixedQueueNotCast": "Gemischte Warteschlange nicht an Qobuz Connect gesendet (enthält lokale oder Plex-Titel).", "queueVersionLabel": "Version", "queueItemsLabel": "Warteschlangeneinträge", "autoplayModeLabel": "Autoplay", diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index 3643ae8b9..19df37529 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -1810,6 +1810,7 @@ "endpointLabel": "Endpoint", "lastErrorLabel": "Last error", "queueStateTitle": "Remote Queue", + "mixedQueueNotCast": "Mixed queue not cast to Qobuz Connect (contains local or Plex tracks).", "queueVersionLabel": "Version", "queueItemsLabel": "Queue items", "autoplayModeLabel": "Autoplay", diff --git a/src/lib/i18n/locales/es.json b/src/lib/i18n/locales/es.json index ee362ba97..1f253b54d 100644 --- a/src/lib/i18n/locales/es.json +++ b/src/lib/i18n/locales/es.json @@ -1810,6 +1810,7 @@ "endpointLabel": "Endpoint", "lastErrorLabel": "Ultimo error", "queueStateTitle": "Cola remota", + "mixedQueueNotCast": "La cola mixta no se envió a Qobuz Connect (tiene pistas locales o de Plex).", "queueVersionLabel": "Version", "queueItemsLabel": "Items en cola", "autoplayModeLabel": "Autoplay", diff --git a/src/lib/i18n/locales/fr.json b/src/lib/i18n/locales/fr.json index ec51a079d..791bc6520 100644 --- a/src/lib/i18n/locales/fr.json +++ b/src/lib/i18n/locales/fr.json @@ -1810,6 +1810,7 @@ "endpointLabel": "Point d'accès", "lastErrorLabel": "Dernière erreur", "queueStateTitle": "File d'attente distante", + "mixedQueueNotCast": "File d'attente mixte non envoyée à Qobuz Connect (contient des pistes locales ou Plex).", "queueVersionLabel": "Version", "queueItemsLabel": "Éléments de la file", "autoplayModeLabel": "Lecture auto", diff --git a/src/lib/i18n/locales/pt.json b/src/lib/i18n/locales/pt.json index e558c733f..0f5a79330 100644 --- a/src/lib/i18n/locales/pt.json +++ b/src/lib/i18n/locales/pt.json @@ -1810,6 +1810,7 @@ "endpointLabel": "Endpoint", "lastErrorLabel": "Último erro", "queueStateTitle": "Fila remota", + "mixedQueueNotCast": "Fila mista não enviada ao Qobuz Connect (contém faixas locais ou do Plex).", "queueVersionLabel": "Versão", "queueItemsLabel": "Itens da fila", "autoplayModeLabel": "Autoplay", diff --git a/src/lib/services/queuePlaybackService.test.ts b/src/lib/services/queuePlaybackService.test.ts index fade4ed1c..28d250b4f 100644 --- a/src/lib/services/queuePlaybackService.test.ts +++ b/src/lib/services/queuePlaybackService.test.ts @@ -65,6 +65,16 @@ describe('assessQconnectQueueSync', () => { blockedTrackIds: [] }); }); + + it('refuses (not subset) when queue is mixed — on-connect contract', () => { + const r = assessQconnectQueueSync([ + buildTrack(1, { source: 'qobuz' }), + buildTrack(2, { source: 'local' }) + ]); + expect(r.syncable).toBe(false); + expect(r.reason).toBe('queue_contains_non_qobuz_tracks'); + expect(r.trackIds).toEqual([]); // subset NOT returned + }); }); describe('assessQconnectQueueSync — offline-cache', () => { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 589b7eccd..2deb2804c 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -357,10 +357,13 @@ handleAddToFavorites, addToPlaylist, shareQobuzTrackLink, - shareSonglinkTrack, - loadQconnectQueue + shareSonglinkTrack } from '$lib/services/trackActions'; - import { replacePlaybackQueue } from '$lib/services/queuePlaybackService'; + import { + replacePlaybackQueue, + assessQconnectQueueSync, + syncQconnectQueueFromTracks + } from '$lib/services/queuePlaybackService'; import type { QconnectDiagnosticsPayload, QconnectQueueSnapshot, @@ -5726,17 +5729,38 @@ // Delay briefly so the QConnect session setup (ask_for_queue_state etc.) // completes before we try to push our queue. setTimeout(() => { - const queueState = getQueueState(); - const trackIds = queueState.queue - .map(item => item.trackId ?? parseInt(item.id)) - .filter((id): id is number => typeof id === 'number' && !isNaN(id) && id > 0); - if (trackIds.length > 0) { - console.log('[QConnect] Pushing local queue to remote on connect (%d tracks)', trackIds.length); - loadQconnectQueue(trackIds, 0).then(ok => { - if (ok) console.log('[QConnect] Local queue pushed to remote on connect'); - else console.warn('[QConnect] Local queue NOT pushed on connect (rejected or failed)'); - }).catch(err => console.error('[QConnect] Local queue push on connect error:', err)); - } + void (async () => { + // Use the BACKEND queue snapshot: its tracks carry `source` + // and `is_local`, which the frontend display queue + // (getQueueState) does not. The mixed-queue guard needs them + // to refuse pushing a local/Plex row id to a Qobuz renderer. + const state = await getBackendQueueState(); + if (!state) { + console.warn('[QConnect] On-connect push skipped: no backend queue snapshot'); + return; + } + const tracks = [ + ...state.history, + ...(state.current_track ? [state.current_track] : []), + ...state.upcoming + ]; + // Start index = the current track's position in the ordered list. + const startIndex = state.current_track ? state.history.length : 0; + const assessment = assessQconnectQueueSync(tracks); + if (!assessment.syncable) { + console.warn('[QConnect] On-connect push refused: reason=%s blocked=%o', + assessment.reason, assessment.blockedTrackIds); + if (assessment.reason === 'queue_contains_non_qobuz_tracks') { + showToast($t('qconnect.mixedQueueNotCast'), 'warning'); + } + return; + } + console.log('[QConnect] Pushing local queue to remote on connect (%d tracks)', tracks.length); + const ok = await syncQconnectQueueFromTracks(tracks, startIndex, 'on-connect-push'); + console.log(ok + ? '[QConnect] Local queue pushed to remote on connect' + : '[QConnect] Local queue NOT pushed on connect (rejected or failed)'); + })().catch(err => console.error('[QConnect] Local queue push on connect error:', err)); }, 2000); } }