diff options
| author | salmonik <raisersoft@proton.me> | 2026-05-27 20:26:56 +0200 |
|---|---|---|
| committer | salmonik <raisersoft@proton.me> | 2026-05-27 20:26:56 +0200 |
| commit | 09c78a14cb7dc83c7711b6e58025d43272e9eaf7 (patch) | |
| tree | e5eec691695c3beff320a2a2a9c2d60c2524ec55 /src/daemon.rs | |
| parent | add licensing (diff) | |
| download | rsplayer-09c78a14cb7dc83c7711b6e58025d43272e9eaf7.tar.gz rsplayer-09c78a14cb7dc83c7711b6e58025d43272e9eaf7.zip | |
play [track] [-a/--album] option
if supplied when a track is played, the rest of the album will be queued
after the track
Diffstat (limited to '')
| -rw-r--r-- | src/daemon.rs | 141 |
1 files changed, 141 insertions, 0 deletions
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( | |||
| 681 | "Error accessing queue\n".to_string() | 681 | "Error accessing queue\n".to_string() |
| 682 | } | 682 | } |
| 683 | } | 683 | } |
| 684 | _ if cmd.starts_with("play-album ") => { | ||
| 685 | let rest = &cmd[11..].trim(); | ||
| 686 | let (position, query_str): (&str, &str) = if let Some((pos, p)) = rest.split_once(' ') { | ||
| 687 | if pos == "now" || pos == "next" || pos == "last" { | ||
| 688 | (pos, p) | ||
| 689 | } else { | ||
| 690 | ("last", rest) | ||
| 691 | } | ||
| 692 | } else { | ||
| 693 | ("last", rest) | ||
| 694 | }; | ||
| 695 | |||
| 696 | if query_str.is_empty() { | ||
| 697 | return "Usage: play-album [now|next|last] <path_or_query>\n".to_string(); | ||
| 698 | } | ||
| 699 | |||
| 700 | let path = std::path::PathBuf::from(query_str); | ||
| 701 | |||
| 702 | // Resolve the source track path and its album continuation tracks. | ||
| 703 | // If the argument is an existing file, extract metadata from the path | ||
| 704 | // and look up the album in the DB. Otherwise search the DB by query. | ||
| 705 | let (track_path, album_paths, display_name): (PathBuf, Vec<PathBuf>, String) = | ||
| 706 | if path.is_file() { | ||
| 707 | let album_paths: Vec<PathBuf> = match Config::load() { | ||
| 708 | Ok(config) => match crate::database::Database::open(&config) { | ||
| 709 | Ok(db) => db | ||
| 710 | .find_album_tracks_after_path(&path) | ||
| 711 | .unwrap_or_default() | ||
| 712 | .into_iter() | ||
| 713 | .map(|t| t.path) | ||
| 714 | .collect(), | ||
| 715 | Err(_) => Vec::new(), | ||
| 716 | }, | ||
| 717 | Err(_) => Vec::new(), | ||
| 718 | }; | ||
| 719 | let name = path | ||
| 720 | .file_name() | ||
| 721 | .and_then(|n| n.to_str()) | ||
| 722 | .unwrap_or("unknown") | ||
| 723 | .to_string(); | ||
| 724 | (path, album_paths, name) | ||
| 725 | } else { | ||
| 726 | match Config::load() { | ||
| 727 | Ok(config) => match crate::database::Database::open(&config) { | ||
| 728 | Ok(db) => match db.search_tracks(query_str) { | ||
| 729 | Ok(tracks) if !tracks.is_empty() => { | ||
| 730 | let db_track = &tracks[0]; | ||
| 731 | let album_tracks = db | ||
| 732 | .get_album_tracks_after( | ||
| 733 | db_track.album_id, | ||
| 734 | db_track.track_number, | ||
| 735 | &db_track.path, | ||
| 736 | ) | ||
| 737 | .unwrap_or_default(); | ||
| 738 | let display = format!( | ||
| 739 | "{} - {}", | ||
| 740 | db_track.artist_name.as_deref().unwrap_or("unknown"), | ||
| 741 | db_track.title, | ||
| 742 | ); | ||
| 743 | let paths: Vec<PathBuf> = | ||
| 744 | album_tracks.into_iter().map(|t| t.path).collect(); | ||
| 745 | (db_track.path.clone(), paths, display) | ||
| 746 | } | ||
| 747 | Ok(_) => { | ||
| 748 | return format!("No tracks found matching '{}'\n", query_str); | ||
| 749 | } | ||
| 750 | Err(e) => return format!("Database error: {}\n", e), | ||
| 751 | }, | ||
| 752 | Err(e) => return format!("Database error: {}\n", e), | ||
| 753 | }, | ||
| 754 | Err(e) => return format!("Config error: {}\n", e), | ||
| 755 | } | ||
| 756 | }; | ||
| 757 | |||
| 758 | if let Ok(mut q) = queue.lock() { | ||
| 759 | let was_empty = q.is_empty(); | ||
| 760 | match position { | ||
| 761 | "now" => { | ||
| 762 | if let Ok(mut s) = scrobble_state.lock() { | ||
| 763 | try_scrobble_current(scrobbler, &mut s); | ||
| 764 | } | ||
| 765 | for t in album_paths.iter().rev() { | ||
| 766 | q.insert_after_current(t.clone()); | ||
| 767 | } | ||
| 768 | q.insert_after_current(track_path.clone()); | ||
| 769 | if !was_empty { | ||
| 770 | let new_pos = q.position() + 1; | ||
| 771 | q.set_position(new_pos); | ||
| 772 | } | ||
| 773 | if let Some(track) = q.track_at(q.position()) { | ||
| 774 | audio.play(track.path.clone()); | ||
| 775 | send_now_playing(scrobbler, track); | ||
| 776 | if let Ok(mut s) = scrobble_state.lock() { | ||
| 777 | s.start(track.clone()); | ||
| 778 | } | ||
| 779 | let fmt = Config::load().ok().and_then(|c| c.display_format.clone()); | ||
| 780 | notification::notify_track(track, fmt.as_deref()); | ||
| 781 | } | ||
| 782 | format!( | ||
| 783 | "Playing {} with {} album track(s)\n", | ||
| 784 | display_name, | ||
| 785 | album_paths.len() | ||
| 786 | ) | ||
| 787 | } | ||
| 788 | "next" => { | ||
| 789 | for t in album_paths.iter().rev() { | ||
| 790 | q.insert_after_current(t.clone()); | ||
| 791 | } | ||
| 792 | q.insert_after_current(track_path.clone()); | ||
| 793 | format!( | ||
| 794 | "Queued next: {} with {} album track(s)\n", | ||
| 795 | display_name, | ||
| 796 | album_paths.len() | ||
| 797 | ) | ||
| 798 | } | ||
| 799 | _ => { | ||
| 800 | q.add_track(track_path.clone()); | ||
| 801 | for t in &album_paths { | ||
| 802 | q.add_track(t.clone()); | ||
| 803 | } | ||
| 804 | if was_empty | ||
| 805 | && !audio.is_playing() | ||
| 806 | && let Some(track) = q.current() | ||
| 807 | { | ||
| 808 | audio.play(track.path.clone()); | ||
| 809 | send_now_playing(scrobbler, track); | ||
| 810 | if let Ok(mut s) = scrobble_state.lock() { | ||
| 811 | s.start(track.clone()); | ||
| 812 | } | ||
| 813 | } | ||
| 814 | format!( | ||
| 815 | "Queued: {} with {} album track(s)\n", | ||
| 816 | display_name, | ||
| 817 | album_paths.len() | ||
| 818 | ) | ||
| 819 | } | ||
| 820 | } | ||
| 821 | } else { | ||
| 822 | "Error accessing queue\n".to_string() | ||
| 823 | } | ||
| 824 | } | ||
| 684 | _ if cmd.starts_with("play ") => { | 825 | _ if cmd.starts_with("play ") => { |
| 685 | let rest = &cmd[5..].trim(); | 826 | let rest = &cmd[5..].trim(); |
| 686 | let (position, path_str): (&str, &str) = if let Some((pos, p)) = rest.split_once(' ') { | 827 | let (position, path_str): (&str, &str) = if let Some((pos, p)) = rest.split_once(' ') { |
