diff --git a/src-tauri/src/commands/playback.rs b/src-tauri/src/commands/playback.rs index 82f7dfd4d..d7a3528ea 100644 --- a/src-tauri/src/commands/playback.rs +++ b/src-tauri/src/commands/playback.rs @@ -215,6 +215,12 @@ fn spawn_prefetch( let track_id = track.id; let track_title = track.title.clone(); + // Skip local tracks - they don't need prefetching from Qobuz + if track.is_local { + log::debug!("Skipping prefetch for local track: {} - {}", track_id, track_title); + continue; + } + // Check if already cached or being fetched if cache.contains(track_id) { log::debug!("Track {} already cached", track_id); diff --git a/src-tauri/src/queue/mod.rs b/src-tauri/src/queue/mod.rs index 6c88520ed..4aac2f88d 100644 --- a/src-tauri/src/queue/mod.rs +++ b/src-tauri/src/queue/mod.rs @@ -23,6 +23,9 @@ pub struct QueueTrack { pub hires: bool, pub bit_depth: Option, pub sample_rate: Option, + /// Whether this is a local library track (not from Qobuz) + #[serde(default)] + pub is_local: bool, } /// Repeat mode options diff --git a/src/lib/components/views/PlaylistDetailView.svelte b/src/lib/components/views/PlaylistDetailView.svelte index f425d0d01..3c1848662 100644 --- a/src/lib/components/views/PlaylistDetailView.svelte +++ b/src/lib/components/views/PlaylistDetailView.svelte @@ -586,6 +586,7 @@ hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, sample_rate: t.samplingRate != null ? (t.isLocal ? t.samplingRate * 1000 : t.samplingRate) : null, + is_local: t.isLocal ?? false, })); // Collect local track IDs (original positive IDs) @@ -667,6 +668,7 @@ hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, sample_rate: t.samplingRate != null ? (t.isLocal ? t.samplingRate * 1000 : t.samplingRate) : null, + is_local: t.isLocal ?? false, } }); } catch (err) { @@ -694,6 +696,7 @@ hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, sample_rate: t.samplingRate != null ? (t.isLocal ? t.samplingRate * 1000 : t.samplingRate) : null, + is_local: t.isLocal ?? false, })); // Collect local track IDs diff --git a/src/lib/services/playbackService.ts b/src/lib/services/playbackService.ts index 7863bdac5..641624056 100644 --- a/src/lib/services/playbackService.ts +++ b/src/lib/services/playbackService.ts @@ -81,7 +81,21 @@ export async function playTrack( setCurrentTrack(track); try { - if (showLoadingToast) { + // For Qobuz tracks: stop current playback immediately and show buffering + // This prevents the previous track from continuing while we download + // Local tracks load instantly so they don't need this + if (!isLocal && !isCasting()) { + // Stop current playback immediately + try { + await invoke('stop_playback'); + } catch { + // Ignore errors - player might not be playing + } + // Show buffering indicator + if (showLoadingToast) { + showToast(track.title, 'buffering'); + } + } else if (showLoadingToast && !isLocal) { showToast(track.title, 'buffering'); } diff --git a/src/lib/services/trackActions.ts b/src/lib/services/trackActions.ts index 7a4fa8eb3..856320931 100644 --- a/src/lib/services/trackActions.ts +++ b/src/lib/services/trackActions.ts @@ -36,7 +36,8 @@ export function buildQueueTrackFromQobuz(track: QobuzTrack): BackendQueueTrack { artwork_url: artwork || null, hires: track.hires_streamable ?? false, bit_depth: track.maximum_bit_depth ?? null, - sample_rate: track.maximum_sampling_rate ?? null + sample_rate: track.maximum_sampling_rate ?? null, + is_local: false }; } @@ -50,7 +51,8 @@ export function buildQueueTrackFromAlbumTrack(track: Track, albumArtwork: string artwork_url: albumArtwork || null, hires: track.hires ?? false, bit_depth: track.bitDepth ?? null, - sample_rate: track.samplingRate ?? null + sample_rate: track.samplingRate ?? null, + is_local: false }; } @@ -64,7 +66,8 @@ export function buildQueueTrackFromPlaylistTrack(track: PlaylistTrack): BackendQ artwork_url: track.albumArt || null, hires: track.hires ?? false, bit_depth: track.bitDepth ?? null, - sample_rate: track.samplingRate ?? null + sample_rate: track.samplingRate ?? null, + is_local: false }; } @@ -81,7 +84,8 @@ export function buildQueueTrackFromLocalTrack(track: LocalLibraryTrack): Backend artwork_url: artwork, hires: isHires, bit_depth: track.bit_depth ?? null, - sample_rate: track.sample_rate ?? null + sample_rate: track.sample_rate ?? null, + is_local: true }; } diff --git a/src/lib/stores/queueStore.ts b/src/lib/stores/queueStore.ts index a953333ed..2d2dc468a 100644 --- a/src/lib/stores/queueStore.ts +++ b/src/lib/stores/queueStore.ts @@ -27,6 +27,7 @@ export interface BackendQueueTrack { hires: boolean; bit_depth: number | null; sample_rate: number | null; + is_local?: boolean; } interface BackendQueueState { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 45a8a7207..24323ff21 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -433,7 +433,8 @@ artwork_url: artwork || null, hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, - sample_rate: t.samplingRate ?? null + sample_rate: t.samplingRate ?? null, + is_local: false })); await setQueue(queueTracks, 0, true); @@ -475,7 +476,8 @@ artwork_url: artwork || null, hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, - sample_rate: t.samplingRate ?? null + sample_rate: t.samplingRate ?? null, + is_local: false }); } showToast(`Playing ${album.tracks.length} tracks next`, 'success'); @@ -495,7 +497,8 @@ artwork_url: artwork || null, hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, - sample_rate: t.samplingRate ?? null + sample_rate: t.samplingRate ?? null, + is_local: false })); const success = await addTracksToQueue(queueTracks); @@ -603,7 +606,8 @@ artwork_url: artwork || null, hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, - sample_rate: t.samplingRate ?? null + sample_rate: t.samplingRate ?? null, + is_local: false })); // Set the queue starting at the clicked track @@ -923,7 +927,8 @@ artwork_url: artwork || null, hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, - sample_rate: t.samplingRate ?? null + sample_rate: t.samplingRate ?? null, + is_local: false }); } showToast(`Playing ${selectedAlbum.tracks.length} tracks next`, 'success'); @@ -943,7 +948,8 @@ artwork_url: artwork || null, hires: t.hires ?? false, bit_depth: t.bitDepth ?? null, - sample_rate: t.samplingRate ?? null + sample_rate: t.samplingRate ?? null, + is_local: false })); const success = await addTracksToQueue(queueTracks); @@ -1295,7 +1301,8 @@ artwork_url: t.artwork_url, hires: t.hires ?? false, bit_depth: t.bit_depth ?? null, - sample_rate: t.sample_rate ?? null + sample_rate: t.sample_rate ?? null, + is_local: t.is_local ?? false })); await setQueue(tracks, session.current_index ?? 0, true);