293 lines
8.4 KiB
Rust
293 lines
8.4 KiB
Rust
use crabidy_core::proto::crabidy::{Queue, Track};
|
|
use rand::{seq::SliceRandom, thread_rng};
|
|
use std::time::SystemTime;
|
|
use tracing::{debug, error};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct QueueManager {
|
|
created_at: SystemTime,
|
|
current_offset: usize,
|
|
play_order: Vec<usize>,
|
|
tracks: Vec<Track>,
|
|
pub repeat: bool,
|
|
pub shuffle: bool,
|
|
}
|
|
|
|
impl From<QueueManager> for Queue {
|
|
fn from(queue_manager: QueueManager) -> Self {
|
|
Self {
|
|
timestamp: queue_manager
|
|
.created_at
|
|
.elapsed()
|
|
.expect("failed to get elapsed time")
|
|
.as_secs(),
|
|
current_position: queue_manager.current_position() as u32,
|
|
tracks: queue_manager.tracks,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl QueueManager {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
created_at: SystemTime::now(),
|
|
current_offset: 0,
|
|
play_order: Vec::new(),
|
|
tracks: Vec::new(),
|
|
repeat: false,
|
|
shuffle: false,
|
|
}
|
|
}
|
|
pub fn current_position(&self) -> usize {
|
|
if self.current_offset < self.play_order.len() {
|
|
self.play_order[self.current_offset]
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
pub fn is_last_track(&self) -> bool {
|
|
self.current_position() == self.tracks.len() - 1
|
|
}
|
|
|
|
pub fn shuffle_on(&mut self) {
|
|
self.shuffle = true;
|
|
self.shuffle_before(self.current_offset);
|
|
self.shuffle_behind(self.current_offset);
|
|
}
|
|
|
|
pub fn shuffle_off(&mut self) {
|
|
self.shuffle = false;
|
|
let pos = self.current_position();
|
|
self.current_offset = pos;
|
|
self.play_order = (0..self.tracks.len()).collect();
|
|
}
|
|
|
|
pub fn shuffle_all(&mut self) {
|
|
self.play_order.shuffle(&mut thread_rng());
|
|
}
|
|
|
|
pub fn shuffle_before(&mut self, pos: usize) {
|
|
self.play_order[..pos].shuffle(&mut thread_rng());
|
|
}
|
|
|
|
pub fn shuffle_behind(&mut self, pos: usize) {
|
|
self.play_order[pos + 1..].shuffle(&mut thread_rng());
|
|
}
|
|
|
|
pub fn current_track(&self) -> Option<Track> {
|
|
if self.current_position() < self.tracks.len() {
|
|
Some(self.tracks[self.current_position()].clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn next_track(&mut self) -> Option<Track> {
|
|
let len = self.tracks.len();
|
|
if len == 0 {
|
|
return None;
|
|
};
|
|
if self.current_offset < len - 1 {
|
|
self.current_offset += 1;
|
|
let current_pos = self.current_position();
|
|
if current_pos < len {
|
|
Some(self.tracks[current_pos].clone())
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
debug!("no more tracks");
|
|
if self.repeat {
|
|
debug!("repeat");
|
|
self.current_offset = 0;
|
|
if self.shuffle {
|
|
self.shuffle_all();
|
|
}
|
|
return self.current_track();
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn prev_track(&mut self) -> Option<Track> {
|
|
if 0 < self.current_offset {
|
|
self.current_offset -= 1;
|
|
Some(self.tracks[self.current_position()].clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn set_current_position(&mut self, current_position: u32) -> bool {
|
|
if current_position < self.tracks.len() as u32 {
|
|
if self.shuffle {
|
|
self.shuffle_all();
|
|
}
|
|
let Some(current_offset) = self
|
|
.play_order
|
|
.iter()
|
|
.position(|&i| i == current_position as usize)
|
|
else {
|
|
error!("invalid current position");
|
|
error!("queue: {:#?}", self);
|
|
return false
|
|
};
|
|
if self.shuffle {
|
|
self.play_order.swap(0, current_offset);
|
|
self.current_offset = 0;
|
|
} else {
|
|
self.current_offset = current_offset;
|
|
}
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
pub fn replace_with_tracks(&mut self, tracks: &[Track]) -> Option<Track> {
|
|
self.current_offset = 0;
|
|
self.tracks = tracks.to_vec();
|
|
self.play_order = (0..self.tracks.len()).collect();
|
|
if self.shuffle {
|
|
self.shuffle_all();
|
|
}
|
|
if 0 < self.tracks.len() as u32 {
|
|
Some(self.tracks[self.current_position()].clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn append_tracks(&mut self, tracks: &[Track]) -> Option<Track> {
|
|
let len = self.tracks.len();
|
|
let is_empty = len == 0;
|
|
let order_additions: Vec<usize> = (len..len + tracks.len()).collect();
|
|
self.play_order.extend(order_additions);
|
|
self.tracks.extend(tracks.iter().cloned());
|
|
if self.shuffle {
|
|
self.shuffle_behind(self.current_offset);
|
|
}
|
|
if is_empty {
|
|
self.current_track()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn remove_tracks(&mut self, positions: &[u32]) -> Option<Track> {
|
|
let mut play_next = false;
|
|
for pos in positions {
|
|
if (self.tracks.len() as u32) < *pos {
|
|
return None;
|
|
};
|
|
if *pos == self.current_position() as u32 {
|
|
play_next = true;
|
|
}
|
|
let Some(offset) = self
|
|
.play_order
|
|
.iter()
|
|
.position(|&i| i == *pos as usize)
|
|
else {
|
|
error!("invalid current position");
|
|
error!("queue: {:#?}", self);
|
|
return None
|
|
};
|
|
if offset < self.current_offset {
|
|
self.current_offset -= 1;
|
|
}
|
|
self.tracks.remove(*pos as usize);
|
|
self.play_order.remove(offset);
|
|
self.play_order
|
|
.iter_mut()
|
|
.filter(|i| (*pos as usize) < **i)
|
|
.for_each(|i| *i -= 1);
|
|
}
|
|
if play_next {
|
|
self.current_track()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn insert_tracks(&mut self, position: u32, tracks: &[Track]) -> Option<Track> {
|
|
let len = self.tracks.len();
|
|
if len == 0 {
|
|
return self.replace_with_tracks(tracks);
|
|
}
|
|
let order_additions: Vec<usize> = (len..len + tracks.len()).collect();
|
|
self.play_order.extend(order_additions);
|
|
let tail: Vec<Track> = self
|
|
.tracks
|
|
.splice((position as usize + 1).., tracks.to_vec())
|
|
.collect();
|
|
self.tracks.extend(tail);
|
|
let mut changed: Vec<usize> = Vec::new();
|
|
// in shuffle mode, it might be that we played already postions which are behind
|
|
// the insertion point and which postions are shifted by the lenght of the inserted
|
|
// track
|
|
for i in self
|
|
.play_order
|
|
.iter_mut()
|
|
.take(self.current_offset)
|
|
.filter(|i| (position as usize) < **i)
|
|
{
|
|
*i += len;
|
|
changed.push(*i);
|
|
}
|
|
if !self.shuffle {
|
|
// if we don't shuffle, there should be no positions alredy played behind the
|
|
// current track
|
|
assert!(changed.is_empty());
|
|
}
|
|
// the newly inserted indices need to replaced with the ones that we already handled
|
|
self.play_order
|
|
.iter_mut()
|
|
.skip(self.current_offset)
|
|
.for_each(|i| {
|
|
if changed.contains(i) {
|
|
*i -= len;
|
|
}
|
|
});
|
|
|
|
if self.shuffle {
|
|
self.shuffle_behind(self.current_offset);
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn queue_tracks(&mut self, tracks: &[Track]) -> Option<Track> {
|
|
let pos = self.current_position();
|
|
self.insert_tracks(pos as u32, tracks)
|
|
}
|
|
|
|
pub fn clear(&mut self, exclude_current: bool) -> bool {
|
|
let current_track = self.current_track();
|
|
self.current_offset = 0;
|
|
self.tracks.clear();
|
|
|
|
if exclude_current {
|
|
if let Some(track) = current_track {
|
|
self.tracks.push(track);
|
|
}
|
|
}
|
|
|
|
!exclude_current
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn random_delete_before() {}
|
|
#[test]
|
|
fn random_delete_track() {}
|
|
#[test]
|
|
fn random_delete_after() {}
|
|
#[test]
|
|
fn random_select_track() {}
|
|
}
|