aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-06-25 14:57:00 +0200
committersalmonik <raisersoft@proton.me>2026-06-25 14:57:00 +0200
commit7f42d61cf45d77b0162c60805393ccf56169efd0 (patch)
tree989530f3e0de1bb780f22739bf3351594ae27883
parentI love \r (diff)
downloadrsplayer-7f42d61cf45d77b0162c60805393ccf56169efd0.tar.gz
rsplayer-7f42d61cf45d77b0162c60805393ccf56169efd0.zip
fix -a to work properly
-rw-r--r--src/cli.rs2
-rw-r--r--src/daemon.rs186
2 files changed, 113 insertions, 75 deletions
diff --git a/src/cli.rs b/src/cli.rs
index de342e4..87d9003 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -36,7 +36,7 @@ pub enum Commands {
36 /// Add to queue after current track (don't skip) 36 /// Add to queue after current track (don't skip)
37 #[arg(short = 'n', long = "next")] 37 #[arg(short = 'n', long = "next")]
38 next: bool, 38 next: bool,
39 /// Queue rest of album after this track (requires a track query) 39 /// Queue the entire album containing this track (start at this track)
40 #[arg(short = 'a', long = "album")] 40 #[arg(short = 'a', long = "album")]
41 album: bool, 41 album: bool,
42 #[arg(value_name = "QUERY")] 42 #[arg(value_name = "QUERY")]
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(
770 770
771 let path = std::path::PathBuf::from(query_str); 771 let path = std::path::PathBuf::from(query_str);
772 772
773 // Resolve the source track path and its album continuation tracks. 773 // Resolve the full album containing the requested track, ordered by
774 // If the argument is an existing file, extract metadata from the path 774 // track_number. The album is rotated so playback starts at the
775 // and look up the album in the DB. Otherwise search the DB by query. 775 // requested track while the *entire* album is queued (auto-advance
776 let (track_path, album_paths, display_name): (PathBuf, Vec<PathBuf>, String) = 776 // wraps through any earlier tracks). If the argument is an existing
777 if path.is_file() { 777 // file, derive the album from its metadata; otherwise query the DB
778 let album_paths: Vec<PathBuf> = match Config::load() { 778 // by hierarchical path or free-text search.
779 Ok(config) => match crate::database::Database::open(&config) { 779 let (album_tracks, display_name): (Vec<PathBuf>, String) = if path.is_file() {
780 Ok(db) => db 780 let canon = std::fs::canonicalize(&path).unwrap_or_else(|_| path.clone());
781 .find_album_tracks_after_path(&path) 781 let (artist, album, _, _, _) = crate::database::extract_metadata(&canon);
782 .unwrap_or_default() 782 let full: Vec<PathBuf> = match Config::load() {
783 .into_iter() 783 Ok(config) => match crate::database::Database::open(&config) {
784 .map(|t| t.path) 784 Ok(db) => db
785 .collect(), 785 .find_tracks_by_album(&artist, &album)
786 Err(_) => Vec::new(), 786 .unwrap_or_default()
787 }, 787 .into_iter()
788 .map(|t| t.path)
789 .collect(),
788 Err(_) => Vec::new(), 790 Err(_) => Vec::new(),
789 }; 791 },
790 let name = path 792 Err(_) => Vec::new(),
791 .file_name() 793 };
792 .and_then(|n| n.to_str()) 794 let start_idx = full.iter().position(|p| *p == canon).unwrap_or(0);
793 .unwrap_or("unknown") 795 let mut rotated: Vec<PathBuf> = full[start_idx..].to_vec();
794 .to_string(); 796 rotated.extend_from_slice(&full[..start_idx]);
795 (path, album_paths, name) 797 if rotated.is_empty() {
796 } else { 798 rotated.push(path.clone());
797 match Config::load() { 799 }
798 Ok(config) => match crate::database::Database::open(&config) { 800 let name = path
799 Ok(db) => { 801 .file_name()
800 let tracks = match db.resolve_path(query_str) { 802 .and_then(|n| n.to_str())
801 Ok(Some(t)) => t, 803 .unwrap_or("unknown")
802 Ok(None) => match db.search_tracks(query_str) { 804 .to_string();
803 Ok(t) => t, 805 (rotated, name)
804 Err(e) => return format!("Database error: {}\n", e), 806 } else {
805 }, 807 match Config::load() {
808 Ok(config) => match crate::database::Database::open(&config) {
809 Ok(db) => {
810 let tracks = match db.resolve_path(query_str) {
811 Ok(Some(t)) => t,
812 Ok(None) => match db.search_tracks(query_str) {
813 Ok(t) => t,
806 Err(e) => return format!("Database error: {}\n", e), 814 Err(e) => return format!("Database error: {}\n", e),
807 }; 815 },
808 if tracks.is_empty() { 816 Err(e) => return format!("Database error: {}\n", e),
809 return format!("No tracks found matching '{}'\n", query_str); 817 };
810 } 818 if tracks.is_empty() {
811 let db_track = &tracks[0]; 819 return format!("No tracks found matching '{}'\n", query_str);
812 let album_paths: Vec<PathBuf> = if tracks.len() > 1 { 820 }
813 // resolve_path returned full album — use remaining tracks 821 let (full, display): (Vec<crate::database::Track>, String) =
814 tracks[1..].iter().map(|t| t.path.clone()).collect() 822 if tracks.len() > 1 {
823 // resolve_path returned the full album
824 // ("Artist/Album" query) — keep order.
825 let d = format!(
826 "{} - {}",
827 tracks[0].artist_name.as_deref().unwrap_or("unknown"),
828 tracks[0].title,
829 );
830 (tracks, d)
815 } else { 831 } else {
816 db.get_album_tracks_after( 832 // Single track from "Artist/Album/Track"
817 db_track.album_id, 833 // or a search result — fetch the whole
818 db_track.track_number, 834 // album and rotate so the requested
819 &db_track.path, 835 // track plays first.
820 ) 836 let db_track = &tracks[0];
821 .unwrap_or_default() 837 let full = match db.find_tracks_by_album(
822 .into_iter() 838 db_track.artist_name.as_deref().unwrap_or(""),
823 .map(|t| t.path) 839 db_track.album_title.as_deref().unwrap_or(""),
824 .collect() 840 ) {
841 Ok(f) if !f.is_empty() => f,
842 _ => vec![db_track.clone()],
843 };
844 let start_idx = full
845 .iter()
846 .position(|t| t.path == db_track.path)
847 .unwrap_or(0);
848 let mut rotated: Vec<crate::database::Track> =
849 full[start_idx..].to_vec();
850 rotated.extend_from_slice(&full[..start_idx]);
851 let d = format!(
852 "{} - {}",
853 db_track.artist_name.as_deref().unwrap_or("unknown"),
854 db_track.title,
855 );
856 (rotated, d)
825 }; 857 };
826 let display = format!( 858 let album_tracks: Vec<PathBuf> =
827 "{} - {}", 859 full.iter().map(|t| t.path.clone()).collect();
828 db_track.artist_name.as_deref().unwrap_or("unknown"), 860 (album_tracks, display)
829 db_track.title, 861 }
830 ); 862 Err(e) => return format!("Database error: {}\n", e),
831 (db_track.path.clone(), album_paths, display) 863 },
832 } 864 Err(e) => return format!("Config error: {}\n", e),
833 Err(e) => return format!("Database error: {}\n", e), 865 }
834 }, 866 };
835 Err(e) => return format!("Config error: {}\n", e),
836 }
837 };
838 867
839 if let Ok(mut q) = queue.lock() { 868 if let Ok(mut q) = queue.lock() {
840 let was_empty = q.is_empty(); 869 let was_empty = q.is_empty();
@@ -843,11 +872,16 @@ fn process_command(
843 if let Ok(mut s) = scrobble_state.lock() { 872 if let Ok(mut s) = scrobble_state.lock() {
844 try_scrobble_current(scrobbler, &mut s); 873 try_scrobble_current(scrobbler, &mut s);
845 } 874 }
846 for t in album_paths.iter().rev() { 875 // album_tracks[0] is the track to start at; insert the
847 q.insert_after_current(t.clone()); 876 // whole block in correct order.
848 } 877 if was_empty {
849 q.insert_after_current(track_path.clone()); 878 for t in &album_tracks {
850 if !was_empty { 879 q.add_track(t.clone());
880 }
881 } else {
882 for t in album_tracks.iter().rev() {
883 q.insert_after_current(t.clone());
884 }
851 let new_pos = q.position() + 1; 885 let new_pos = q.position() + 1;
852 q.set_position(new_pos); 886 q.set_position(new_pos);
853 } 887 }
@@ -863,23 +897,27 @@ fn process_command(
863 format!( 897 format!(
864 "Playing {} with {} album track(s)\n", 898 "Playing {} with {} album track(s)\n",
865 display_name, 899 display_name,
866 album_paths.len() 900 album_tracks.len().saturating_sub(1)
867 ) 901 )
868 } 902 }
869 "next" => { 903 "next" => {
870 for t in album_paths.iter().rev() { 904 if was_empty {
871 q.insert_after_current(t.clone()); 905 for t in &album_tracks {
906 q.add_track(t.clone());
907 }
908 } else {
909 for t in album_tracks.iter().rev() {
910 q.insert_after_current(t.clone());
911 }
872 } 912 }
873 q.insert_after_current(track_path.clone());
874 format!( 913 format!(
875 "Queued next: {} with {} album track(s)\n", 914 "Queued next: {} with {} album track(s)\n",
876 display_name, 915 display_name,
877 album_paths.len() 916 album_tracks.len().saturating_sub(1)
878 ) 917 )
879 } 918 }
880 _ => { 919 _ => {
881 q.add_track(track_path.clone()); 920 for t in &album_tracks {
882 for t in &album_paths {
883 q.add_track(t.clone()); 921 q.add_track(t.clone());
884 } 922 }
885 if was_empty 923 if was_empty
@@ -895,7 +933,7 @@ fn process_command(
895 format!( 933 format!(
896 "Queued: {} with {} album track(s)\n", 934 "Queued: {} with {} album track(s)\n",
897 display_name, 935 display_name,
898 album_paths.len() 936 album_tracks.len().saturating_sub(1)
899 ) 937 )
900 } 938 }
901 } 939 }