play next (#278)
* add play next feature * fix play_next insertion order * fix play_next call for playable * document play_next shortcut + reformat Co-authored-by: Henrik Friedrichsen <henrik@affekt.org>
This commit is contained in:
@@ -80,7 +80,8 @@ depending on your desktop environment settings. Have a look at the
|
||||
* `F3`: Library
|
||||
* `d` deletes the currently selected playlist
|
||||
* Tracks and playlists can be played using `Return` and queued using `Space`
|
||||
* `.` will move to the currently playing track in the queue.
|
||||
* `n` will play the selected item after the currently playing track
|
||||
* `.` will move to the currently playing track in the queue
|
||||
* `s` will save, `d` will remove the currently selected track to/from your
|
||||
library
|
||||
* `o` will open a detail view or context menu for the selected item
|
||||
|
||||
10
src/album.rs
10
src/album.rs
@@ -189,6 +189,16 @@ impl ListItem for Album {
|
||||
}
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
self.load_tracks(queue.get_spotify());
|
||||
|
||||
if let Some(tracks) = self.tracks.as_ref() {
|
||||
for t in tracks.iter().rev() {
|
||||
queue.insert_after_current(Playable::Track(t.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
self.load_tracks(queue.get_spotify());
|
||||
|
||||
|
||||
@@ -185,6 +185,16 @@ impl ListItem for Artist {
|
||||
}
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
self.load_albums(queue.get_spotify());
|
||||
|
||||
if let Some(tracks) = self.tracks.as_ref() {
|
||||
for t in tracks.iter().rev() {
|
||||
queue.insert_after_current(Playable::Track(t.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
self.load_albums(queue.get_spotify());
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ pub enum Command {
|
||||
Next,
|
||||
Clear,
|
||||
Queue,
|
||||
PlayNext,
|
||||
Play,
|
||||
UpdateLibrary,
|
||||
Save,
|
||||
@@ -117,6 +118,7 @@ impl fmt::Display for Command {
|
||||
Command::Next => "next".to_string(),
|
||||
Command::Clear => "clear".to_string(),
|
||||
Command::Queue => "queue".to_string(),
|
||||
Command::PlayNext => "play next".to_string(),
|
||||
Command::Play => "play".to_string(),
|
||||
Command::UpdateLibrary => "update".to_string(),
|
||||
Command::Save => "save".to_string(),
|
||||
@@ -208,6 +210,7 @@ pub fn parse(input: &str) -> Option<Command> {
|
||||
"previous" => Some(Command::Previous),
|
||||
"next" => Some(Command::Next),
|
||||
"clear" => Some(Command::Clear),
|
||||
"playnext" => Some(Command::PlayNext),
|
||||
"queue" => Some(Command::Queue),
|
||||
"play" => Some(Command::Play),
|
||||
"update" => Some(Command::UpdateLibrary),
|
||||
|
||||
@@ -177,6 +177,7 @@ impl CommandManager {
|
||||
| Command::Move(_, _)
|
||||
| Command::Shift(_, _)
|
||||
| Command::Play
|
||||
| Command::PlayNext
|
||||
| Command::Queue
|
||||
| Command::Save
|
||||
| Command::Delete
|
||||
@@ -269,6 +270,7 @@ impl CommandManager {
|
||||
kb.insert(">".into(), Command::Next);
|
||||
kb.insert("c".into(), Command::Clear);
|
||||
kb.insert("Space".into(), Command::Queue);
|
||||
kb.insert("n".into(), Command::PlayNext);
|
||||
kb.insert("Enter".into(), Command::Play);
|
||||
kb.insert("s".into(), Command::Save);
|
||||
kb.insert("Ctrl+s".into(), Command::SaveQueue);
|
||||
|
||||
@@ -84,6 +84,10 @@ impl ListItem for Episode {
|
||||
queue.play(index, true, false);
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
queue.insert_after_current(Playable::Episode(self.clone()));
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
queue.append(Playable::Episode(self.clone()));
|
||||
}
|
||||
|
||||
@@ -88,6 +88,10 @@ impl ListItem for Playable {
|
||||
self.as_listitem().play(queue)
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
self.as_listitem().play_next(queue)
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
self.as_listitem().queue(queue)
|
||||
}
|
||||
|
||||
@@ -165,6 +165,16 @@ impl ListItem for Playlist {
|
||||
}
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
self.load_tracks(queue.get_spotify());
|
||||
|
||||
if let Some(tracks) = self.tracks.as_ref() {
|
||||
for track in tracks.iter().rev() {
|
||||
queue.insert_after_current(Playable::Track(track.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
self.load_tracks(queue.get_spotify());
|
||||
|
||||
|
||||
22
src/queue.rs
22
src/queue.rs
@@ -96,6 +96,28 @@ impl Queue {
|
||||
*self.current_track.read().unwrap()
|
||||
}
|
||||
|
||||
pub fn insert_after_current(&self, track: Playable) {
|
||||
if let Some(index) = self.get_current_index() {
|
||||
let mut random_order = self.random_order.write().unwrap();
|
||||
if let Some(order) = random_order.as_mut() {
|
||||
let next_i = order.iter().position(|&i| i == index).unwrap();
|
||||
// shift everything after the insertion in order
|
||||
let size = order.len();
|
||||
for i in 0..size {
|
||||
if order[i] > index {
|
||||
order[i] += 1;
|
||||
}
|
||||
}
|
||||
// finally, add the next track index
|
||||
order.insert(next_i + 1, index + 1);
|
||||
}
|
||||
let mut q = self.queue.write().unwrap();
|
||||
q.insert(index + 1, track);
|
||||
} else {
|
||||
self.append(track);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&self, track: Playable) {
|
||||
let mut random_order = self.random_order.write().unwrap();
|
||||
if let Some(order) = random_order.as_mut() {
|
||||
|
||||
10
src/show.rs
10
src/show.rs
@@ -124,6 +124,16 @@ impl ListItem for Show {
|
||||
queue.play(index, true, true);
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
self.load_episodes(queue.get_spotify());
|
||||
|
||||
if let Some(episodes) = self.episodes.as_ref() {
|
||||
for ep in episodes.iter().rev() {
|
||||
queue.insert_after_current(Playable::Episode(ep.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
self.load_episodes(queue.get_spotify());
|
||||
|
||||
|
||||
@@ -46,12 +46,12 @@ use url::Url;
|
||||
use core::task::Poll;
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
use std::sync::RwLock;
|
||||
use std::thread;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::{env, io};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::artist::Artist;
|
||||
use crate::config;
|
||||
@@ -401,7 +401,7 @@ impl Spotify {
|
||||
) {
|
||||
let bitrate_str = cfg.bitrate.unwrap_or(320).to_string();
|
||||
let bitrate = Bitrate::from_str(&bitrate_str);
|
||||
if bitrate.is_err(){
|
||||
if bitrate.is_err() {
|
||||
error!("invalid bitrate, will use 320 instead")
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ impl ListItem for Track {
|
||||
}
|
||||
|
||||
fn display_center(&self) -> String {
|
||||
format!("{}", self.album)
|
||||
self.album.to_string()
|
||||
}
|
||||
|
||||
fn display_right(&self, library: Arc<Library>) -> String {
|
||||
@@ -175,6 +175,10 @@ impl ListItem for Track {
|
||||
queue.play(index, true, false);
|
||||
}
|
||||
|
||||
fn play_next(&mut self, queue: Arc<Queue>) {
|
||||
queue.insert_after_current(Playable::Track(self.clone()));
|
||||
}
|
||||
|
||||
fn queue(&mut self, queue: Arc<Queue>) {
|
||||
queue.append(Playable::Track(self.clone()));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ pub trait ListItem: Sync + Send + 'static {
|
||||
fn display_right(&self, library: Arc<Library>) -> String;
|
||||
fn play(&mut self, queue: Arc<Queue>);
|
||||
fn queue(&mut self, queue: Arc<Queue>);
|
||||
fn play_next(&mut self, queue: Arc<Queue>);
|
||||
fn toggle_saved(&mut self, library: Arc<Library>);
|
||||
fn save(&mut self, library: Arc<Library>);
|
||||
fn unsave(&mut self, library: Arc<Library>);
|
||||
|
||||
@@ -338,6 +338,15 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
|
||||
|
||||
return Ok(CommandResult::Consumed(None));
|
||||
}
|
||||
Command::PlayNext => {
|
||||
info!("played next");
|
||||
let mut content = self.content.write().unwrap();
|
||||
if let Some(item) = content.get_mut(self.selected) {
|
||||
item.play_next(self.queue.clone());
|
||||
}
|
||||
|
||||
return Ok(CommandResult::Consumed(None));
|
||||
}
|
||||
Command::Queue => {
|
||||
let mut content = self.content.write().unwrap();
|
||||
if let Some(item) = content.get_mut(self.selected) {
|
||||
|
||||
@@ -95,6 +95,9 @@ impl ViewExt for QueueView {
|
||||
self.queue.play(self.list.get_selected_index(), true, false);
|
||||
return Ok(CommandResult::Consumed(None));
|
||||
}
|
||||
Command::PlayNext => {
|
||||
return Ok(CommandResult::Ignored);
|
||||
}
|
||||
Command::Queue => {
|
||||
return Ok(CommandResult::Ignored);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user