diff --git a/src-tauri/src/commands/playback.rs b/src-tauri/src/commands/playback.rs index a20e80828..ab636987a 100644 --- a/src-tauri/src/commands/playback.rs +++ b/src-tauri/src/commands/playback.rs @@ -13,16 +13,29 @@ use crate::player::PlaybackState; use crate::queue::QueueManager; use crate::AppState; +/// Convert quality string from frontend to Quality enum +fn parse_quality(quality_str: Option<&str>) -> Quality { + match quality_str { + Some("MP3") => Quality::Mp3, + Some("CD Quality") => Quality::Lossless, + Some("Hi-Res") => Quality::HiRes, + Some("Hi-Res+") => Quality::UltraHiRes, + _ => Quality::UltraHiRes, // Default to highest + } +} + /// Play a track by ID (with caching support) #[tauri::command] pub async fn play_track( track_id: u64, duration_secs: Option, + quality: Option, state: State<'_, AppState>, offline_cache: State<'_, OfflineCacheState>, audio_settings: State<'_, AudioSettingsState>, ) -> Result<(), String> { - log::info!("Command: play_track {} (duration: {:?}s)", track_id, duration_secs); + let preferred_quality = parse_quality(quality.as_deref()); + log::info!("Command: play_track {} (duration: {:?}s, quality: {:?})", track_id, duration_secs, preferred_quality); // First check offline cache (persistent disk cache) { @@ -47,6 +60,7 @@ pub async fn play_track( state.client.clone(), state.audio_cache.clone(), &state.queue, + preferred_quality, ); return Ok(()); @@ -66,6 +80,7 @@ pub async fn play_track( state.client.clone(), state.audio_cache.clone(), &state.queue, + preferred_quality, ); return Ok(()); @@ -86,6 +101,7 @@ pub async fn play_track( state.client.clone(), state.audio_cache.clone(), &state.queue, + preferred_quality, ); return Ok(()); @@ -109,9 +125,9 @@ pub async fn play_track( let client = state.client.lock().await; - // Get the stream URL with highest quality available + // Get the stream URL with preferred quality let stream_url = client - .get_stream_url_with_fallback(track_id, Quality::UltraHiRes) + .get_stream_url_with_fallback(track_id, preferred_quality) .await .map_err(|e| format!("Failed to get stream URL: {}", e))?; @@ -162,6 +178,7 @@ pub async fn play_track( state.client.clone(), state.audio_cache.clone(), &state.queue, + preferred_quality, ); return Ok(()); @@ -190,6 +207,7 @@ pub async fn play_track( state.client.clone(), state.audio_cache.clone(), &state.queue, + preferred_quality, ); Ok(()) @@ -199,10 +217,12 @@ pub async fn play_track( #[tauri::command] pub async fn prefetch_track( track_id: u64, + quality: Option, state: State<'_, AppState>, offline_cache: State<'_, OfflineCacheState>, ) -> Result<(), String> { - log::info!("Command: prefetch_track {}", track_id); + let preferred_quality = parse_quality(quality.as_deref()); + log::info!("Command: prefetch_track {} (quality: {:?})", track_id, preferred_quality); let cache = state.audio_cache.clone(); @@ -236,7 +256,7 @@ pub async fn prefetch_track( let client = state.client.lock().await; let stream_url = client - .get_stream_url_with_fallback(track_id, Quality::UltraHiRes) + .get_stream_url_with_fallback(track_id, preferred_quality) .await .map_err(|e| format!("Failed to get stream URL: {}", e))?; drop(client); @@ -578,6 +598,7 @@ fn spawn_prefetch( client: Arc>, cache: Arc, queue: &QueueManager, + quality: Quality, ) { // Look further ahead to find Qobuz tracks in mixed playlists let upcoming_tracks = queue.peek_upcoming(PREFETCH_LOOKAHEAD); @@ -631,7 +652,7 @@ fn spawn_prefetch( let result = async { let client_guard = client_clone.lock().await; let stream_url = client_guard - .get_stream_url_with_fallback(track_id, Quality::UltraHiRes) + .get_stream_url_with_fallback(track_id, quality) .await .map_err(|e| format!("Failed to get stream URL: {}", e))?; drop(client_guard); diff --git a/src/lib/services/playbackService.ts b/src/lib/services/playbackService.ts index 165ed2628..c2160fe51 100644 --- a/src/lib/services/playbackService.ts +++ b/src/lib/services/playbackService.ts @@ -10,6 +10,15 @@ */ import { invoke } from '@tauri-apps/api/core'; + +/** + * Get the preferred streaming quality from localStorage + * Returns: 'MP3', 'CD Quality', 'Hi-Res', or 'Hi-Res+' (default) + */ +function getStreamingQuality(): string { + if (typeof localStorage === 'undefined') return 'Hi-Res+'; + return localStorage.getItem('qbz-streaming-quality') || 'Hi-Res+'; +} import { setCurrentTrack, setIsPlaying, @@ -114,7 +123,11 @@ export async function playTrack( if (isLocal) { await invoke('library_play_track', { trackId: track.id }); } else { - await invoke('play_track', { trackId: track.id, durationSecs: track.duration }); + await invoke('play_track', { + trackId: track.id, + durationSecs: track.duration, + quality: getStreamingQuality() + }); } } diff --git a/src/lib/stores/playerStore.ts b/src/lib/stores/playerStore.ts index 0a1b54e53..6cb88a562 100644 --- a/src/lib/stores/playerStore.ts +++ b/src/lib/stores/playerStore.ts @@ -6,6 +6,14 @@ */ import { invoke } from '@tauri-apps/api/core'; + +/** + * Get the preferred streaming quality from localStorage + */ +function getStreamingQuality(): string { + if (typeof localStorage === 'undefined') return 'Hi-Res+'; + return localStorage.getItem('qbz-streaming-quality') || 'Hi-Res+'; +} import { listen, type UnlistenFn } from '@tauri-apps/api/event'; import { isCasting, @@ -251,7 +259,11 @@ export async function togglePlay(): Promise { await invoke('library_play_track', { trackId: localTrackId }); } else { // Qobuz track - use play_track with duration for seekbar - await invoke('play_track', { trackId: currentTrack.id, durationSecs: currentTrack.duration }); + await invoke('play_track', { + trackId: currentTrack.id, + durationSecs: currentTrack.duration, + quality: getStreamingQuality() + }); } // Seek to saved position after a short delay to let audio load