Fix buffering UX when transitioning from local to Qobuz tracks

- Stop playback immediately when starting a Qobuz track to prevent
  previous track from continuing during download
- Show buffering toast while downloading Qobuz audio
- Skip buffering UI for local tracks since they load instantly
- Add is_local field to QueueTrack for prefetch optimization
- Update prefetch to skip local tracks (no network needed)
- Update all queue track builders to include is_local flag
This commit is contained in:
vicrodh committed 2026-01-16 07:58:13 -06:00
1 parent 3e3f89b6a8
commit 94d7d78a22
7 files changed
+50 -12

No files matched your search

+6
View File
@@ -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);
+3
View File
@@ -23,6 +23,9 @@ pub struct QueueTrack {
pub hires: bool,
pub bit_depth: Option<u32>,
pub sample_rate: Option<f64>,
/// Whether this is a local library track (not from Qobuz)
#[serde(default)]
pub is_local: bool,
}
/// Repeat mode options
@@ -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
+15 -1
View File
@@ -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');
}
+8 -4
View File
@@ -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
};
}
+1
View File
@@ -27,6 +27,7 @@ export interface BackendQueueTrack {
hires: boolean;
bit_depth: number | null;
sample_rate: number | null;
is_local?: boolean;
}
interface BackendQueueState {
+14 -7
View File
@@ -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);