aboutsummaryrefslogtreecommitdiffstats
path: root/src/daemon.rs
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-05-11 16:25:49 +0200
committersalmonik <raisersoft@proton.me>2026-05-11 16:25:49 +0200
commit455ec54613230f4004a24f8d2798fb0084e00cb4 (patch)
tree5ffb7606a11ebf33ea940f068ed046bfda0f0c54 /src/daemon.rs
parentFix shuffle to not shuffle current track - only shuffle tracks after current ... (diff)
downloadrsplayer-455ec54613230f4004a24f8d2798fb0084e00cb4.tar.gz
rsplayer-455ec54613230f4004a24f8d2798fb0084e00cb4.zip
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
Diffstat (limited to '')
-rw-r--r--src/daemon.rs66
1 files changed, 50 insertions, 16 deletions
diff --git a/src/daemon.rs b/src/daemon.rs
index b79e67a..acc7729 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -12,7 +12,7 @@ use std::time::Duration;
12 12
13pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> { 13pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
14 let config = Config::load()?; 14 let config = Config::load()?;
15 let socket_path = config.get_socket_path(); 15 let socket_path = Config::get_socket_path();
16 let pid_path = get_pid_path(&config); 16 let pid_path = get_pid_path(&config);
17 17
18 // Check if daemon is already running 18 // Check if daemon is already running
@@ -55,7 +55,6 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
55 let queue = Arc::new(Mutex::new(Queue::new())); 55 let queue = Arc::new(Mutex::new(Queue::new()));
56 56
57 // Load and apply saved settings 57 // Load and apply saved settings
58 let config = Config::load().unwrap_or_default();
59 if let Some(vol) = config.volume { 58 if let Some(vol) = config.volume {
60 audio_engine.set_volume(vol as f32 / 100.0); 59 audio_engine.set_volume(vol as f32 / 100.0);
61 } 60 }
@@ -69,6 +68,9 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
69 } 68 }
70 } 69 }
71 70
71 // Save full config
72 config.save()?;
73
72 // Accept connections until shutdown 74 // Accept connections until shutdown
73 listener.set_nonblocking(true)?; 75 listener.set_nonblocking(true)?;
74 // Helper: poll until a specific track actually starts playing or timeout 76 // Helper: poll until a specific track actually starts playing or timeout
@@ -106,7 +108,8 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
106 audio_engine.play(next_track.clone()); 108 audio_engine.play(next_track.clone());
107 if wait_for_start(&audio_engine, &next_track, 500) { 109 if wait_for_start(&audio_engine, &next_track, 500) {
108 started = true; 110 started = true;
109 notification::notify_track(&Track::from_path(next_track)); 111 let fmt = Config::load().ok().and_then(|c| c.display_format.clone());
112 notification::notify_track(&Track::from_path(next_track), fmt.as_deref());
110 break; 113 break;
111 } 114 }
112 } else { 115 } else {
@@ -182,7 +185,7 @@ pub fn stop_daemon() -> Result<(), Box<dyn std::error::Error>> {
182 } 185 }
183 186
184 // Clean up files 187 // Clean up files
185 let socket_path = config.get_socket_path(); 188 let socket_path = Config::get_socket_path();
186 std::fs::remove_file(&socket_path).ok(); 189 std::fs::remove_file(&socket_path).ok();
187 std::fs::remove_file(&pid_path).ok(); 190 std::fs::remove_file(&pid_path).ok();
188 191
@@ -203,7 +206,7 @@ pub fn restart_daemon() -> Result<(), Box<dyn std::error::Error>> {
203 // Start new daemon in background 206 // Start new daemon in background
204 let current_exe = std::env::current_exe()?; 207 let current_exe = std::env::current_exe()?;
205 std::process::Command::new(current_exe) 208 std::process::Command::new(current_exe)
206 .arg("start") 209 .arg("start-daemon")
207 .spawn()?; 210 .spawn()?;
208 211
209 println!("Daemon restarted"); 212 println!("Daemon restarted");
@@ -216,8 +219,7 @@ pub fn send_command(cmd: String) -> Result<(), Box<dyn std::error::Error>> {
216} 219}
217 220
218fn send_command_raw(cmd: String) -> Result<(), Box<dyn std::error::Error>> { 221fn send_command_raw(cmd: String) -> Result<(), Box<dyn std::error::Error>> {
219 let config = Config::load()?; 222 let socket_path = Config::get_socket_path();
220 let socket_path = config.get_socket_path();
221 223
222 let mut stream = UnixStream::connect(socket_path)?; 224 let mut stream = UnixStream::connect(socket_path)?;
223 stream.write_all(cmd.as_bytes())?; 225 stream.write_all(cmd.as_bytes())?;
@@ -254,7 +256,11 @@ fn process_command(
254 if audio.is_playing() { 256 if audio.is_playing() {
255 if let Some(track_path) = audio.current_track() { 257 if let Some(track_path) = audio.current_track() {
256 let track = Track::from_path(track_path); 258 let track = Track::from_path(track_path);
257 let track_str = track.display_string(0, 1); 259 let fmt = Config::load().ok().and_then(|c| c.display_format.clone());
260 let track_str = match fmt.as_deref() {
261 Some(f) => track.format(f),
262 None => track.display_string(0, 1),
263 };
258 format!("Playing\nTrack: {}\n", track_str) 264 format!("Playing\nTrack: {}\n", track_str)
259 } else { 265 } else {
260 "Playing\n".to_string() 266 "Playing\n".to_string()
@@ -296,7 +302,11 @@ fn process_command(
296 if let Ok(mut q) = queue.lock() { 302 if let Ok(mut q) = queue.lock() {
297 if let Some(next_track) = q.next() { 303 if let Some(next_track) = q.next() {
298 audio.play(next_track.clone()); 304 audio.play(next_track.clone());
299 notification::notify_track(&Track::from_path(next_track)); 305 let fmt = Config::load().ok().and_then(|c| c.display_format.clone());
306 notification::notify_track(
307 &Track::from_path(next_track.clone()),
308 fmt.as_deref(),
309 );
300 "Playing next track\n".to_string() 310 "Playing next track\n".to_string()
301 } else { 311 } else {
302 audio.stop(); 312 audio.stop();
@@ -311,7 +321,11 @@ fn process_command(
311 if let Ok(mut q) = queue.lock() { 321 if let Ok(mut q) = queue.lock() {
312 if let Some(prev_track) = q.previous() { 322 if let Some(prev_track) = q.previous() {
313 audio.play(prev_track.clone()); 323 audio.play(prev_track.clone());
314 notification::notify_track(&Track::from_path(prev_track)); 324 let fmt = Config::load().ok().and_then(|c| c.display_format.clone());
325 notification::notify_track(
326 &Track::from_path(prev_track.clone()),
327 fmt.as_deref(),
328 );
315 "Playing previous track\n".to_string() 329 "Playing previous track\n".to_string()
316 } else { 330 } else {
317 "No previous track\n".to_string() 331 "No previous track\n".to_string()
@@ -371,7 +385,13 @@ fn process_command(
371 } else { 385 } else {
372 ("last", rest) 386 ("last", rest)
373 }; 387 };
374 let path_str = path_str.to_string(); 388 let path_str = if path_str.starts_with("~/") {
389 std::env::var("HOME")
390 .map(|h| path_str.replacen("~", &h, 1))
391 .unwrap_or_else(|_| path_str.to_string())
392 } else {
393 path_str.to_string()
394 };
375 395
376 if path_str.is_empty() { 396 if path_str.is_empty() {
377 return "Usage: play [now|next|last] <path>\n".to_string(); 397 return "Usage: play [now|next|last] <path>\n".to_string();
@@ -390,7 +410,9 @@ fn process_command(
390 } 410 }
391 if let Some(track) = q.track_at(q.position()) { 411 if let Some(track) = q.track_at(q.position()) {
392 audio.play(track.path.clone()); 412 audio.play(track.path.clone());
393 notification::notify_track(track); 413 let fmt =
414 Config::load().ok().and_then(|c| c.display_format.clone());
415 notification::notify_track(track, fmt.as_deref());
394 } 416 }
395 format!("Playing now: {}\n", path_str) 417 format!("Playing now: {}\n", path_str)
396 } 418 }
@@ -405,7 +427,9 @@ fn process_command(
405 if was_empty && !was_playing { 427 if was_empty && !was_playing {
406 let track = Track::from_path(path.clone()); 428 let track = Track::from_path(path.clone());
407 audio.play(path); 429 audio.play(path);
408 notification::notify_track(&track); 430 let fmt =
431 Config::load().ok().and_then(|c| c.display_format.clone());
432 notification::notify_track(&track, fmt.as_deref());
409 format!("Playing: {}\n", path_str) 433 format!("Playing: {}\n", path_str)
410 } else { 434 } else {
411 format!("Queued: {}\n", path_str) 435 format!("Queued: {}\n", path_str)
@@ -431,7 +455,10 @@ fn process_command(
431 } 455 }
432 if let Some(track) = q.track_at(q.position()) { 456 if let Some(track) = q.track_at(q.position()) {
433 audio.play(track.path.clone()); 457 audio.play(track.path.clone());
434 notification::notify_track(track); 458 let fmt = Config::load()
459 .ok()
460 .and_then(|c| c.display_format.clone());
461 notification::notify_track(track, fmt.as_deref());
435 } 462 }
436 format!( 463 format!(
437 "Playing now: {} tracks from {}\n", 464 "Playing now: {} tracks from {}\n",
@@ -459,7 +486,10 @@ fn process_command(
459 && let Some(track) = q.current() 486 && let Some(track) = q.current()
460 { 487 {
461 audio.play(track.path.clone()); 488 audio.play(track.path.clone());
462 notification::notify_track(track); 489 let fmt = Config::load()
490 .ok()
491 .and_then(|c| c.display_format.clone());
492 notification::notify_track(track, fmt.as_deref());
463 } 493 }
464 format!("Queued {} tracks from: {}\n", files.len(), path_str) 494 format!("Queued {} tracks from: {}\n", files.len(), path_str)
465 } 495 }
@@ -512,9 +542,13 @@ fn process_command(
512 q.shuffled_indices().to_vec() 542 q.shuffled_indices().to_vec()
513 }; 543 };
514 let is_playing = audio.is_playing(); 544 let is_playing = audio.is_playing();
545 let fmt = Config::load().ok().and_then(|c| c.display_format.clone());
515 for (display_idx, &track_idx) in indices.iter().enumerate() { 546 for (display_idx, &track_idx) in indices.iter().enumerate() {
516 if let Some(track) = q.track_at(track_idx) { 547 if let Some(track) = q.track_at(track_idx) {
517 let track_str = track.display_string(display_idx, q.len()); 548 let track_str = match fmt.as_deref() {
549 Some(f) => track.format(f),
550 None => track.display_string(display_idx, q.len()),
551 };
518 if track_idx == playing_track_idx && is_playing { 552 if track_idx == playing_track_idx && is_playing {
519 output.push_str(&format!("> {} (playing)\n", track_str)); 553 output.push_str(&format!("> {} (playing)\n", track_str));
520 } else { 554 } else {