use std::ops::AddAssign; use crate::{load::ToFromBytes, server::Action}; use super::{database::Database, SongId}; #[derive(Clone, Debug, PartialEq)] pub struct Queue { enabled: bool, content: QueueContent, } #[derive(Clone, Debug, PartialEq)] pub enum QueueContent { Song(SongId), Folder(QueueFolder), Loop(usize, usize, Box), } #[derive(Clone, Debug, Default, PartialEq)] pub struct QueueFolder { pub index: usize, pub content: Vec, pub name: String, pub order: Option>, } impl Queue { pub fn enabled(&self) -> bool { self.enabled } pub fn content(&self) -> &QueueContent { &self.content } pub fn content_mut(&mut self) -> &mut QueueContent { &mut self.content } pub fn add_to_end(&mut self, v: Vec, skip_init: bool) -> Option { match &mut self.content { QueueContent::Song(_) => None, QueueContent::Folder(folder) => folder.add_to_end(v, skip_init), QueueContent::Loop(..) => None, } } pub fn is_current(&self, index: &[usize]) -> bool { if index.is_empty() { return true; } match self.content() { QueueContent::Song(_) => true, QueueContent::Folder(folder) => { folder.index == index[0] && folder .get_current_immut() .is_some_and(|c| c.is_current(&index[1..])) } QueueContent::Loop(_, _, inner) => index[0] == 0 && inner.is_current(&index[1..]), } } pub fn insert(&mut self, v: Vec, index: usize, skip_init: bool) -> bool { match &mut self.content { QueueContent::Song(_) => false, QueueContent::Folder(folder) => folder.insert(v, index, skip_init), QueueContent::Loop(..) => false, } } pub fn is_empty(&self) -> bool { if !self.enabled { return true; } match &self.content { QueueContent::Song(_) => false, QueueContent::Folder(folder) => folder.content.iter().all(|v| v.is_empty()), QueueContent::Loop(_total, _done, inner) => inner.is_empty(), } } /// returns true if there is at most one song in the queue pub fn is_almost_empty(&self) -> bool { self.is_almost_empty_int() < 2 } fn is_almost_empty_int(&self) -> u8 { if !self.enabled { return 0; } match &self.content { QueueContent::Song(_) => 1, QueueContent::Folder(folder) => { let mut o = 0; for v in folder.content.iter() { o += v.is_almost_empty_int(); if o >= 2 { return 2; } } o } QueueContent::Loop(_total, _done, inner) => inner.is_almost_empty_int(), } } pub fn len(&self) -> usize { if !self.enabled { return 0; } match &self.content { QueueContent::Song(_) => 1, QueueContent::Folder(folder) => folder.len(), QueueContent::Loop(total, _done, inner) => { if *total == 0 { inner.len() } else { total.saturating_mul(inner.len()) } } } } pub fn duration_total(&self, db: &Database) -> QueueDuration { let mut dur = QueueDuration::new_total(); self.add_duration(&mut dur, db); dur } // remaining time, including current song pub fn duration_remaining(&self, db: &Database) -> QueueDuration { let mut dur = QueueDuration::new_remaining(); self.add_duration(&mut dur, db); dur } pub fn add_duration(&self, dur: &mut QueueDuration, db: &Database) { if self.enabled { match &self.content { QueueContent::Song(v) => { dur.millis += db.get_song(v).map(|s| s.duration_millis).unwrap_or(0) } QueueContent::Folder(QueueFolder { index, content, name: _, order: _, }) => { for (i, inner) in content.iter().enumerate() { if dur.include_past || i >= *index { inner.add_duration(dur, db); } } } QueueContent::Loop(total, done, inner) => { if *total == 0 { dur.infinite = true; } else if dur.include_past { // * let dt = inner.duration_total(db); for _ in 0..*total { *dur += dt; } } else { // + * inner.add_duration(dur, db); let dt = inner.duration_total(db); for _ in 0..(total.saturating_sub(*done + 1)) { *dur += dt; } } } } } } /// recursively descends the queue until the current active element is found, then returns it. pub fn get_current(&self) -> Option<&Self> { match &self.content { QueueContent::Song(_) => Some(self), QueueContent::Folder(folder) => folder.get_current_immut()?.get_current(), QueueContent::Loop(_, _, inner) => inner.get_current(), } } pub fn get_current_song(&self) -> Option<&SongId> { if let QueueContent::Song(id) = self.get_current()?.content() { Some(id) } else { None } } pub fn get_next_song(&self) -> Option<&SongId> { if let QueueContent::Song(id) = self.get_next()?.content() { Some(id) } else { None } } pub fn get_next(&self) -> Option<&Self> { match &self.content { QueueContent::Song(_) => None, QueueContent::Folder(folder) => folder.get_next(), QueueContent::Loop(total, current, inner) => { if let Some(v) = inner.get_next() { Some(v) } else if *total == 0 || current < total { inner.get_first() } else { None } } } } pub fn get_first(&self) -> Option<&Self> { match &self.content { QueueContent::Song(..) => Some(self), QueueContent::Folder(folder) => folder.get_first(), QueueContent::Loop(_, _, q) => q.get_first(), } } pub fn advance_index_db(db: &mut Database) -> bool { let mut actions = Vec::new(); let o = db.queue.advance_index_inner(&mut Vec::new(), &mut actions); for action in actions { db.apply_action_unchecked_seq(action, None); } o } pub fn init(&mut self) { match &mut self.content { QueueContent::Song(..) => {} QueueContent::Folder(folder) => { folder.index = 0; for v in &mut folder.content { v.init(); } } QueueContent::Loop(_, _, inner) => inner.init(), } } pub fn done(&mut self, path: &Vec, actions: &mut Vec) { match &mut self.content { QueueContent::Song(..) => {} QueueContent::Folder(folder) => { if folder.order.is_some() { actions.push(Action::QueueShuffle(path.clone(), 0)); } } QueueContent::Loop(_, _, _) => {} } } pub fn advance_index_inner( &mut self, path: &mut Vec, actions: &mut Vec, ) -> bool { match &mut self.content { QueueContent::Song(_) => { self.done(path, actions); false } QueueContent::Folder(folder) => { if folder.advance_index_inner(path, actions) { true } else { self.done(path, actions); false } } QueueContent::Loop(total, current, inner) => { path.push(0); if inner.advance_index_inner(path, actions) { path.pop(); true } else { inner.done(path, actions); path.pop(); *current += 1; if *total == 0 || *current < *total { inner.init(); true } else { *current = 0; self.done(path, actions); false } } } } } pub fn set_index_db(db: &mut Database, index: &[usize]) { db.queue.reset_index(); db.queue.set_index_inner(index, 0, vec![], false); } pub fn set_index_inner( &mut self, index: &[usize], depth: usize, mut build_index: Vec, keep_child_indices: bool, ) { let i = if let Some(i) = index.get(depth) { *i } else { return; }; build_index.push(i); match &mut self.content { QueueContent::Song(_) => {} QueueContent::Folder(folder) => { folder.index = i; if let Some(c) = folder.get_current_mut() { if !keep_child_indices { c.init(); } c.set_index_inner(index, depth + 1, build_index, keep_child_indices); } } QueueContent::Loop(_, _, inner) => { if !keep_child_indices { inner.init(); } inner.set_index_inner(index, depth + 1, build_index, keep_child_indices) } } } pub fn reset_index(&mut self) { match self.content_mut() { QueueContent::Song(_) => {} QueueContent::Folder(folder) => { folder.index = 0; for v in &mut folder.content { v.reset_index(); } } QueueContent::Loop(_, done, i) => { *done = 0; i.reset_index(); } } } pub fn get_item_at_index(&self, index: &[usize], depth: usize) -> Option<&Self> { if let Some(i) = index.get(depth) { match &self.content { QueueContent::Song(_) => None, QueueContent::Folder(folder) => { if let Some(v) = folder.get_at(*i) { v.get_item_at_index(index, depth + 1) } else { None } } QueueContent::Loop(_, _, inner) => inner.get_item_at_index(index, depth + 1), } } else { Some(self) } } pub fn get_item_at_index_mut(&mut self, index: &[usize], depth: usize) -> Option<&mut Self> { if let Some(i) = index.get(depth) { match &mut self.content { QueueContent::Song(_) => None, QueueContent::Folder(folder) => { if let Some(v) = folder.get_mut_at(*i) { v.get_item_at_index_mut(index, depth + 1) } else { None } } QueueContent::Loop(_, _, inner) => inner.get_item_at_index_mut(index, depth + 1), } } else { Some(self) } } pub fn remove_by_index(&mut self, index: &[usize], depth: usize) -> Option { if let Some(i) = index.get(depth) { match &mut self.content { QueueContent::Song(_) => None, QueueContent::Folder(folder) => { if depth + 1 < index.len() { if let Some(v) = folder.get_mut_at(*i) { v.remove_by_index(index, depth + 1) } else { None } } else { if *i < folder.content.len() { // if current playback is past this point, // reduce the index by 1 so that it still points to the same element if folder.index > *i { folder.index -= 1; } let idx = if let Some(order) = &mut folder.order { let idx = order.remove(*i); // compensate for removal of element from .content for o in order { if *o > idx { *o -= 1; } } idx } else { *i }; Some(folder.content.remove(idx)) } else { None } } } QueueContent::Loop(_, _, inner) => { if depth + 1 < index.len() { inner.remove_by_index(index, depth + 1) } else { None } } } } else { None } } } impl QueueFolder { pub fn iter(&self) -> QueueFolderIter<'_> { QueueFolderIter { folder: self, index: 0, } } pub fn add_to_end(&mut self, v: Vec, skip_init: bool) -> Option { let add_len = v.len(); let len = self.content.len(); for mut v in v.into_iter() { if !skip_init { v.init(); } self.content.push(v); } if let Some(order) = &mut self.order { for i in 0..add_len { order.push(len + i); } } Some(len) } pub fn insert(&mut self, v: Vec, index: usize, skip_init: bool) -> bool { if index <= self.content.len() { if self.index >= index { self.index += v.len(); } fn insert_multiple(index: usize, vec: &mut Vec, v: impl IntoIterator) { // remove the elements starting at the insertion point let end = vec.split_off(index); // insert new elements for v in v { vec.push(v); } // re-add previously removed elements vec.extend(end); } let mapfunc = |mut v: Queue| { if !skip_init { v.init(); } v }; if let Some(order) = &mut self.order { insert_multiple(index, order, (0..v.len()).map(|i| self.content.len() + i)); self.content.extend(v.into_iter().map(mapfunc)); } else { insert_multiple(index, &mut self.content, v.into_iter().map(mapfunc)); } true } else { false } } pub fn len(&self) -> usize { self.content.iter().map(|v| v.len()).sum() } pub fn get_at(&self, mut i: usize) -> Option<&Queue> { if let Some(order) = &self.order { i = *order.get(i)?; } self.content.get(i) } pub fn get_mut_at(&mut self, mut i: usize) -> Option<&mut Queue> { if let Some(order) = &self.order { i = *order.get(i)?; } self.content.get_mut(i) } pub fn get_current_immut(&self) -> Option<&Queue> { self.get_at(self.index) } pub fn get_current_mut(&mut self) -> Option<&mut Queue> { self.get_mut_at(self.index) } pub fn get_next(&self) -> Option<&Queue> { if let Some(v) = self.get_current_immut() { if let Some(v) = v.get_next() { Some(v) } else { if let Some(v) = self.get_at(self.index + 1) { v.get_current() } else { None } } } else { None } } pub fn get_first(&self) -> Option<&Queue> { if let Some(order) = &self.order { self.content.get(*order.first()?) } else { self.content.first() } } pub fn advance_index_inner( &mut self, path: &mut Vec, actions: &mut Vec, ) -> bool { let index = self.index; if let Some(c) = self.get_current_mut() { path.push(index); if c.advance_index_inner(path, actions) { path.pop(); // inner value could advance index, do nothing. true } else { path.pop(); loop { if self.index + 1 < self.content.len() { // can advance self.index += 1; if self.content[self.index].enabled { self.content[self.index].init(); break true; } } else { // can't advance: index would be out of bounds self.index = 0; break false; } } } } else { self.index = 0; false } } pub fn move_elem(&mut self, index_from: usize, index_to: usize) -> bool { fn vec_move(vec: &mut Vec, from: usize, to: usize) -> bool { if from < vec.len() && to < vec.len() { if from == to { return true; } let elem = vec.remove(from); vec.insert(to, elem); true } else { false } } if self.index == index_from { self.index = index_to; } else if index_from < self.index && self.index <= index_to { self.index -= 1; } else if index_to <= self.index && self.index < index_from { self.index += 1; } if let Some(order) = &mut self.order { vec_move(order, index_from, index_to) } else { vec_move(&mut self.content, index_from, index_to) } } } pub struct QueueFolderIter<'a> { folder: &'a QueueFolder, index: usize, } impl<'a> Iterator for QueueFolderIter<'a> { type Item = &'a Queue; fn next(&mut self) -> Option { if let Some(v) = self.folder.get_at(self.index) { self.index += 1; Some(v) } else { None } } } impl From for Queue { fn from(value: QueueContent) -> Self { Self { enabled: true, content: value, } } } impl ToFromBytes for Queue { fn to_bytes(&self, s: &mut T) -> Result<(), std::io::Error> where T: std::io::Write, { s.write_all(&[if self.enabled { 0b11111111 } else { 0b00000000 }])?; self.content.to_bytes(s)?; Ok(()) } fn from_bytes(s: &mut T) -> Result where T: std::io::Read, { let mut enabled = [0]; s.read_exact(&mut enabled)?; Ok(Self { enabled: enabled[0].count_ones() >= 4, content: ToFromBytes::from_bytes(s)?, }) } } impl ToFromBytes for QueueContent { fn to_bytes(&self, s: &mut T) -> Result<(), std::io::Error> where T: std::io::Write, { match self { Self::Song(id) => { s.write_all(&[0b11111111])?; id.to_bytes(s)?; } Self::Folder(folder) => { s.write_all(&[0b00000000])?; ToFromBytes::to_bytes(folder, s)?; } Self::Loop(total, current, inner) => { s.write_all(&[0b11000000])?; total.to_bytes(s)?; current.to_bytes(s)?; inner.to_bytes(s)?; } } Ok(()) } fn from_bytes(s: &mut T) -> Result where T: std::io::Read, { let mut switch_on = [0]; s.read_exact(&mut switch_on)?; Ok(match switch_on[0] { 0b11111111 => Self::Song(ToFromBytes::from_bytes(s)?), 0b00000000 => Self::Folder(ToFromBytes::from_bytes(s)?), 0b11000000 => Self::Loop( ToFromBytes::from_bytes(s)?, ToFromBytes::from_bytes(s)?, Box::new(ToFromBytes::from_bytes(s)?), ), _ => Self::Folder(QueueFolder { index: 0, content: vec![], name: "".to_string(), order: None, }), }) } } impl ToFromBytes for QueueFolder { fn to_bytes(&self, s: &mut T) -> Result<(), std::io::Error> where T: std::io::prelude::Write, { ToFromBytes::to_bytes(&self.index, s)?; ToFromBytes::to_bytes(&self.content, s)?; ToFromBytes::to_bytes(&self.name, s)?; ToFromBytes::to_bytes(&self.order, s)?; Ok(()) } fn from_bytes(s: &mut T) -> Result where T: std::io::prelude::Read, { let v = Self { index: ToFromBytes::from_bytes(s)?, content: ToFromBytes::from_bytes(s)?, name: ToFromBytes::from_bytes(s)?, order: ToFromBytes::from_bytes(s)?, }; Ok(v) } } #[derive(Clone, Copy)] pub struct QueueDuration { pub include_past: bool, pub infinite: bool, /// number of milliseconds (that we know of) pub millis: u64, /// number of milliseconds from the element - only accurate the first time it is reached in queue. pub random_known_millis: u64, /// number of elements, which could have pretty much any duration. pub random_counter: u64, } impl QueueDuration { fn new_total() -> Self { Self::new(true) } fn new_remaining() -> Self { Self::new(false) } fn new(include_past: bool) -> Self { QueueDuration { include_past, infinite: false, millis: 0, random_known_millis: 0, random_counter: 0, } } } impl AddAssign for QueueDuration { fn add_assign(&mut self, rhs: QueueDuration) { if rhs.infinite { self.infinite = true; } self.millis += rhs.millis; self.random_known_millis += rhs.random_known_millis; self.random_counter += rhs.random_counter; } }