use std::cmp::Ordering; use std::sync::{Arc, RwLock}; use log::{debug, error, info}; #[cfg(feature = "notify")] use notify_rust::Notification; use rand::prelude::*; use strum_macros::Display; use crate::playable::Playable; use crate::spotify::Spotify; use crate::{config::Config, spotify::PlayerEvent}; #[derive(Display, Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] pub enum RepeatSetting { #[serde(rename = "off")] None, #[serde(rename = "playlist")] RepeatPlaylist, #[serde(rename = "track")] RepeatTrack, } #[derive(Clone, Debug, PartialEq)] pub enum QueueEvent { PreloadTrackRequest, } pub struct Queue { pub queue: Arc>>, random_order: RwLock>>, current_track: RwLock>, spotify: Spotify, cfg: Arc, } impl Queue { pub fn new(spotify: Spotify, cfg: Arc) -> Queue { let state = cfg.state().queuestate.clone(); let queue = Queue { queue: Arc::new(RwLock::new(state.queue)), spotify: spotify.clone(), current_track: RwLock::new(state.current_track), random_order: RwLock::new(state.random_order), cfg, }; if let Some(playable) = queue.get_current() { spotify.load(&playable, false, state.track_progress.as_millis() as u32); spotify.update_track(); spotify.pause(); spotify.seek(state.track_progress.as_millis() as u32); } queue } pub fn next_index(&self) -> Option { 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 { 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 { match self.get_current_index() { Some(index) => Some(self.queue.read().unwrap()[index].clone()), None => None, } } pub fn get_current_index(&self) -> Option { *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 for item in order.iter_mut() { if *item > index { *item += 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) -> 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, true, 0); 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) { match self.spotify.get_current_status() { PlayerEvent::Playing(_) | PlayerEvent::Paused(_) => { self.spotify.toggleplayback(); } PlayerEvent::Stopped => match self.next_index() { Some(_) => self.next(false), None => self.play(0, false, false), }, _ => (), } } 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.cfg.state().repeat; 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.cfg.state().repeat; 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 { self.cfg.state().repeat } pub fn set_repeat(&self, new: RepeatSetting) { self.cfg.with_state_mut(|mut s| s.repeat = new); } pub fn get_shuffle(&self) -> bool { self.cfg.state().shuffle } pub fn get_random_order(&self) -> Option> { self.random_order.read().unwrap().clone() } fn generate_random_order(&self) { let q = self.queue.read().unwrap(); let mut order: Vec = Vec::with_capacity(q.len()); let mut random: Vec = (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) { self.cfg.with_state_mut(|mut s| s.shuffle = new); if new { self.generate_random_order(); } else { let mut random_order = self.random_order.write().unwrap(); *random_order = None; } } pub fn handle_event(&self, event: QueueEvent) { match event { QueueEvent::PreloadTrackRequest => { if let Some(next_index) = self.next_index() { let track = self.queue.read().unwrap()[next_index].clone(); debug!("Preloading track {} as requested by librespot", track); self.spotify.preload(&track); } } } } pub fn get_spotify(&self) -> Spotify { self.spotify.clone() } }