aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Cargo.toml2
-rw-r--r--PLAN.md28
-rw-r--r--TODO.md2
-rw-r--r--src/daemon.rs5
-rw-r--r--src/queue.rs4
-rw-r--r--tests/queue.rs6
6 files changed, 10 insertions, 37 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8ba5ee0..3482486 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,7 +32,7 @@ dirs = "6.0"
32libc = "0.2.186" 32libc = "0.2.186"
33ctrlc = "3.5.2" 33ctrlc = "3.5.2"
34rand = "0.10.1" 34rand = "0.10.1"
35notify-rust = "4" 35notify-rust = "4.17.0"
36 36
37[dev-dependencies] 37[dev-dependencies]
38tempfile = "3.17" 38tempfile = "3.17"
diff --git a/PLAN.md b/PLAN.md
deleted file mode 100644
index d04e528..0000000
--- a/PLAN.md
+++ /dev/null
@@ -1,28 +0,0 @@
1# rsplayer Project Plan
2
3## Phase 1: Setup & Core ✓
41. Initialize Rust project, add dependencies: `clap` (CLI/autocompletion), `rusqlite` (SQLite), `symphonia`/`cpal` (audio), `directories` (XDG paths), `serde`/`toml` (config), `tokio` (daemon async), `reqwest` (scrobbling). ✓
52. Config stored at `$XDG_CONFIG_HOME/rsplayer/config.toml`: `music_paths` (array of directories to scan), `scan_interval` (optional periodic rescan interval), scrobbling credentials, daemon socket path. ✓
63. SQLite library schema: `artists`, `albums`, `tracks` tables. Implement `rsp scan` to scan all paths in config's `music_paths`, populate/update database. ✓
7
8## Phase 2: Daemon ✓
91. Unix socket daemon (path: `$XDG_RUNTIME_DIR/rsplayer/daemon.sock`) for IPC. ✓
102. Audio engine: Symphonia decode + CPAL output, support play/pause/stop/volume/seek. ✓
113. Queue: in-memory, `last` (append to end, default), `now` (play immediately, ending current song playback), `next` (insert after current). ✓
124. Shuffle modes: `none` (linear playback), `all` (shuffle all tracks), `album` (shuffle within album, structure ready). ✓
13
14## Phase 3: CLI & Completion ✓
151. Clap-based CLI with all subcommands. ✓
162. Shell completions for bash/zsh/fish/powershell/elvish (`rsp completions`). PARTIALLY
173. `rsp status`: show player state, current track, progress, volume, next 2-3 queue items. ✓
184. `rsp queue`: no arguments display current queue; with arguments add items (default `last` behavior). BUGGED
195. `rsp settings` subcommands: `get`, `set`, `list`, `unset` config values. ✓
206. `rsp scan`: `--force` full rescan, `--paths` override config paths. Uses symphonia for metadata. ✓
217. `rsp play`: flexible insert options (`-i`/`--now`, `-n`/`--next`, `-l`/`--last`). ✓
22
23## Phase 4: Polish
241. [ ] Queue state persistence - Save queue to disk so it survives daemon restarts.
252. [ ] Album shuffle mode - Implement proper `ShuffleMode::Album` (shuffle tracks within same album/folder).
263. [ ] Scrobbling - Last.fm and ListenBrainz support via configured credentials.
274. [ ] Logging and error handling improvements.
285. [ ] Systemd service unit for auto-start.
diff --git a/TODO.md b/TODO.md
index 95d775c..1048440 100644
--- a/TODO.md
+++ b/TODO.md
@@ -64,6 +64,6 @@
64 64
65- [ ] **Scrobbling (Last.fm/ListenBrainz)** - Add scrobbling support via configured credentials. 65- [ ] **Scrobbling (Last.fm/ListenBrainz)** - Add scrobbling support via configured credentials.
66 66
67- [ ] **Shuffle first song** - First song is not being shuffled due to currently played track not being shuffled. 67- [x] **Shuffle first song** - `update_shuffle_indices` now shuffles all indices when position is 0; directory playback uses `q.current()` instead of `files.first()`.
68 68
69- [ ] **In album shuffle** - Shuffle songs within albums, don't mix albums together (only applies to shuffle: album). 69- [ ] **In album shuffle** - Shuffle songs within albums, don't mix albums together (only applies to shuffle: album).
diff --git a/src/daemon.rs b/src/daemon.rs
index cca9b11..00bf35f 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -453,11 +453,10 @@ fn process_command(
453 q.add_track(file.clone()); 453 q.add_track(file.clone());
454 } 454 }
455 if !audio.is_playing() 455 if !audio.is_playing()
456 && let Some(track) = files.first() 456 && let Some(track) = q.current()
457 { 457 {
458 let track = Track::from_path(track.clone());
459 audio.play(track.path.clone()); 458 audio.play(track.path.clone());
460 notification::notify_track(&track); 459 notification::notify_track(track);
461 } 460 }
462 format!("Queued {} tracks from: {}\n", files.len(), path_str) 461 format!("Queued {} tracks from: {}\n", files.len(), path_str)
463 } 462 }
diff --git a/src/queue.rs b/src/queue.rs
index 53b104f..dca8e3d 100644
--- a/src/queue.rs
+++ b/src/queue.rs
@@ -171,7 +171,9 @@ impl Queue {
171 if let ShuffleMode::All = self.shuffle { 171 if let ShuffleMode::All = self.shuffle {
172 let mut rng = rand::rng(); 172 let mut rng = rand::rng();
173 let pos = self.position.min(len.saturating_sub(1)); 173 let pos = self.position.min(len.saturating_sub(1));
174 if pos + 1 < len { 174 if pos == 0 && len > 1 {
175 self.shuffled_indices.shuffle(&mut rng);
176 } else if pos + 1 < len {
175 self.shuffled_indices[pos + 1..].shuffle(&mut rng); 177 self.shuffled_indices[pos + 1..].shuffle(&mut rng);
176 } 178 }
177 } 179 }
diff --git a/tests/queue.rs b/tests/queue.rs
index 0ceaba0..d3e923e 100644
--- a/tests/queue.rs
+++ b/tests/queue.rs
@@ -269,8 +269,6 @@ fn test_shuffle_all_navigates_in_shuffled_order() {
269 add_track(&mut q, &format!("/music/song{}.mp3", i)); 269 add_track(&mut q, &format!("/music/song{}.mp3", i));
270 } 270 }
271 q.set_shuffle(ShuffleMode::All); 271 q.set_shuffle(ShuffleMode::All);
272 // Current should still return the first track
273 assert_eq!(q.current().unwrap().path, PathBuf::from("/music/song0.mp3"));
274 // Navigate through all tracks, should visit all 10 (in shuffled order) 272 // Navigate through all tracks, should visit all 10 (in shuffled order)
275 let mut visited = std::collections::HashSet::new(); 273 let mut visited = std::collections::HashSet::new();
276 visited.insert(q.current().unwrap().path.clone()); 274 visited.insert(q.current().unwrap().path.clone());
@@ -291,7 +289,9 @@ fn test_shuffle_all_current_stays_at_position_zero() {
291 } 289 }
292 q.set_shuffle(ShuffleMode::All); 290 q.set_shuffle(ShuffleMode::All);
293 assert_eq!(q.position(), 0); 291 assert_eq!(q.position(), 0);
294 assert_eq!(q.shuffled_indices()[0], 0); 292 let mut sorted = q.shuffled_indices().to_vec();
293 sorted.sort();
294 assert_eq!(sorted, vec![0, 1, 2, 3, 4]);
295} 295}
296 296
297#[test] 297#[test]