aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs2
-rw-r--r--src/daemon.rs7
-rw-r--r--src/playback.rs1
-rw-r--r--src/queue.rs130
4 files changed, 115 insertions, 25 deletions
diff --git a/src/config.rs b/src/config.rs
index 5fd2f1b..63b0164 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -200,7 +200,7 @@ impl Config {
200 self.volume = Some(value.parse()?); 200 self.volume = Some(value.parse()?);
201 } 201 }
202 "shuffle" => { 202 "shuffle" => {
203 if value == "on" || value == "all" || value == "album" { 203 if value == "on" || value == "all" || value == "album" || value == "albums" {
204 self.shuffle = Some(value.to_string()); 204 self.shuffle = Some(value.to_string());
205 } else if value == "off" || value == "none" { 205 } else if value == "off" || value == "none" {
206 self.shuffle = None; 206 self.shuffle = None;
diff --git a/src/daemon.rs b/src/daemon.rs
index 8b74357..b18ecc9 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -248,6 +248,7 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
248 match shuffle_str.as_str() { 248 match shuffle_str.as_str() {
249 "on" | "all" => q.set_shuffle(ShuffleMode::All), 249 "on" | "all" => q.set_shuffle(ShuffleMode::All),
250 "album" => q.set_shuffle(ShuffleMode::Album), 250 "album" => q.set_shuffle(ShuffleMode::Album),
251 "albums" => q.set_shuffle(ShuffleMode::Albums),
251 _ => {} 252 _ => {}
252 } 253 }
253 } 254 }
@@ -633,6 +634,7 @@ fn process_command(
633 ShuffleMode::None => "Shuffle: off\n".to_string(), 634 ShuffleMode::None => "Shuffle: off\n".to_string(),
634 ShuffleMode::All => "Shuffle: on (all)\n".to_string(), 635 ShuffleMode::All => "Shuffle: on (all)\n".to_string(),
635 ShuffleMode::Album => "Shuffle: on (album)\n".to_string(), 636 ShuffleMode::Album => "Shuffle: on (album)\n".to_string(),
637 ShuffleMode::Albums => "Shuffle: on (albums)\n".to_string(),
636 } 638 }
637 } else { 639 } else {
638 "Error accessing queue\n".to_string() 640 "Error accessing queue\n".to_string()
@@ -647,11 +649,14 @@ fn process_command(
647 } else if mode_str == "album" { 649 } else if mode_str == "album" {
648 q.set_shuffle(ShuffleMode::Album); 650 q.set_shuffle(ShuffleMode::Album);
649 "Shuffle: on (album)\n".to_string() 651 "Shuffle: on (album)\n".to_string()
652 } else if mode_str == "albums" {
653 q.set_shuffle(ShuffleMode::Albums);
654 "Shuffle: on (albums)\n".to_string()
650 } else if mode_str == "off" || mode_str == "none" { 655 } else if mode_str == "off" || mode_str == "none" {
651 q.set_shuffle(ShuffleMode::None); 656 q.set_shuffle(ShuffleMode::None);
652 "Shuffle: off\n".to_string() 657 "Shuffle: off\n".to_string()
653 } else { 658 } else {
654 "Usage: shuffle [on|off|album]\n".to_string() 659 "Usage: shuffle [on|off|album|albums]\n".to_string()
655 }; 660 };
656 // Save to config 661 // Save to config
657 if let Ok(mut config) = Config::load() { 662 if let Ok(mut config) = Config::load() {
diff --git a/src/playback.rs b/src/playback.rs
index d9dd1f9..f16bf8f 100644
--- a/src/playback.rs
+++ b/src/playback.rs
@@ -24,6 +24,7 @@ pub enum ShuffleMode {
24 None, 24 None,
25 All, 25 All,
26 Album, 26 Album,
27 Albums,
27} 28}
28 29
29#[allow(dead_code)] 30#[allow(dead_code)]
diff --git a/src/queue.rs b/src/queue.rs
index b0acf5f..9d09fec 100644
--- a/src/queue.rs
+++ b/src/queue.rs
@@ -6,6 +6,7 @@ pub enum ShuffleMode {
6 None, 6 None,
7 All, 7 All,
8 Album, 8 Album,
9 Albums,
9} 10}
10 11
11#[derive(Debug, Default, Clone)] 12#[derive(Debug, Default, Clone)]
@@ -46,28 +47,8 @@ pub fn extract_metadata(path: &Path) -> Option<AudioMetadata> {
46 }; 47 };
47 48
48 { 49 {
49 let meta = probed.metadata(); 50 let mut meta = probed.metadata();
50 if let Some(rev) = meta.current() { 51 if let Some(rev) = meta.skip_to_latest() {
51 for tag in &rev.media.tags {
52 match &tag.std {
53 Some(StandardTag::TrackTitle(value)) => {
54 metadata.title = Some(value.to_string());
55 }
56 Some(StandardTag::Artist(value)) => {
57 metadata.artist = Some(value.to_string());
58 }
59 Some(StandardTag::Album(value)) => {
60 metadata.album = Some(value.to_string());
61 }
62 _ => {}
63 }
64 }
65 }
66 }
67
68 if metadata.title.is_none() && metadata.artist.is_none() && metadata.album.is_none() {
69 let meta = probed.metadata();
70 if let Some(rev) = meta.current() {
71 for tag in &rev.media.tags { 52 for tag in &rev.media.tags {
72 match &tag.std { 53 match &tag.std {
73 Some(StandardTag::TrackTitle(value)) => { 54 Some(StandardTag::TrackTitle(value)) => {
@@ -183,6 +164,12 @@ impl Queue {
183 self.update_shuffle_indices(); 164 self.update_shuffle_indices();
184 } 165 }
185 166
167 #[allow(dead_code)]
168 pub fn push_track(&mut self, track: Track) {
169 self.tracks.push(track);
170 self.update_shuffle_indices();
171 }
172
186 pub fn insert_after_current(&mut self, path: PathBuf) { 173 pub fn insert_after_current(&mut self, path: PathBuf) {
187 if self.tracks.is_empty() { 174 if self.tracks.is_empty() {
188 self.tracks.push(Track::from_path(path)); 175 self.tracks.push(Track::from_path(path));
@@ -213,6 +200,11 @@ impl Queue {
213 self.shuffle = ShuffleMode::Album; 200 self.shuffle = ShuffleMode::Album;
214 self.update_shuffle_indices_album(); 201 self.update_shuffle_indices_album();
215 } 202 }
203 ShuffleMode::Albums => {
204 self.original_tracks = self.tracks.clone();
205 self.shuffle = ShuffleMode::Albums;
206 self.update_shuffle_indices_albums();
207 }
216 } 208 }
217 } 209 }
218 210
@@ -268,6 +260,9 @@ impl Queue {
268 ShuffleMode::Album => { 260 ShuffleMode::Album => {
269 self.update_shuffle_indices_album(); 261 self.update_shuffle_indices_album();
270 } 262 }
263 ShuffleMode::Albums => {
264 self.update_shuffle_indices_albums();
265 }
271 ShuffleMode::None => {} 266 ShuffleMode::None => {}
272 } 267 }
273 } 268 }
@@ -285,6 +280,62 @@ impl Queue {
285 } 280 }
286 } 281 }
287 282
283 fn update_shuffle_indices_albums(&mut self) {
284 let len = self.tracks.len();
285 self.shuffled_indices = (0..len).collect();
286
287 if len <= 1 {
288 return;
289 }
290
291 let pos = self.position.min(len.saturating_sub(1));
292 if pos + 1 >= len {
293 return;
294 }
295
296 let range_start = pos + 1;
297 let mut rng = rand::rng();
298
299 // Build album groups within the range, shuffling tracks within each group
300 let mut groups: Vec<Vec<usize>> = Vec::new();
301 let mut current_group: Vec<usize> = Vec::new();
302 let mut current_album: Option<&str> = None;
303
304 for i in range_start..len {
305 let album = self.tracks.get(i).and_then(|t| t.album.as_deref());
306
307 if current_group.is_empty() {
308 current_group.push(i);
309 current_album = album;
310 } else if album != current_album {
311 // Shuffle tracks within this album group before adding it
312 if current_group.len() > 1 {
313 current_group.shuffle(&mut rng);
314 }
315 groups.push(std::mem::take(&mut current_group));
316 current_group.push(i);
317 current_album = album;
318 } else {
319 current_group.push(i);
320 }
321 }
322 if !current_group.is_empty() {
323 if current_group.len() > 1 {
324 current_group.shuffle(&mut rng);
325 }
326 groups.push(current_group);
327 }
328
329 // Shuffle the album group order
330 groups.shuffle(&mut rng);
331
332 let mut new_indices: Vec<usize> = self.shuffled_indices[..=pos].to_vec();
333 for group in &groups {
334 new_indices.extend(group);
335 }
336 self.shuffled_indices = new_indices;
337 }
338
288 fn shuffle_album_range(&mut self, range: std::ops::Range<usize>, rng: &mut impl rand::Rng) { 339 fn shuffle_album_range(&mut self, range: std::ops::Range<usize>, rng: &mut impl rand::Rng) {
289 if range.start >= range.end { 340 if range.start >= range.end {
290 return; 341 return;
@@ -327,7 +378,7 @@ impl Queue {
327 self.shuffled_indices 378 self.shuffled_indices
328 .get(self.position) 379 .get(self.position)
329 .and_then(|&idx| self.tracks.get(idx)) 380 .and_then(|&idx| self.tracks.get(idx))
330 } else if self.shuffle == ShuffleMode::Album { 381 } else if self.shuffle == ShuffleMode::Album || self.shuffle == ShuffleMode::Albums {
331 self.shuffled_indices 382 self.shuffled_indices
332 .get(self.position) 383 .get(self.position)
333 .and_then(|&idx| self.tracks.get(idx)) 384 .and_then(|&idx| self.tracks.get(idx))
@@ -397,6 +448,39 @@ impl Queue {
397 ShuffleMode::Album => { 448 ShuffleMode::Album => {
398 self.shuffle_album_range(0..self.tracks.len(), &mut rng); 449 self.shuffle_album_range(0..self.tracks.len(), &mut rng);
399 } 450 }
451 ShuffleMode::Albums => {
452 let mut groups: Vec<Vec<usize>> = Vec::new();
453 let mut current_group: Vec<usize> = Vec::new();
454 let mut current_album: Option<&str> = None;
455
456 for i in 0..self.tracks.len() {
457 let album = self.tracks.get(i).and_then(|t| t.album.as_deref());
458
459 if current_group.is_empty() {
460 current_group.push(i);
461 current_album = album;
462 } else if album != current_album {
463 if current_group.len() > 1 {
464 current_group.shuffle(&mut rng);
465 }
466 groups.push(std::mem::take(&mut current_group));
467 current_group.push(i);
468 current_album = album;
469 } else {
470 current_group.push(i);
471 }
472 }
473 if !current_group.is_empty() {
474 if current_group.len() > 1 {
475 current_group.shuffle(&mut rng);
476 }
477 groups.push(current_group);
478 }
479
480 groups.shuffle(&mut rng);
481
482 self.shuffled_indices = groups.concat();
483 }
400 ShuffleMode::None => {} 484 ShuffleMode::None => {}
401 } 485 }
402 } 486 }