aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cli.rs11
-rw-r--r--src/daemon.rs14
-rw-r--r--src/queue.rs85
3 files changed, 96 insertions, 14 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 0da3078..bd81983 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -21,15 +21,12 @@ pub enum Commands {
21 Restart, 21 Restart,
22 /// Play a track, album, or artist 22 /// Play a track, album, or artist
23 Play { 23 Play {
24 /// Play immediately (add after current and skip to it) 24 /// Add after current track and skip to it
25 #[arg(short = 'i', long = "now", group = "position")] 25 #[arg(short = 'i', long = "now")]
26 now: bool, 26 now: bool,
27 /// Add to queue after current track (don't skip) 27 /// Add to queue after current track (don't skip)
28 #[arg(short = 'n', long = "next", group = "position")] 28 #[arg(short = 'n', long = "next")]
29 next: bool, 29 next: bool,
30 /// Add to end of queue (default)
31 #[arg(short = 'l', long = "last", group = "position")]
32 last: bool,
33 #[arg(value_name = "QUERY")] 30 #[arg(value_name = "QUERY")]
34 query: Option<String>, 31 query: Option<String>,
35 }, 32 },
@@ -126,7 +123,7 @@ impl Cli {
126 }; 123 };
127 crate::daemon::send_command(format!("play {} {}", pos, q))?; 124 crate::daemon::send_command(format!("play {} {}", pos, q))?;
128 } else { 125 } else {
129 crate::daemon::send_command("play".to_string())?; 126 crate::daemon::send_command("play last".to_string())?;
130 } 127 }
131 } 128 }
132 Commands::Pause => { 129 Commands::Pause => {
diff --git a/src/daemon.rs b/src/daemon.rs
index e88669a..f4a72b7 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -399,9 +399,9 @@ fn process_command(
399 format!("Queued next: {}\n", path_str) 399 format!("Queued next: {}\n", path_str)
400 } 400 }
401 _ => { 401 _ => {
402 let was_playing = audio.is_playing(); 402 let was_empty = q.is_empty();
403 q.add_track(path.clone()); 403 q.add_track(path.clone());
404 if !was_playing { 404 if was_empty && audio.is_playing() {
405 let track = Track::from_path(path.clone()); 405 let track = Track::from_path(path.clone());
406 audio.play(path); 406 audio.play(path);
407 notification::notify_track(&track); 407 notification::notify_track(&track);
@@ -452,7 +452,8 @@ fn process_command(
452 for file in &files { 452 for file in &files {
453 q.add_track(file.clone()); 453 q.add_track(file.clone());
454 } 454 }
455 if !audio.is_playing() 455 if was_empty
456 && audio.is_playing()
456 && let Some(track) = q.current() 457 && let Some(track) = q.current()
457 { 458 {
458 audio.play(track.path.clone()); 459 audio.play(track.path.clone());
@@ -519,8 +520,11 @@ fn process_command(
519 } 520 }
520 } 521 }
521 } 522 }
522 // Use pager if output is long (more than 24 lines) 523 // Use pager if queue is longer than terminal height
523 if output.lines().count() > 24 { 524 let terminal_rows = terminal_size::terminal_size()
525 .map(|(w, _)| w.0 as usize)
526 .unwrap_or(24);
527 if output.lines().count() > terminal_rows {
524 use std::process::{Command, Stdio}; 528 use std::process::{Command, Stdio};
525 let pager = Command::new("less").arg("-R").stdin(Stdio::piped()).spawn(); 529 let pager = Command::new("less").arg("-R").stdin(Stdio::piped()).spawn();
526 if let Ok(mut child) = pager { 530 if let Ok(mut child) = pager {
diff --git a/src/queue.rs b/src/queue.rs
index 9e8e1af..109b748 100644
--- a/src/queue.rs
+++ b/src/queue.rs
@@ -124,6 +124,7 @@ pub struct Queue {
124 position: usize, 124 position: usize,
125 shuffle: ShuffleMode, 125 shuffle: ShuffleMode,
126 shuffled_indices: Vec<usize>, 126 shuffled_indices: Vec<usize>,
127 original_tracks: Vec<Track>,
127} 128}
128 129
129impl Default for Queue { 130impl Default for Queue {
@@ -139,6 +140,7 @@ impl Queue {
139 position: 0, 140 position: 0,
140 shuffle: ShuffleMode::None, 141 shuffle: ShuffleMode::None,
141 shuffled_indices: Vec::new(), 142 shuffled_indices: Vec::new(),
143 original_tracks: Vec::new(),
142 } 144 }
143 } 145 }
144 146
@@ -162,8 +164,38 @@ impl Queue {
162 } 164 }
163 165
164 pub fn set_shuffle(&mut self, mode: ShuffleMode) { 166 pub fn set_shuffle(&mut self, mode: ShuffleMode) {
165 self.shuffle = mode; 167 match mode {
166 self.update_shuffle_indices(); 168 ShuffleMode::None => {
169 self.shuffle = ShuffleMode::None;
170 self.restore_from_original();
171 }
172 ShuffleMode::All => {
173 self.original_tracks = self.tracks.clone();
174 self.shuffle = ShuffleMode::All;
175 self.update_shuffle_indices();
176 }
177 ShuffleMode::Album => {
178 self.original_tracks = self.tracks.clone();
179 self.shuffle = ShuffleMode::Album;
180 self.update_shuffle_indices_album();
181 }
182 }
183 }
184
185 fn restore_from_original(&mut self) {
186 if self.original_tracks.is_empty() {
187 self.shuffled_indices.clear();
188 return;
189 }
190
191 let current_pos = self.position;
192 let played: Vec<Track> = self.tracks[..current_pos].to_vec();
193 let unplayed_original: Vec<Track> = self.original_tracks[current_pos..].to_vec();
194
195 self.tracks = played;
196 self.tracks.extend(unplayed_original);
197 self.position = current_pos;
198 self.shuffled_indices.clear();
167 } 199 }
168 200
169 fn update_shuffle_indices(&mut self) { 201 fn update_shuffle_indices(&mut self) {
@@ -180,6 +212,55 @@ impl Queue {
180 } 212 }
181 } 213 }
182 214
215 fn update_shuffle_indices_album(&mut self) {
216 let len = self.tracks.len();
217 self.shuffled_indices = (0..len).collect();
218
219 let mut rng = rand::rng();
220 let pos = self.position.min(len.saturating_sub(1));
221
222 if pos == 0 && len > 1 {
223 self.shuffle_album_range(0..len, &mut rng);
224 } else if pos + 1 < len {
225 self.shuffle_album_range(pos + 1..len, &mut rng);
226 }
227 }
228
229 fn shuffle_album_range(&mut self, range: std::ops::Range<usize>, rng: &mut impl rand::Rng) {
230 if range.start >= range.end {
231 return;
232 }
233
234 let mut album_ranges: Vec<(usize, usize)> = Vec::new();
235 let mut current_album: Option<&str> = None;
236 let mut range_start: Option<usize> = None;
237
238 for i in range.clone() {
239 let album = self.tracks.get(i).and_then(|t| t.album.as_deref());
240 match (current_album, range_start) {
241 (None, None) => {
242 current_album = album;
243 range_start = Some(i);
244 }
245 (Some(_), Some(start)) if album != current_album => {
246 album_ranges.push((start, i));
247 current_album = album;
248 range_start = Some(i);
249 }
250 _ => {}
251 }
252 }
253 if let Some(start) = range_start {
254 album_ranges.push((start, range.end));
255 }
256
257 for (start, end) in album_ranges {
258 if start + 1 < end {
259 self.shuffled_indices[start..end].shuffle(rng);
260 }
261 }
262 }
263
183 pub fn current(&self) -> Option<&Track> { 264 pub fn current(&self) -> Option<&Track> {
184 if self.tracks.is_empty() { 265 if self.tracks.is_empty() {
185 None 266 None