diff options
Diffstat (limited to '')
| -rw-r--r-- | src/config.rs | 2 | ||||
| -rw-r--r-- | src/daemon.rs | 7 | ||||
| -rw-r--r-- | src/playback.rs | 1 | ||||
| -rw-r--r-- | src/queue.rs | 130 |
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 | } |
