From bd29978b2a0d0d6def486fcdaa63bc8934d68045 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 5 May 2026 21:12:32 +0200 Subject: fix(daemon): robust auto-advance; ensure next track starts even if playback fails --- src/config.rs | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/config.rs (limited to 'src/config.rs') diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..cda59f4 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,180 @@ +use directories::ProjectDirs; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Config { + #[serde(skip)] + 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, +} + +impl Config { + pub fn load() -> Result> { + let proj_dirs = ProjectDirs::from("com", "rsplayer", "rsplayer") + .ok_or("Failed to get project directories")?; + + let config_dir = if let Ok(xdg) = std::env::var("XDG_CONFIG_HOME") { + PathBuf::from(xdg).join("rsplayer") + } else { + proj_dirs.config_dir().to_path_buf() + }; + + let config_path = config_dir.join("config.toml"); + + if !config_path.exists() { + fs::create_dir_all(&config_dir)?; + let default = Self { + 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() + }; + default.save()?; + return Ok(default); + } + + let content = fs::read_to_string(&config_path)?; + let mut config: Self = toml::from_str(&content)?; + config.config_path = config_path; + Ok(config) + } + + pub fn save(&self) -> Result<(), Box> { + let content = toml::to_string_pretty(self)?; + if let Some(parent) = self.config_path.parent() { + fs::create_dir_all(parent)?; + } + fs::write(&self.config_path, content)?; + Ok(()) + } + + pub fn get(&self, key: &str) -> Result> { + match key { + "music_paths" => Ok(self + .music_paths + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join(", ")), + "scan_interval" => Ok(self + .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() + .unwrap_or_else(|| "not set".to_string())), + "lastfm_api_secret" => Ok(self + .lastfm_api_secret + .clone() + .unwrap_or_else(|| "not set".to_string())), + "lastfm_session_key" => Ok(self + .lastfm_session_key + .clone() + .unwrap_or_else(|| "not set".to_string())), + "listenbrainz_token" => Ok(self + .listenbrainz_token + .clone() + .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())), + _ => Err(format!("Unknown setting: {}", key).into()), + } + } + + pub fn set(&mut self, key: &str, value: &str) -> Result<(), Box> { + match key { + "music_paths" => { + self.music_paths = value.split(',').map(|p| PathBuf::from(p.trim())).collect(); + } + "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()); + } + "lastfm_api_secret" => { + self.lastfm_api_secret = Some(value.to_string()); + } + "lastfm_session_key" => { + self.lastfm_session_key = Some(value.to_string()); + } + "listenbrainz_token" => { + self.listenbrainz_token = Some(value.to_string()); + } + "volume" => { + self.volume = Some(value.parse()?); + } + "shuffle" => { + if value == "on" || value == "all" || value == "album" { + self.shuffle = Some(value.to_string()); + } else if value == "off" || value == "none" { + self.shuffle = None; + } else { + return Err(format!("Invalid shuffle value: {}", value).into()); + } + } + _ => 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); + } + + 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, + _ => 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") + }) + } +} -- cgit v1.2.3