From 09c78a14cb7dc83c7711b6e58025d43272e9eaf7 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 27 May 2026 20:26:56 +0200 Subject: play [track] [-a/--album] option if supplied when a track is played, the rest of the album will be queued after the track --- src/daemon.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) (limited to 'src/daemon.rs') diff --git a/src/daemon.rs b/src/daemon.rs index 63510ea..20f4e7e 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -681,6 +681,147 @@ fn process_command( "Error accessing queue\n".to_string() } } + _ if cmd.starts_with("play-album ") => { + let rest = &cmd[11..].trim(); + let (position, query_str): (&str, &str) = if let Some((pos, p)) = rest.split_once(' ') { + if pos == "now" || pos == "next" || pos == "last" { + (pos, p) + } else { + ("last", rest) + } + } else { + ("last", rest) + }; + + if query_str.is_empty() { + return "Usage: play-album [now|next|last] \n".to_string(); + } + + 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(), + }, + 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) => match db.search_tracks(query_str) { + Ok(tracks) if !tracks.is_empty() => { + let db_track = &tracks[0]; + let album_tracks = db + .get_album_tracks_after( + db_track.album_id, + db_track.track_number, + &db_track.path, + ) + .unwrap_or_default(); + let display = format!( + "{} - {}", + db_track.artist_name.as_deref().unwrap_or("unknown"), + db_track.title, + ); + let paths: Vec = + album_tracks.into_iter().map(|t| t.path).collect(); + (db_track.path.clone(), paths, display) + } + Ok(_) => { + return format!("No tracks found matching '{}'\n", query_str); + } + Err(e) => return format!("Database error: {}\n", e), + }, + 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(); + match position { + "now" => { + 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 { + let new_pos = q.position() + 1; + q.set_position(new_pos); + } + if let Some(track) = q.track_at(q.position()) { + audio.play(track.path.clone()); + send_now_playing(scrobbler, track); + if let Ok(mut s) = scrobble_state.lock() { + s.start(track.clone()); + } + let fmt = Config::load().ok().and_then(|c| c.display_format.clone()); + notification::notify_track(track, fmt.as_deref()); + } + format!( + "Playing {} with {} album track(s)\n", + display_name, + album_paths.len() + ) + } + "next" => { + for t in album_paths.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() + ) + } + _ => { + q.add_track(track_path.clone()); + for t in &album_paths { + q.add_track(t.clone()); + } + if was_empty + && !audio.is_playing() + && let Some(track) = q.current() + { + audio.play(track.path.clone()); + send_now_playing(scrobbler, track); + if let Ok(mut s) = scrobble_state.lock() { + s.start(track.clone()); + } + } + format!( + "Queued: {} with {} album track(s)\n", + display_name, + album_paths.len() + ) + } + } + } else { + "Error accessing queue\n".to_string() + } + } _ if cmd.starts_with("play ") => { let rest = &cmd[5..].trim(); let (position, path_str): (&str, &str) = if let Some((pos, p)) = rest.split_once(' ') { -- cgit v1.2.3