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
|
* `F3`: Library
|
||||||
* `d` deletes the currently selected playlist
|
* `d` deletes the currently selected playlist
|
||||||
* Tracks and playlists can be played using `Return` and queued using `Space`
|
* 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
|
* `s` will save, `d` will remove the currently selected track to/from your
|
||||||
library
|
library
|
||||||
* `o` will open a detail view or context menu for the selected item
|
* `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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
self.load_tracks(queue.get_spotify());
|
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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
self.load_albums(queue.get_spotify());
|
self.load_albums(queue.get_spotify());
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ pub enum Command {
|
|||||||
Next,
|
Next,
|
||||||
Clear,
|
Clear,
|
||||||
Queue,
|
Queue,
|
||||||
|
PlayNext,
|
||||||
Play,
|
Play,
|
||||||
UpdateLibrary,
|
UpdateLibrary,
|
||||||
Save,
|
Save,
|
||||||
@@ -117,6 +118,7 @@ impl fmt::Display for Command {
|
|||||||
Command::Next => "next".to_string(),
|
Command::Next => "next".to_string(),
|
||||||
Command::Clear => "clear".to_string(),
|
Command::Clear => "clear".to_string(),
|
||||||
Command::Queue => "queue".to_string(),
|
Command::Queue => "queue".to_string(),
|
||||||
|
Command::PlayNext => "play next".to_string(),
|
||||||
Command::Play => "play".to_string(),
|
Command::Play => "play".to_string(),
|
||||||
Command::UpdateLibrary => "update".to_string(),
|
Command::UpdateLibrary => "update".to_string(),
|
||||||
Command::Save => "save".to_string(),
|
Command::Save => "save".to_string(),
|
||||||
@@ -208,6 +210,7 @@ pub fn parse(input: &str) -> Option<Command> {
|
|||||||
"previous" => Some(Command::Previous),
|
"previous" => Some(Command::Previous),
|
||||||
"next" => Some(Command::Next),
|
"next" => Some(Command::Next),
|
||||||
"clear" => Some(Command::Clear),
|
"clear" => Some(Command::Clear),
|
||||||
|
"playnext" => Some(Command::PlayNext),
|
||||||
"queue" => Some(Command::Queue),
|
"queue" => Some(Command::Queue),
|
||||||
"play" => Some(Command::Play),
|
"play" => Some(Command::Play),
|
||||||
"update" => Some(Command::UpdateLibrary),
|
"update" => Some(Command::UpdateLibrary),
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ impl CommandManager {
|
|||||||
| Command::Move(_, _)
|
| Command::Move(_, _)
|
||||||
| Command::Shift(_, _)
|
| Command::Shift(_, _)
|
||||||
| Command::Play
|
| Command::Play
|
||||||
|
| Command::PlayNext
|
||||||
| Command::Queue
|
| Command::Queue
|
||||||
| Command::Save
|
| Command::Save
|
||||||
| Command::Delete
|
| Command::Delete
|
||||||
@@ -269,6 +270,7 @@ impl CommandManager {
|
|||||||
kb.insert(">".into(), Command::Next);
|
kb.insert(">".into(), Command::Next);
|
||||||
kb.insert("c".into(), Command::Clear);
|
kb.insert("c".into(), Command::Clear);
|
||||||
kb.insert("Space".into(), Command::Queue);
|
kb.insert("Space".into(), Command::Queue);
|
||||||
|
kb.insert("n".into(), Command::PlayNext);
|
||||||
kb.insert("Enter".into(), Command::Play);
|
kb.insert("Enter".into(), Command::Play);
|
||||||
kb.insert("s".into(), Command::Save);
|
kb.insert("s".into(), Command::Save);
|
||||||
kb.insert("Ctrl+s".into(), Command::SaveQueue);
|
kb.insert("Ctrl+s".into(), Command::SaveQueue);
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ impl ListItem for Episode {
|
|||||||
queue.play(index, true, false);
|
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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
queue.append(Playable::Episode(self.clone()));
|
queue.append(Playable::Episode(self.clone()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ impl ListItem for Playable {
|
|||||||
self.as_listitem().play(queue)
|
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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
self.as_listitem().queue(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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
self.load_tracks(queue.get_spotify());
|
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()
|
*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) {
|
pub fn append(&self, track: Playable) {
|
||||||
let mut random_order = self.random_order.write().unwrap();
|
let mut random_order = self.random_order.write().unwrap();
|
||||||
if let Some(order) = random_order.as_mut() {
|
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);
|
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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
self.load_episodes(queue.get_spotify());
|
self.load_episodes(queue.get_spotify());
|
||||||
|
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ use url::Url;
|
|||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::atomic::{AtomicU16, Ordering};
|
use std::sync::atomic::{AtomicU16, Ordering};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use std::{env, io};
|
use std::{env, io};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use crate::artist::Artist;
|
use crate::artist::Artist;
|
||||||
use crate::config;
|
use crate::config;
|
||||||
@@ -401,7 +401,7 @@ impl Spotify {
|
|||||||
) {
|
) {
|
||||||
let bitrate_str = cfg.bitrate.unwrap_or(320).to_string();
|
let bitrate_str = cfg.bitrate.unwrap_or(320).to_string();
|
||||||
let bitrate = Bitrate::from_str(&bitrate_str);
|
let bitrate = Bitrate::from_str(&bitrate_str);
|
||||||
if bitrate.is_err(){
|
if bitrate.is_err() {
|
||||||
error!("invalid bitrate, will use 320 instead")
|
error!("invalid bitrate, will use 320 instead")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ impl ListItem for Track {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn display_center(&self) -> String {
|
fn display_center(&self) -> String {
|
||||||
format!("{}", self.album)
|
self.album.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_right(&self, library: Arc<Library>) -> String {
|
fn display_right(&self, library: Arc<Library>) -> String {
|
||||||
@@ -175,6 +175,10 @@ impl ListItem for Track {
|
|||||||
queue.play(index, true, false);
|
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>) {
|
fn queue(&mut self, queue: Arc<Queue>) {
|
||||||
queue.append(Playable::Track(self.clone()));
|
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 display_right(&self, library: Arc<Library>) -> String;
|
||||||
fn play(&mut self, queue: Arc<Queue>);
|
fn play(&mut self, queue: Arc<Queue>);
|
||||||
fn queue(&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 toggle_saved(&mut self, library: Arc<Library>);
|
||||||
fn save(&mut self, library: Arc<Library>);
|
fn save(&mut self, library: Arc<Library>);
|
||||||
fn unsave(&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));
|
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 => {
|
Command::Queue => {
|
||||||
let mut content = self.content.write().unwrap();
|
let mut content = self.content.write().unwrap();
|
||||||
if let Some(item) = content.get_mut(self.selected) {
|
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);
|
self.queue.play(self.list.get_selected_index(), true, false);
|
||||||
return Ok(CommandResult::Consumed(None));
|
return Ok(CommandResult::Consumed(None));
|
||||||
}
|
}
|
||||||
|
Command::PlayNext => {
|
||||||
|
return Ok(CommandResult::Ignored);
|
||||||
|
}
|
||||||
Command::Queue => {
|
Command::Queue => {
|
||||||
return Ok(CommandResult::Ignored);
|
return Ok(CommandResult::Ignored);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user