fix(discovery-v2): hydrate Continue Listening tracks with full metadata and context

The TrackRowLite click handler was constructing a DisplayTrack with only
the fields shown on the row (id, title, artist, album, albumArt), masked
by an `as DisplayTrack` cast. duration, durationSeconds, hires, bitDepth,
samplingRate and isrc never reached the player, so the NowPlayingBar
showed no time and the seekbar was unusable. The legacy HomeView path
(home:continue_listening) also wired setPlaybackContext +
replacePlaybackQueue before the play call; the discovery-v2 rewrite
dropped both, which left a stale context label and a stale queue after
clicking a track from the section.

Backend already returns the full TrackDisplayMeta (duration_seconds,
hires, bit_depth, sampling_rate, isrc); the wire and the display types
just weren't carrying it. Extend RecoTrackDisplayMeta and
DiscoveryTrackCard with those fields, propagate them in
fetchHomeResolved, and rebuild the click handler to set the
home:continue_listening context and replace the queue with the full
section before emitting onTrackPlay with a complete DisplayTrack.

cc
This commit is contained in:
vicrodh committed 2026-05-14 23:35:21 -06:00
1 parent a40b1b6c48
commit db39072488
2 files changed
+75 -9

No files matched your search

+57 -9
View File
@@ -43,6 +43,7 @@
import { toggleArtistFavorite } from '$lib/stores/artistFavoritesStore';
import { invoke } from '@tauri-apps/api/core';
import { replacePlaybackQueue } from '$lib/services/queuePlaybackService';
import { setPlaybackContext } from '$lib/stores/playbackContextStore';
import { playQueueIndex } from '$lib/stores/queueStore';
import type { BackendQueueTrack } from '$lib/stores/queueStore';
@@ -291,6 +292,61 @@
let spotlightTopTracksLoading = $state(false);
let spotlightRadioLoading = $state(false);
async function handleContinueListeningTrackClick(track: DiscoveryTrackCard) {
const trackIndex = continueListening.findIndex((entry) => entry.trackId === track.trackId);
if (trackIndex < 0) return;
const trackIds = continueListening.map((entry) => entry.trackId);
await setPlaybackContext(
'home_list',
'home:continue_listening',
'Continue Listening',
'qobuz',
trackIds,
trackIndex,
);
try {
const queueTracks: BackendQueueTrack[] = continueListening.map((entry) => ({
id: entry.trackId,
title: entry.title,
version: null,
artist: entry.artist,
album: entry.album,
duration_secs: entry.durationSeconds,
artwork_url: entry.artwork ?? null,
hires: entry.hires,
bit_depth: entry.bitDepth ?? null,
sample_rate: entry.samplingRate ?? null,
is_local: false,
album_id: entry.albumId ?? null,
artist_id: entry.artistId ?? null,
}));
await replacePlaybackQueue(queueTracks, trackIndex, {
debugLabel: 'discovery-v2:continue-listening',
});
} catch (err) {
console.error('[discovery-v2] continue listening queue failed', err);
}
onTrackPlay?.({
id: track.trackId,
title: track.title,
artist: track.artist,
album: track.album,
albumId: track.albumId,
artistId: track.artistId,
albumArt: track.artwork,
duration: track.duration,
durationSeconds: track.durationSeconds,
hires: track.hires,
bitDepth: track.bitDepth,
samplingRate: track.samplingRate,
isrc: track.isrc,
});
}
async function handleSpotlightTopTracks() {
if (!spotlight || spotlightTopTracksLoading || spotlight.topTracks.length === 0) return;
spotlightTopTracksLoading = true;
@@ -455,15 +511,7 @@
artist={track.artist}
artwork={track.artwork}
isPlaying={track.trackId === activeTrackId && isPlaybackActive === true}
onClick={() => onTrackPlay?.({
id: track.trackId,
title: track.title,
artist: track.artist,
album: track.album,
albumId: track.albumId,
artistId: track.artistId,
albumArt: track.artwork,
} as DisplayTrack)}
onClick={() => handleContinueListeningTrackClick(track)}
onArtistClick={track.artistId !== undefined
? () => onTrackGoToArtist?.(track.artistId!)
: undefined}
+18
View File
@@ -234,6 +234,12 @@ export interface DiscoveryTrackCard {
albumId?: string;
artistId?: number;
artwork?: string;
duration: string;
durationSeconds: number;
hires: boolean;
bitDepth?: number;
samplingRate?: number;
isrc?: string;
}
export interface DiscoveryArtistTile {
@@ -267,6 +273,12 @@ interface RecoTrackDisplayMeta {
albumArt: string;
albumId?: string;
artistId?: number;
duration: string;
durationSeconds: number;
hires: boolean;
bitDepth?: number;
samplingRate?: number;
isrc?: string;
}
interface RecoArtistCardMeta {
@@ -673,6 +685,12 @@ export async function fetchHomeResolved(
albumId: track.albumId,
artistId: track.artistId,
artwork: track.albumArt || undefined,
duration: track.duration,
durationSeconds: track.durationSeconds,
hires: track.hires,
bitDepth: track.bitDepth,
samplingRate: track.samplingRate,
isrc: track.isrc,
})),
topArtists: resp.topArtists.slice(0, perSection).map((a) => ({
artistId: a.id,