From 455ec54613230f4004a24f8d2798fb0084e00cb4 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 11 May 2026 16:25:49 +0200 Subject: Add display_format config, fix restart command, clean up config code - Add display_format option with get/set/list/unset for customizing track display - Add Track::format() supporting $ARTIST, $ALBUM, $TRACK variables - Update queue, status, notifications to use display_format - Fix restart command using wrong subcommand 'start' -> 'start-daemon' - Remove socket_path from config (now computed at runtime from XDG_RUNTIME_DIR) - Consolidate duplicate save() and save_all() into single save() - Fix redundant .clone().clone() in config.rs - Add todo item for caching config in daemon hot paths --- src/config.rs | 186 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 152 insertions(+), 34 deletions(-) (limited to 'src/config.rs') diff --git a/src/config.rs b/src/config.rs index 42573ed..5fd2f1b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,13 +9,13 @@ pub struct Config { pub config_path: PathBuf, pub music_paths: Vec, pub scan_interval: Option, - pub socket_path: Option, pub lastfm_api_key: Option, pub lastfm_api_secret: Option, pub lastfm_session_key: Option, pub listenbrainz_token: Option, pub volume: Option, pub shuffle: Option, + pub display_format: Option, } impl Config { @@ -43,8 +43,13 @@ impl Config { config_path: config_path.clone(), music_paths: vec![dirs::audio_dir().unwrap_or_else(|| PathBuf::from("~/Music"))], scan_interval: Some(3600), - socket_path: None, - ..Default::default() + lastfm_api_key: None, + lastfm_api_secret: None, + lastfm_session_key: None, + listenbrainz_token: None, + volume: Some(50), + shuffle: None, + display_format: Some(String::from("$ARTIST - $TRACK")), }; default.save()?; return Ok(default); @@ -53,15 +58,72 @@ impl Config { let content = fs::read_to_string(&config_path)?; let mut config: Self = toml::from_str(&content)?; config.config_path = config_path; + if config.music_paths.is_empty() { + config.music_paths = + vec![dirs::audio_dir().unwrap_or_else(|| PathBuf::from("~/Music"))]; + } + if config.scan_interval.is_none() { + config.scan_interval = Some(3600); + } + config.save()?; Ok(config) } pub fn save(&self) -> Result<(), Box> { - let content = toml::to_string_pretty(self)?; + let mut output = String::new(); + let paths_str = self + .music_paths + .iter() + .map(|p| format!("\"{}\"", p.display())) + .collect::>() + .join(", "); + output.push_str(&format!("music_paths = [{}]\n", paths_str)); + output.push_str(&format!( + "scan_interval = {}\n", + self.scan_interval.unwrap_or(3600) + )); + let api_key = self + .lastfm_api_key + .as_ref() + .filter(|s| !s.is_empty()) + .map(|s| s.as_str()) + .unwrap_or(""); + output.push_str(&format!("lastfm_api_key = \"{}\"\n", api_key)); + let api_secret = self + .lastfm_api_secret + .as_ref() + .filter(|s| !s.is_empty()) + .map(|s| s.as_str()) + .unwrap_or(""); + output.push_str(&format!("lastfm_api_secret = \"{}\"\n", api_secret)); + let session_key = self + .lastfm_session_key + .as_ref() + .filter(|s| !s.is_empty()) + .map(|s| s.as_str()) + .unwrap_or(""); + output.push_str(&format!("lastfm_session_key = \"{}\"\n", session_key)); + let lb_token = self + .listenbrainz_token + .as_ref() + .filter(|s| !s.is_empty()) + .map(|s| s.as_str()) + .unwrap_or(""); + output.push_str(&format!("listenbrainz_token = \"{}\"\n", lb_token)); + output.push_str(&format!("volume = {}\n", self.volume.unwrap_or(0))); + let shuffle = self + .shuffle + .as_ref() + .filter(|s| !s.is_empty()) + .map(|s| s.as_str()) + .unwrap_or(""); + output.push_str(&format!("shuffle = \"{}\"\n", shuffle)); + let fmt = self.display_format.clone().unwrap_or_default(); + output.push_str(&format!("display_format = \"{}\"\n", fmt)); if let Some(parent) = self.config_path.parent() { fs::create_dir_all(parent)?; } - fs::write(&self.config_path, content)?; + fs::write(&self.config_path, output)?; Ok(()) } @@ -77,32 +139,39 @@ impl Config { .scan_interval .map(|i| i.to_string()) .unwrap_or_else(|| "not set".to_string())), - "socket_path" => Ok(self - .socket_path - .as_ref() - .map(|p| p.display().to_string()) - .unwrap_or_else(|| "default".to_string())), "lastfm_api_key" => Ok(self .lastfm_api_key - .clone() + .as_ref() + .filter(|s| !s.is_empty()) + .map(|_| "".to_string()) .unwrap_or_else(|| "not set".to_string())), "lastfm_api_secret" => Ok(self .lastfm_api_secret - .clone() + .as_ref() + .filter(|s| !s.is_empty()) + .map(|_| "".to_string()) .unwrap_or_else(|| "not set".to_string())), "lastfm_session_key" => Ok(self .lastfm_session_key - .clone() + .as_ref() + .filter(|s| !s.is_empty()) + .map(|_| "".to_string()) .unwrap_or_else(|| "not set".to_string())), "listenbrainz_token" => Ok(self .listenbrainz_token - .clone() + .as_ref() + .filter(|s| !s.is_empty()) + .map(|_| "".to_string()) .unwrap_or_else(|| "not set".to_string())), "volume" => Ok(self .volume .map(|v| v.to_string()) .unwrap_or_else(|| "not set".to_string())), "shuffle" => Ok(self.shuffle.clone().unwrap_or_else(|| "off".to_string())), + "display_format" => Ok(self + .display_format + .clone() + .unwrap_or_else(|| "not set".to_string())), _ => Err(format!("Unknown setting: {}", key).into()), } } @@ -115,9 +184,6 @@ impl Config { "scan_interval" => { self.scan_interval = Some(value.parse()?); } - "socket_path" => { - self.socket_path = Some(PathBuf::from(value)); - } "lastfm_api_key" => { self.lastfm_api_key = Some(value.to_string()); } @@ -142,45 +208,97 @@ impl Config { return Err(format!("Invalid shuffle value: {}", value).into()); } } + "display_format" => { + self.display_format = Some(value.to_string()); + } _ => return Err(format!("Unknown setting: {}", key).into()), } Ok(()) } pub fn list(&self) { - println!("music_paths = {:?}", self.music_paths); - println!("scan_interval = {:?}", self.scan_interval); - println!("socket_path = {:?}", self.socket_path); - println!("lastfm_api_key = {:?}", self.lastfm_api_key); - println!("lastfm_api_secret = {:?}", self.lastfm_api_secret); - println!("lastfm_session_key = {:?}", self.lastfm_session_key); - println!("listenbrainz_token = {:?}", self.listenbrainz_token); - println!("volume = {:?}", self.volume); - println!("shuffle = {:?}", self.shuffle); + println!( + "music_paths = {}", + self.music_paths + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ") + ); + println!( + "scan_interval = {}", + self.scan_interval + .map_or("not set".to_string(), |v| v.to_string()) + ); + println!( + "lastfm_api_key = {}", + self.lastfm_api_key + .as_ref() + .filter(|s| !s.is_empty()) + .map_or("not set".to_string(), |_| "".to_string()) + ); + println!( + "lastfm_api_secret = {}", + self.lastfm_api_secret + .as_ref() + .filter(|s| !s.is_empty()) + .map_or("not set".to_string(), |_| "".to_string()) + ); + println!( + "lastfm_session_key = {}", + self.lastfm_session_key + .as_ref() + .filter(|s| !s.is_empty()) + .map_or("not set".to_string(), |_| "".to_string()) + ); + println!( + "listenbrainz_token = {}", + self.listenbrainz_token + .as_ref() + .filter(|s| !s.is_empty()) + .map_or("not set".to_string(), |_| "".to_string()) + ); + println!( + "volume = {}", + self.volume + .filter(|&v| v != 0) + .map_or("not set".to_string(), |v| v.to_string()) + ); + println!( + "shuffle = {}", + self.shuffle + .clone() + .filter(|s| !s.is_empty()) + .unwrap_or_else(|| "off".to_string()) + ); + println!( + "display_format = {}", + self.display_format + .clone() + .unwrap_or_else(|| "not set".to_string()) + ); } pub fn unset(&mut self, key: &str) -> Result<(), Box> { match key { "music_paths" => self.music_paths.clear(), "scan_interval" => self.scan_interval = None, - "socket_path" => self.socket_path = None, "lastfm_api_key" => self.lastfm_api_key = None, "lastfm_api_secret" => self.lastfm_api_secret = None, "lastfm_session_key" => self.lastfm_session_key = None, "listenbrainz_token" => self.listenbrainz_token = None, "volume" => self.volume = None, "shuffle" => self.shuffle = None, + "display_format" => self.display_format = None, _ => return Err(format!("Unknown setting: {}", key).into()), } Ok(()) } - pub fn get_socket_path(&self) -> PathBuf { - self.socket_path.clone().unwrap_or_else(|| { - let runtime_dir = std::env::var("XDG_RUNTIME_DIR") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from("/tmp")); - runtime_dir.join("rsplayer").join("daemon.sock") - }) + pub fn get_socket_path() -> PathBuf { + let runtime_dir = std::env::var("XDG_RUNTIME_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| PathBuf::from("/tmp")); + runtime_dir.join("rsplayer").join("daemon.sock") } } -- cgit v1.2.3