Files
ncspot/src/queue.rs
Henrik Friedrichsen 013beb245b refactor: pass globally mutable config reference
Before, copies of the configuration were passed over. This change also
causes configuration reloads to affect the entire application and is
easier to maintain but introduces some RwLock overhead.
2020-10-18 13:09:45 +02:00

372 lines
12 KiB
Rust

use std::cmp::Ordering;
use std::sync::{Arc, RwLock};
#[cfg(feature = "notify")]
use notify_rust::Notification;
use rand::prelude::*;
use strum_macros::Display;
use crate::config::Config;
use crate::playable::Playable;
use crate::spotify::Spotify;
#[derive(Display, Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum RepeatSetting {
None,
RepeatPlaylist,
RepeatTrack,
}
pub struct Queue {
pub queue: Arc<RwLock<Vec<Playable>>>,
random_order: RwLock<Option<Vec<usize>>>,
current_track: RwLock<Option<usize>>,
repeat: RwLock<RepeatSetting>,
spotify: Arc<Spotify>,
cfg: Arc<Config>,
}
impl Queue {
pub fn new(spotify: Arc<Spotify>, cfg: Arc<Config>) -> Queue {
let q = Queue {
queue: Arc::new(RwLock::new(Vec::new())),
spotify,
current_track: RwLock::new(None),
repeat: RwLock::new(RepeatSetting::None),
random_order: RwLock::new(None),
cfg,
};
q.set_repeat(q.spotify.repeat);
q.set_shuffle(q.spotify.shuffle);
q
}
pub fn next_index(&self) -> Option<usize> {
match *self.current_track.read().unwrap() {
Some(mut index) => {
let random_order = self.random_order.read().unwrap();
if let Some(order) = random_order.as_ref() {
index = order.iter().position(|&i| i == index).unwrap();
}
let mut next_index = index + 1;
if next_index < self.queue.read().unwrap().len() {
if let Some(order) = random_order.as_ref() {
next_index = order[next_index];
}
Some(next_index)
} else {
None
}
}
None => None,
}
}
pub fn previous_index(&self) -> Option<usize> {
match *self.current_track.read().unwrap() {
Some(mut index) => {
let random_order = self.random_order.read().unwrap();
if let Some(order) = random_order.as_ref() {
index = order.iter().position(|&i| i == index).unwrap();
}
if index > 0 {
let mut next_index = index - 1;
if let Some(order) = random_order.as_ref() {
next_index = order[next_index];
}
Some(next_index)
} else {
None
}
}
None => None,
}
}
pub fn get_current(&self) -> Option<Playable> {
match self.get_current_index() {
Some(index) => Some(self.queue.read().unwrap()[index].clone()),
None => None,
}
}
pub fn get_current_index(&self) -> Option<usize> {
*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() {
let index = order.len().saturating_sub(1);
order.push(index);
}
let mut q = self.queue.write().unwrap();
q.push(track);
}
pub fn append_next(&self, tracks: Vec<Playable>) -> usize {
let mut q = self.queue.write().unwrap();
{
let mut random_order = self.random_order.write().unwrap();
if let Some(order) = random_order.as_mut() {
order.extend((q.len().saturating_sub(1))..(q.len() + tracks.len()));
}
}
let first = match *self.current_track.read().unwrap() {
Some(index) => index + 1,
None => q.len(),
};
let mut i = first;
for track in tracks {
q.insert(i, track.clone());
i += 1;
}
first
}
pub fn remove(&self, index: usize) {
{
let mut q = self.queue.write().unwrap();
if q.len() == 0 {
info!("queue is empty");
return;
}
q.remove(index);
}
// if the queue is empty stop playback
let len = self.queue.read().unwrap().len();
if len == 0 {
self.stop();
return;
}
// if we are deleting the currently playing track, play the track with
// the same index again, because the next track is now at the position
// of the one we deleted
let current = *self.current_track.read().unwrap();
if let Some(current_track) = current {
match current_track.cmp(&index) {
Ordering::Equal => {
// if we have deleted the last item and it was playing
// stop playback, unless repeat playlist is on, play next
if current_track == len {
if self.get_repeat() == RepeatSetting::RepeatPlaylist {
self.next(false);
} else {
self.stop();
}
} else {
self.play(index, false, false);
}
}
Ordering::Greater => {
let mut current = self.current_track.write().unwrap();
current.replace(current_track - 1);
}
_ => (),
}
}
if self.get_shuffle() {
self.generate_random_order();
}
}
pub fn clear(&self) {
self.stop();
let mut q = self.queue.write().unwrap();
q.clear();
let mut random_order = self.random_order.write().unwrap();
if let Some(o) = random_order.as_mut() {
o.clear()
}
}
pub fn len(&self) -> usize {
self.queue.read().unwrap().len()
}
pub fn shift(&self, from: usize, to: usize) {
let mut queue = self.queue.write().unwrap();
let item = queue.remove(from);
queue.insert(to, item);
// if the currently playing track is affected by the shift, update its
// index
let mut current = self.current_track.write().unwrap();
if let Some(index) = *current {
if index == from {
current.replace(to);
} else if index == to && from > index {
current.replace(to + 1);
} else if index == to && from < index {
current.replace(to - 1);
}
}
}
pub fn play(&self, mut index: usize, reshuffle: bool, shuffle_index: bool) {
if shuffle_index && self.get_shuffle() {
let mut rng = rand::thread_rng();
index = rng.gen_range(0, &self.queue.read().unwrap().len());
}
if let Some(track) = &self.queue.read().unwrap().get(index) {
self.spotify.load(&track);
let mut current = self.current_track.write().unwrap();
current.replace(index);
self.spotify.update_track();
if self.cfg.values().notify.unwrap_or(false) {
#[cfg(feature = "notify")]
if let Err(e) = Notification::new().summary(&track.to_string()).show() {
error!("error showing notification: {:?}", e);
}
}
}
if reshuffle && self.get_shuffle() {
self.generate_random_order()
}
}
pub fn toggleplayback(&self) {
self.spotify.toggleplayback();
}
pub fn stop(&self) {
let mut current = self.current_track.write().unwrap();
*current = None;
self.spotify.stop();
}
pub fn next(&self, manual: bool) {
let q = self.queue.read().unwrap();
let current = *self.current_track.read().unwrap();
let repeat = *self.repeat.read().unwrap();
if repeat == RepeatSetting::RepeatTrack && !manual {
if let Some(index) = current {
self.play(index, false, false);
}
} else if let Some(index) = self.next_index() {
self.play(index, false, false);
if repeat == RepeatSetting::RepeatTrack && manual {
self.set_repeat(RepeatSetting::RepeatPlaylist);
}
} else if repeat == RepeatSetting::RepeatPlaylist && q.len() > 0 {
let random_order = self.random_order.read().unwrap();
self.play(
random_order.as_ref().map(|o| o[0]).unwrap_or(0),
false,
false,
);
} else {
self.spotify.stop();
}
}
pub fn previous(&self) {
let q = self.queue.read().unwrap();
let current = *self.current_track.read().unwrap();
let repeat = *self.repeat.read().unwrap();
if let Some(index) = self.previous_index() {
self.play(index, false, false);
} else if repeat == RepeatSetting::RepeatPlaylist && q.len() > 0 {
if self.get_shuffle() {
let random_order = self.random_order.read().unwrap();
self.play(
random_order.as_ref().map(|o| o[q.len() - 1]).unwrap_or(0),
false,
false,
);
} else {
self.play(q.len() - 1, false, false);
}
} else if let Some(index) = current {
self.play(index, false, false);
}
}
pub fn get_repeat(&self) -> RepeatSetting {
let repeat = self.repeat.read().unwrap();
*repeat
}
pub fn set_repeat(&self, new: RepeatSetting) {
let mut repeat = self.repeat.write().unwrap();
*repeat = new;
}
pub fn get_shuffle(&self) -> bool {
let random_order = self.random_order.read().unwrap();
random_order.is_some()
}
fn generate_random_order(&self) {
let q = self.queue.read().unwrap();
let mut order: Vec<usize> = Vec::with_capacity(q.len());
let mut random: Vec<usize> = (0..q.len()).collect();
if let Some(current) = *self.current_track.read().unwrap() {
order.push(current);
random.remove(current);
}
let mut rng = rand::thread_rng();
random.shuffle(&mut rng);
order.extend(random);
let mut random_order = self.random_order.write().unwrap();
*random_order = Some(order);
}
pub fn set_shuffle(&self, new: bool) {
if new {
self.generate_random_order();
} else {
let mut random_order = self.random_order.write().unwrap();
*random_order = None;
}
}
pub fn get_spotify(&self) -> Arc<Spotify> {
self.spotify.clone()
}
}