From 7f42d61cf45d77b0162c60805393ccf56169efd0 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 25 Jun 2026 14:57:00 +0200 Subject: fix -a to work properly --- src/daemon.rs | 186 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 112 insertions(+), 74 deletions(-) (limited to 'src/daemon.rs') diff --git a/src/daemon.rs b/src/daemon.rs index 1434555..61d021b 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -770,71 +770,100 @@ fn process_command( let path = std::path::PathBuf::from(query_str); - // Resolve the source track path and its album continuation tracks. - // If the argument is an existing file, extract metadata from the path - // and look up the album in the DB. Otherwise search the DB by query. - let (track_path, album_paths, display_name): (PathBuf, Vec, String) = - if path.is_file() { - let album_paths: Vec = match Config::load() { - Ok(config) => match crate::database::Database::open(&config) { - Ok(db) => db - .find_album_tracks_after_path(&path) - .unwrap_or_default() - .into_iter() - .map(|t| t.path) - .collect(), - Err(_) => Vec::new(), - }, + // Resolve the full album containing the requested track, ordered by + // track_number. The album is rotated so playback starts at the + // requested track while the *entire* album is queued (auto-advance + // wraps through any earlier tracks). If the argument is an existing + // file, derive the album from its metadata; otherwise query the DB + // by hierarchical path or free-text search. + let (album_tracks, display_name): (Vec, String) = if path.is_file() { + let canon = std::fs::canonicalize(&path).unwrap_or_else(|_| path.clone()); + let (artist, album, _, _, _) = crate::database::extract_metadata(&canon); + let full: Vec = match Config::load() { + Ok(config) => match crate::database::Database::open(&config) { + Ok(db) => db + .find_tracks_by_album(&artist, &album) + .unwrap_or_default() + .into_iter() + .map(|t| t.path) + .collect(), Err(_) => Vec::new(), - }; - let name = path - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or("unknown") - .to_string(); - (path, album_paths, name) - } else { - match Config::load() { - Ok(config) => match crate::database::Database::open(&config) { - Ok(db) => { - let tracks = match db.resolve_path(query_str) { - Ok(Some(t)) => t, - Ok(None) => match db.search_tracks(query_str) { - Ok(t) => t, - Err(e) => return format!("Database error: {}\n", e), - }, + }, + Err(_) => Vec::new(), + }; + let start_idx = full.iter().position(|p| *p == canon).unwrap_or(0); + let mut rotated: Vec = full[start_idx..].to_vec(); + rotated.extend_from_slice(&full[..start_idx]); + if rotated.is_empty() { + rotated.push(path.clone()); + } + let name = path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown") + .to_string(); + (rotated, name) + } else { + match Config::load() { + Ok(config) => match crate::database::Database::open(&config) { + Ok(db) => { + let tracks = match db.resolve_path(query_str) { + Ok(Some(t)) => t, + Ok(None) => match db.search_tracks(query_str) { + Ok(t) => t, Err(e) => return format!("Database error: {}\n", e), - }; - if tracks.is_empty() { - return format!("No tracks found matching '{}'\n", query_str); - } - let db_track = &tracks[0]; - let album_paths: Vec = if tracks.len() > 1 { - // resolve_path returned full album — use remaining tracks - tracks[1..].iter().map(|t| t.path.clone()).collect() + }, + Err(e) => return format!("Database error: {}\n", e), + }; + if tracks.is_empty() { + return format!("No tracks found matching '{}'\n", query_str); + } + let (full, display): (Vec, String) = + if tracks.len() > 1 { + // resolve_path returned the full album + // ("Artist/Album" query) — keep order. + let d = format!( + "{} - {}", + tracks[0].artist_name.as_deref().unwrap_or("unknown"), + tracks[0].title, + ); + (tracks, d) } else { - db.get_album_tracks_after( - db_track.album_id, - db_track.track_number, - &db_track.path, - ) - .unwrap_or_default() - .into_iter() - .map(|t| t.path) - .collect() + // Single track from "Artist/Album/Track" + // or a search result — fetch the whole + // album and rotate so the requested + // track plays first. + let db_track = &tracks[0]; + let full = match db.find_tracks_by_album( + db_track.artist_name.as_deref().unwrap_or(""), + db_track.album_title.as_deref().unwrap_or(""), + ) { + Ok(f) if !f.is_empty() => f, + _ => vec![db_track.clone()], + }; + let start_idx = full + .iter() + .position(|t| t.path == db_track.path) + .unwrap_or(0); + let mut rotated: Vec = + full[start_idx..].to_vec(); + rotated.extend_from_slice(&full[..start_idx]); + let d = format!( + "{} - {}", + db_track.artist_name.as_deref().unwrap_or("unknown"), + db_track.title, + ); + (rotated, d) }; - let display = format!( - "{} - {}", - db_track.artist_name.as_deref().unwrap_or("unknown"), - db_track.title, - ); - (db_track.path.clone(), album_paths, display) - } - Err(e) => return format!("Database error: {}\n", e), - }, - Err(e) => return format!("Config error: {}\n", e), - } - }; + let album_tracks: Vec = + full.iter().map(|t| t.path.clone()).collect(); + (album_tracks, display) + } + Err(e) => return format!("Database error: {}\n", e), + }, + Err(e) => return format!("Config error: {}\n", e), + } + }; if let Ok(mut q) = queue.lock() { let was_empty = q.is_empty(); @@ -843,11 +872,16 @@ fn process_command( if let Ok(mut s) = scrobble_state.lock() { try_scrobble_current(scrobbler, &mut s); } - for t in album_paths.iter().rev() { - q.insert_after_current(t.clone()); - } - q.insert_after_current(track_path.clone()); - if !was_empty { + // album_tracks[0] is the track to start at; insert the + // whole block in correct order. + if was_empty { + for t in &album_tracks { + q.add_track(t.clone()); + } + } else { + for t in album_tracks.iter().rev() { + q.insert_after_current(t.clone()); + } let new_pos = q.position() + 1; q.set_position(new_pos); } @@ -863,23 +897,27 @@ fn process_command( format!( "Playing {} with {} album track(s)\n", display_name, - album_paths.len() + album_tracks.len().saturating_sub(1) ) } "next" => { - for t in album_paths.iter().rev() { - q.insert_after_current(t.clone()); + if was_empty { + for t in &album_tracks { + q.add_track(t.clone()); + } + } else { + for t in album_tracks.iter().rev() { + q.insert_after_current(t.clone()); + } } - q.insert_after_current(track_path.clone()); format!( "Queued next: {} with {} album track(s)\n", display_name, - album_paths.len() + album_tracks.len().saturating_sub(1) ) } _ => { - q.add_track(track_path.clone()); - for t in &album_paths { + for t in &album_tracks { q.add_track(t.clone()); } if was_empty @@ -895,7 +933,7 @@ fn process_command( format!( "Queued: {} with {} album track(s)\n", display_name, - album_paths.len() + album_tracks.len().saturating_sub(1) ) } } -- cgit v1.2.3