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:
1 parent
3e3f89b6a8
commit
94d7d78a22
7 files changed
+50
-12
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface BackendQueueTrack {
|
||||
hires: boolean;
|
||||
bit_depth: number | null;
|
||||
sample_rate: number | null;
|
||||
is_local?: boolean;
|
||||
}
|
||||
|
||||
interface BackendQueueState {
|
||||
|
||||
+14
-7
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user