From ea9051409a43bc000a6f73e8392236b134208ffe Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:01:31 -0600 Subject: [PATCH] feat(dailyq): add generated DailyQ view and dynamic suggest client cx --- .../views/DynamicSuggestView.svelte | 543 ++++++++++++++++++ src/lib/i18n/locales/en.json | 34 ++ src/lib/i18n/locales/es.json | 34 ++ src/lib/services/dynamicSuggest.ts | 25 + src/lib/types/dynamicSuggest.ts | 38 ++ 5 files changed, 674 insertions(+) create mode 100644 src/lib/components/views/DynamicSuggestView.svelte create mode 100644 src/lib/services/dynamicSuggest.ts create mode 100644 src/lib/types/dynamicSuggest.ts diff --git a/src/lib/components/views/DynamicSuggestView.svelte b/src/lib/components/views/DynamicSuggestView.svelte new file mode 100644 index 000000000..7e5f85b92 --- /dev/null +++ b/src/lib/components/views/DynamicSuggestView.svelte @@ -0,0 +1,543 @@ + + +
+
+
+
+ {#if result && result.tracks.items.length > 0} + {$t('yourMixes.title')} + {:else} +
+ +
+ {/if} +
+
+ + +
+ + {#if error} +

{error}

+ {/if} + +
+
+ +
+
+ + {#if !result && loading} +
{$t('actions.loading')}
+ {:else if result && filteredTracks.length === 0} +
{$t('yourMixes.result.empty')}
+ {:else if result} +
+
+ # + {$t('common.title')} + {$t('purchases.sort.album')} + {$t('album.duration')} + {$t('album.quality')} +
+ + {#each filteredTracks as track, index} + playTrack(track)} + /> + {/each} +
+ {/if} +
+ + { + showPlaylistModal = false; + }} + onSuccess={() => { + showToast($t('toast.addedToPlaylist'), 'success'); + showPlaylistModal = false; + }} +/> + + diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index cd0ac08b3..80520f358 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -1678,6 +1678,40 @@ }, "regionNotice": "Purchases availability depends on your Qobuz account and region. If unavailable, this may be expected." }, + "yourMixes": { + "title": "DailyQ", + "subtitle": "Based on your recent listening.", + "actions": { + "saveAsPlaylist": "Save as playlist" + }, + "form": { + "limit": "Limit", + "listenedTrackIds": "Listened track IDs (comma-separated)", + "listenedTrackIdsPlaceholder": "123,456,789", + "tracksToAnalyse": "Tracks to analyse (one row: trackId,artistId,genreId,labelId)", + "tracksToAnalysePlaceholder": "123,45,78,90", + "prefillFromRecents": "Use QBZ recent log", + "run": "Fetch Suggestions", + "probeRaw": "Probe Raw Response", + "rawResponse": "Raw response" + }, + "result": { + "algorithm": "Algorithm", + "count": "{count} tracks", + "sourceCached": "Cached for today", + "sourceLive": "Freshly generated", + "empty": "No suggested tracks returned.", + "emptyHint": "Tried fallback payload strategies. This account/seed set may currently return no mix candidates.", + "unknownArtist": "Unknown artist" + }, + "errors": { + "fetchFailed": "Failed to fetch dynamic suggestions.", + "prefillFailed": "Failed to load recent data from QBZ logs.", + "rawProbeFailed": "Failed to fetch raw endpoint response.", + "playlistsLoadFailed": "Failed to load your playlists.", + "emptyPlaylist": "No tracks available to save as playlist." + } + }, "linkResolver": { "title": "Open Qobuz Link", "placeholder": "Paste a Qobuz link (album, track, artist, playlist)...", diff --git a/src/lib/i18n/locales/es.json b/src/lib/i18n/locales/es.json index 2b2ba1cdb..4192baffa 100644 --- a/src/lib/i18n/locales/es.json +++ b/src/lib/i18n/locales/es.json @@ -1678,6 +1678,40 @@ }, "regionNotice": "La disponibilidad de Compras depende de tu cuenta y region de Qobuz. Si no aparece disponible, puede ser esperado." }, + "yourMixes": { + "title": "DailyQ", + "subtitle": "Basado en tus escuchas recientes.", + "actions": { + "saveAsPlaylist": "Guardar como playlist" + }, + "form": { + "limit": "Limite", + "listenedTrackIds": "IDs de pistas escuchadas (separadas por coma)", + "listenedTrackIdsPlaceholder": "123,456,789", + "tracksToAnalyse": "Pistas a analizar (una linea: trackId,artistId,genreId,labelId)", + "tracksToAnalysePlaceholder": "123,45,78,90", + "prefillFromRecents": "Usar log reciente de QBZ", + "run": "Obtener sugerencias", + "probeRaw": "Probar respuesta raw", + "rawResponse": "Respuesta raw" + }, + "result": { + "algorithm": "Algoritmo", + "count": "{count} pistas", + "sourceCached": "Cacheado por hoy", + "sourceLive": "Generado al momento", + "empty": "No se devolvieron pistas sugeridas.", + "emptyHint": "Se probaron estrategias de fallback del payload. Esta cuenta/semillas puede no estar devolviendo candidatos ahora.", + "unknownArtist": "Artista desconocido" + }, + "errors": { + "fetchFailed": "No se pudieron obtener sugerencias dinamicas.", + "prefillFailed": "No se pudieron cargar datos recientes del log de QBZ.", + "rawProbeFailed": "No se pudo obtener la respuesta raw del endpoint.", + "playlistsLoadFailed": "No se pudieron cargar tus playlists.", + "emptyPlaylist": "No hay pistas para guardar como playlist." + } + }, "linkResolver": { "title": "Abrir enlace de Qobuz", "placeholder": "Pega un enlace de Qobuz (album, track, artista, playlist)...", diff --git a/src/lib/services/dynamicSuggest.ts b/src/lib/services/dynamicSuggest.ts new file mode 100644 index 000000000..22282f8db --- /dev/null +++ b/src/lib/services/dynamicSuggest.ts @@ -0,0 +1,25 @@ +import { invoke } from '@tauri-apps/api/core'; +import type { + DynamicSuggestRequest, + DynamicSuggestResponse +} from '$lib/types/dynamicSuggest'; + +export async function getDynamicSuggest( + payload: DynamicSuggestRequest +): Promise { + return invoke('v2_dynamic_suggest', { + limit: payload.limit ?? null, + listenedTrackIds: payload.listenedTrackIds ?? [], + tracksToAnalyse: payload.tracksToAnalyse ?? [] + }); +} + +export async function getDynamicSuggestRaw( + payload: DynamicSuggestRequest +): Promise { + return invoke('v2_dynamic_suggest_raw', { + limit: payload.limit ?? null, + listenedTrackIds: payload.listenedTrackIds ?? [], + tracksToAnalyse: payload.tracksToAnalyse ?? [] + }); +} diff --git a/src/lib/types/dynamicSuggest.ts b/src/lib/types/dynamicSuggest.ts new file mode 100644 index 000000000..e1fdafe95 --- /dev/null +++ b/src/lib/types/dynamicSuggest.ts @@ -0,0 +1,38 @@ +export interface DynamicTrackToAnalyseInput { + trackId: number; + artistId: number; + genreId: number; + labelId: number; +} + +export interface DynamicSuggestTrack { + id: number; + title: string; + duration?: number; + performer?: { id?: number; name: string }; + album?: { + id?: string; + title: string; + image?: { small?: string; thumbnail?: string; large?: string }; + }; + hires?: boolean; + maximum_bit_depth?: number; + maximum_sampling_rate?: number; + streamable?: boolean; +} + +export interface DynamicSuggestResponse { + algorithm: string; + tracks: { + offset: number; + limit: number; + total: number; + items: DynamicSuggestTrack[]; + }; +} + +export interface DynamicSuggestRequest { + limit?: number; + listenedTrackIds?: number[]; + tracksToAnalyse?: DynamicTrackToAnalyseInput[]; +}