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:
2 files changed
+75
-9
No files matched your search
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user