Added song duration support

This commit is contained in:
Mark
2023-11-04 20:06:54 +01:00
parent 273eac4b43
commit 7cae108ffa
14 changed files with 701 additions and 216 deletions

View File

@@ -359,6 +359,11 @@ impl Database {
Command::RemoveArtist(artist) => {
_ = self.remove_artist(artist);
}
Command::SetSongDuration(id, duration) => {
if let Some(song) = self.get_song_mut(&id) {
song.duration_millis = duration;
}
}
Command::InitComplete => {
self.client_is_init = true;
}

View File

@@ -1,4 +1,4 @@
use std::collections::VecDeque;
use std::{collections::VecDeque, ops::AddAssign};
use rand::seq::{IteratorRandom, SliceRandom};
@@ -98,6 +98,60 @@ impl Queue {
QueueContent::Shuffle { inner, state: _ } => 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(c, inner, _) => {
for (i, inner) in inner.iter().enumerate() {
if dur.include_past || i >= *c {
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;
}
}
}
QueueContent::Random(q) => {
if let Some(el) = q.iter().next() {
dur.random_known_millis += el.duration_total(db).millis;
}
dur.random_counter += 1;
}
QueueContent::Shuffle { inner, state: _ } => {
inner.add_duration(dur, db);
}
}
}
}
/// recursively descends the queue until the current active element is found, then returns it.
pub fn get_current(&self) -> Option<&Self> {
@@ -641,3 +695,42 @@ impl ToFromBytes for ShuffleState {
})
}
}
#[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;
}
}

View File

@@ -22,6 +22,9 @@ pub struct Song {
pub artist: ArtistId,
pub more_artists: Vec<ArtistId>,
pub cover: Option<CoverId>,
pub file_size: u64,
/// song duration in milliseconds
pub duration_millis: u64,
pub general: GeneralData,
/// None => No cached data
/// Some(Err) => No cached data yet, but a thread is working on loading it.
@@ -36,6 +39,8 @@ impl Song {
artist: ArtistId,
more_artists: Vec<ArtistId>,
cover: Option<CoverId>,
file_size: u64,
duration_millis: u64,
) -> Self {
Self {
id: 0,
@@ -45,6 +50,8 @@ impl Song {
artist,
more_artists,
cover,
file_size,
duration_millis,
general: GeneralData::default(),
cached_data: Arc::new(Mutex::new(None)),
}
@@ -185,6 +192,8 @@ impl ToFromBytes for Song {
self.artist.to_bytes(s)?;
self.more_artists.to_bytes(s)?;
self.cover.to_bytes(s)?;
self.file_size.to_bytes(s)?;
self.duration_millis.to_bytes(s)?;
self.general.to_bytes(s)?;
Ok(())
}
@@ -200,6 +209,8 @@ impl ToFromBytes for Song {
artist: ToFromBytes::from_bytes(s)?,
more_artists: ToFromBytes::from_bytes(s)?,
cover: ToFromBytes::from_bytes(s)?,
file_size: ToFromBytes::from_bytes(s)?,
duration_millis: ToFromBytes::from_bytes(s)?,
general: ToFromBytes::from_bytes(s)?,
cached_data: Arc::new(Mutex::new(None)),
})

View File

@@ -49,6 +49,7 @@ pub enum Command {
RemoveAlbum(AlbumId),
RemoveArtist(ArtistId),
ModifyArtist(Artist),
SetSongDuration(SongId, u64),
InitComplete,
ErrorInfo(String, String),
}
@@ -102,7 +103,7 @@ pub fn run_server(
let command_sender = command_sender.clone();
let db = Arc::clone(&database);
thread::spawn(move || loop {
if let Ok((connection, con_addr)) = v.accept() {
if let Ok((connection, _con_addr)) = v.accept() {
let command_sender = command_sender.clone();
let db = Arc::clone(&db);
thread::spawn(move || {
@@ -275,6 +276,11 @@ impl ToFromBytes for Command {
s.write_all(&[0b11011100])?;
artist.to_bytes(s)?;
}
Self::SetSongDuration(i, d) => {
s.write_all(&[0b11100000])?;
i.to_bytes(s)?;
d.to_bytes(s)?;
}
Self::InitComplete => {
s.write_all(&[0b00110001])?;
}
@@ -326,6 +332,9 @@ impl ToFromBytes for Command {
0b11010000 => Self::RemoveSong(ToFromBytes::from_bytes(s)?),
0b11010011 => Self::RemoveAlbum(ToFromBytes::from_bytes(s)?),
0b11011100 => Self::RemoveArtist(ToFromBytes::from_bytes(s)?),
0b11100000 => {
Self::SetSongDuration(ToFromBytes::from_bytes(s)?, ToFromBytes::from_bytes(s)?)
}
0b01011101 => Self::AddCover(ToFromBytes::from_bytes(s)?),
0b00110001 => Self::InitComplete,
0b11011011 => Self::ErrorInfo(ToFromBytes::from_bytes(s)?, ToFromBytes::from_bytes(s)?),