2025-08-23 11:46:47 +02:00

736 lines
23 KiB
Rust
Executable File

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<Queue>),
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct QueueFolder {
pub index: usize,
pub content: Vec<Queue>,
pub name: String,
pub order: Option<Vec<usize>>,
}
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<Self>, skip_init: bool) -> Option<usize> {
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<Self>, 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 {
// <total duration> * <total iterations>
let dt = inner.duration_total(db);
for _ in 0..*total {
*dur += dt;
}
} else {
// <remaining duration> + <total duration> * <remaining iterations>
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<usize>, actions: &mut Vec<Action>) {
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<usize>,
actions: &mut Vec<Action>,
) -> 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<usize>,
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<Self> {
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<Queue>, skip_init: bool) -> Option<usize> {
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<Queue>, index: usize, skip_init: bool) -> bool {
if index <= self.content.len() {
if self.index >= index {
self.index += v.len();
}
fn insert_multiple<T>(index: usize, vec: &mut Vec<T>, v: impl IntoIterator<Item = T>) {
// 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<usize>,
actions: &mut Vec<Action>,
) -> 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<T>(vec: &mut Vec<T>, 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<Self::Item> {
if let Some(v) = self.folder.get_at(self.index) {
self.index += 1;
Some(v)
} else {
None
}
}
}
impl From<QueueContent> for Queue {
fn from(value: QueueContent) -> Self {
Self {
enabled: true,
content: value,
}
}
}
impl ToFromBytes for Queue {
fn to_bytes<T>(&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<T>(s: &mut T) -> Result<Self, std::io::Error>
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<T>(&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<T>(s: &mut T) -> Result<Self, std::io::Error>
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: "<invalid byte received>".to_string(),
order: None,
}),
})
}
}
impl ToFromBytes for QueueFolder {
fn to_bytes<T>(&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<T>(s: &mut T) -> Result<Self, std::io::Error>
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 <random> element - only accurate the first time it is reached in queue.
pub random_known_millis: u64,
/// number of <random> 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<QueueDuration> 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;
}
}