diff --git a/doc/users.md b/doc/users.md index e39e05920..1f8d2e777 100644 --- a/doc/users.md +++ b/doc/users.md @@ -128,11 +128,12 @@ When pressing O: | Shift+X | Copy the URL to the **currently playing track** to the system clipboard. | ### Queue -| Key | Command | -|------------------------------|--------------------------------------| -| C | Clear the entire queue. | -| D | Delete the currently selected track. | -| Ctrl+S | Save the current queue. | +| Key | Command | +|------------------------------|-----------------------------------------------------| +| C | Clear the entire queue. | +| D | Delete the currently selected track. | +| Ctrl+S | Save the current queue. | +| Z | Randomize the queue tracks (put into random order). | ### Library | Key | Command | diff --git a/src/command.rs b/src/command.rs index 5d48a0182..064246b09 100644 --- a/src/command.rs +++ b/src/command.rs @@ -143,6 +143,7 @@ pub enum Command { VolumeDown(u16), Repeat(Option), Shuffle(Option), + Randomize, #[cfg(feature = "share_clipboard")] Share(TargetMode), Back, @@ -220,6 +221,7 @@ impl fmt::Display for Command { | Command::SaveQueue | Command::Add | Command::AddCurrent + | Command::Randomize | Command::Delete | Command::Back | Command::Help @@ -259,6 +261,7 @@ impl Command { Command::VolumeDown(_) => "voldown", Command::Repeat(_) => "repeat", Command::Shuffle(_) => "shuffle", + Command::Randomize => "randomize", #[cfg(feature = "share_clipboard")] Command::Share(_) => "share", Command::Back => "back", @@ -547,6 +550,7 @@ pub fn parse(input: &str) -> Result, CommandParseError> { }?; Command::Shuffle(switch) } + "randomize" => Command::Randomize, #[cfg(feature = "share_clipboard")] "share" => { let &target_mode_raw = args.first().ok_or(InsufficientArgs { diff --git a/src/commands.rs b/src/commands.rs index 94d663275..dcca97170 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -173,6 +173,10 @@ impl CommandManager { self.queue.set_shuffle(mode); Ok(None) } + Command::Randomize => { + self.queue.randomize(); + Ok(None) + } Command::Repeat(mode) => { let mode = mode.unwrap_or_else(|| match self.queue.get_repeat() { RepeatSetting::None => RepeatSetting::RepeatPlaylist, @@ -452,6 +456,7 @@ impl CommandManager { kb.insert("r".into(), vec![Command::Repeat(None)]); kb.insert("z".into(), vec![Command::Shuffle(None)]); + kb.insert("Ctrl+z".into(), vec![Command::Randomize]); #[cfg(feature = "share_clipboard")] { diff --git a/src/mpris.rs b/src/mpris.rs index 365ed036a..afa2b4701 100644 --- a/src/mpris.rs +++ b/src/mpris.rs @@ -263,6 +263,12 @@ impl MprisPlayer { self.event.trigger(); } + /*#[dbus_interface(property)] + fn randomize(&self, _b: bool) { + self.queue.randomize(); + self.event.trigger(); + }*/ + #[dbus_interface(property)] fn volume(&self) -> f64 { self.spotify.volume() as f64 / 65535_f64 diff --git a/src/queue.rs b/src/queue.rs index 09324c221..f21a9e0a0 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -467,6 +467,35 @@ impl Queue { } } + /// Randomize the queue ("shuffle" all the entries of the underlying Vec). Different from the `Shuffle` + /// option in that this actually changes the track positions in the queue (that gets displayed to the user), + /// but leaves the play order running from top to bottom. Useful If you want to shuffle a playlist, + /// but still want to add songs to be played next. + /// + /// This deactivates `Shuffle`. + pub fn randomize(&self) { + // stop playlist, because we completely invalidate any playing order + let previous_playback_state = self.cfg.state().playback_state.clone(); + self.stop(); + + // deactivate `Shuffle` feature, because it usually wouldn't make sense to use both + if self.get_shuffle() == true { + self.set_shuffle(false); + } + + // permutate the queue Vec + let mut queue = self.queue.write().unwrap(); + queue.shuffle(&mut thread_rng()); + + // resetting playing position to start of queue doesn't seem necessary + // my guess is that stop() does that + + // resume playback if we were playing before + if previous_playback_state == PlaybackState::Playing { + self.toggleplayback(); + } + } + /// Handle events that are specific to the queue. pub fn handle_event(&self, event: QueueEvent) { match event {