mirror of
https://github.com/Dummi26/musicdb.git
synced 2025-12-14 20:06:16 +01:00
improve website, add "sleep" playback backend
This commit is contained in:
@@ -17,5 +17,6 @@ default = []
|
||||
playback = []
|
||||
default-playback = ["playback-via-playback-rs"]
|
||||
# default-playback = ["playback-via-rodio"]
|
||||
playback-via-sleep = ["playback"]
|
||||
playback-via-playback-rs = ["playback", "dep:playback-rs"]
|
||||
playback-via-rodio = ["playback", "dep:rodio"]
|
||||
|
||||
@@ -186,7 +186,7 @@ impl CachedData {
|
||||
}
|
||||
/// Gets the cached data, if available.
|
||||
/// If a thread is running to load the data, it is awaited.
|
||||
/// This function doesn't block.
|
||||
/// This function blocks!
|
||||
pub fn cached_data_await(&self) -> Option<Arc<Vec<u8>>> {
|
||||
let mut cd = self.0.lock().unwrap();
|
||||
let (out, next) = match replace(&mut cd.0, Err(None)) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
pub mod playback_rs;
|
||||
#[cfg(feature = "playback-via-rodio")]
|
||||
pub mod rodio;
|
||||
#[cfg(feature = "playback-via-sleep")]
|
||||
pub mod sleep;
|
||||
#[cfg(feature = "playback-via-playback-rs")]
|
||||
pub type PlayerBackendFeat<T> = playback_rs::PlayerBackendPlaybackRs<T>;
|
||||
#[cfg(feature = "playback-via-rodio")]
|
||||
@@ -10,7 +12,11 @@ pub type PlayerBackendFeat<T> = rodio::PlayerBackendRodio<T>;
|
||||
use std::{collections::HashMap, ffi::OsStr, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
data::{database::Database, song::CachedData, SongId},
|
||||
data::{
|
||||
database::Database,
|
||||
song::{CachedData, Song},
|
||||
SongId,
|
||||
},
|
||||
server::Action,
|
||||
};
|
||||
|
||||
@@ -28,6 +34,7 @@ pub trait PlayerBackend<T> {
|
||||
fn load_next_song(
|
||||
&mut self,
|
||||
id: SongId,
|
||||
song: &Song,
|
||||
filename: &OsStr,
|
||||
bytes: Arc<Vec<u8>>,
|
||||
load_duration: bool,
|
||||
@@ -158,6 +165,7 @@ impl<T: PlayerBackend<SongCustomData>> Player<T> {
|
||||
let load_duration = song.duration_millis == 0;
|
||||
self.backend.load_next_song(
|
||||
id,
|
||||
song,
|
||||
song.location
|
||||
.rel_path
|
||||
.file_name()
|
||||
@@ -211,6 +219,7 @@ impl<T: PlayerBackend<SongCustomData>> Player<T> {
|
||||
let load_duration = song.duration_millis == 0;
|
||||
self.backend.load_next_song(
|
||||
id,
|
||||
song,
|
||||
song.location
|
||||
.rel_path
|
||||
.file_name()
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{ffi::OsStr, io::Cursor, path::Path, sync::Arc, time::Duration};
|
||||
use playback_rs::Hint;
|
||||
|
||||
use crate::{
|
||||
data::SongId,
|
||||
data::{song::Song, SongId},
|
||||
server::{Action, Command},
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@ impl<T> PlayerBackend<T> for PlayerBackendPlaybackRs<T> {
|
||||
fn load_next_song(
|
||||
&mut self,
|
||||
id: SongId,
|
||||
_song: &Song,
|
||||
filename: &OsStr,
|
||||
bytes: Arc<Vec<u8>>,
|
||||
_load_duration: bool,
|
||||
|
||||
@@ -4,7 +4,7 @@ use rc_u8_reader::ArcU8Reader;
|
||||
use rodio::{decoder::DecoderError, Decoder, OutputStream, OutputStreamHandle, Sink, Source};
|
||||
|
||||
use crate::{
|
||||
data::SongId,
|
||||
data::{song::Song, SongId},
|
||||
server::{Action, Command},
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ impl<T> PlayerBackend<T> for PlayerBackendRodio<T> {
|
||||
fn load_next_song(
|
||||
&mut self,
|
||||
id: SongId,
|
||||
_song: &Song,
|
||||
_filename: &OsStr,
|
||||
bytes: Arc<Vec<u8>>,
|
||||
_load_duration: bool,
|
||||
|
||||
146
musicdb-lib/src/player/sleep.rs
Normal file
146
musicdb-lib/src/player/sleep.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
data::{song::Song, SongId},
|
||||
server::Command,
|
||||
};
|
||||
|
||||
use super::PlayerBackend;
|
||||
|
||||
pub struct PlayerBackendSleep<T> {
|
||||
current: Option<(SongId, u64, T)>,
|
||||
next: Option<(SongId, u64, T)>,
|
||||
/// unused, but could be used to do something smarter than polling at some point
|
||||
#[allow(unused)]
|
||||
command_sender: Option<std::sync::mpsc::Sender<(Command, Option<u64>)>>,
|
||||
finished: SongFinished,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum SongFinished {
|
||||
Never,
|
||||
In(Duration),
|
||||
At(Instant),
|
||||
}
|
||||
|
||||
impl<T> PlayerBackendSleep<T> {
|
||||
pub fn new(
|
||||
command_sender: Option<std::sync::mpsc::Sender<(Command, Option<u64>)>>,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Ok(Self {
|
||||
current: None,
|
||||
next: None,
|
||||
command_sender,
|
||||
finished: SongFinished::Never,
|
||||
})
|
||||
}
|
||||
fn set_finished(&mut self, play: bool) {
|
||||
self.finished = if let Some((_, duration, _)) = &self.current {
|
||||
let duration = Duration::from_millis(*duration);
|
||||
if play {
|
||||
SongFinished::At(Instant::now() + duration)
|
||||
} else {
|
||||
SongFinished::In(duration)
|
||||
}
|
||||
} else {
|
||||
SongFinished::Never
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PlayerBackend<T> for PlayerBackendSleep<T> {
|
||||
fn load_next_song(
|
||||
&mut self,
|
||||
id: SongId,
|
||||
song: &Song,
|
||||
_filename: &OsStr,
|
||||
_bytes: Arc<Vec<u8>>,
|
||||
_load_duration: bool,
|
||||
custom_data: T,
|
||||
) {
|
||||
self.next = Some((id, song.duration_millis, custom_data));
|
||||
}
|
||||
fn pause(&mut self) {
|
||||
match self.finished {
|
||||
SongFinished::Never | SongFinished::In(_) => {}
|
||||
SongFinished::At(time) => {
|
||||
self.finished = SongFinished::In(time.saturating_duration_since(Instant::now()));
|
||||
}
|
||||
}
|
||||
}
|
||||
fn stop(&mut self) {
|
||||
self.set_finished(false);
|
||||
}
|
||||
fn resume(&mut self) {
|
||||
match self.finished {
|
||||
SongFinished::Never | SongFinished::At(_) => {}
|
||||
SongFinished::In(dur) => {
|
||||
self.finished = SongFinished::At(Instant::now() + dur);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn next(&mut self, play: bool, _load_duration: bool) {
|
||||
self.current = self.next.take();
|
||||
self.set_finished(play);
|
||||
}
|
||||
fn clear(&mut self) {
|
||||
self.current = None;
|
||||
self.next = None;
|
||||
self.finished = SongFinished::Never;
|
||||
}
|
||||
fn playing(&self) -> bool {
|
||||
match self.finished {
|
||||
SongFinished::Never => false,
|
||||
SongFinished::In(_) => false,
|
||||
SongFinished::At(_) => true,
|
||||
}
|
||||
}
|
||||
fn current_song(&self) -> Option<(SongId, bool, &T)> {
|
||||
self.current
|
||||
.as_ref()
|
||||
.map(|(id, _, custom)| (*id, true, custom))
|
||||
}
|
||||
fn next_song(&self) -> Option<(SongId, bool, &T)> {
|
||||
self.next
|
||||
.as_ref()
|
||||
.map(|(id, _, custom)| (*id, true, custom))
|
||||
}
|
||||
fn gen_data_mut(&mut self) -> (Option<&mut T>, Option<&mut T>) {
|
||||
(
|
||||
self.current.as_mut().map(|(_, _, t)| t),
|
||||
self.next.as_mut().map(|(_, _, t)| t),
|
||||
)
|
||||
}
|
||||
fn song_finished_polling(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn song_finished(&self) -> bool {
|
||||
match self.finished {
|
||||
SongFinished::Never => true,
|
||||
SongFinished::In(dur) => dur <= Duration::ZERO,
|
||||
SongFinished::At(time) => time <= Instant::now(),
|
||||
}
|
||||
}
|
||||
fn current_song_duration(&self) -> Option<u64> {
|
||||
self.current
|
||||
.as_ref()
|
||||
.map(|(_, dur, _)| *dur)
|
||||
.filter(|dur| *dur > 0)
|
||||
}
|
||||
fn current_song_playback_position(&self) -> Option<u64> {
|
||||
if let Some(duration) = self.current_song_duration() {
|
||||
match self.finished {
|
||||
SongFinished::Never => None,
|
||||
SongFinished::In(dur) => Some(duration.saturating_sub(dur.as_millis() as u64)),
|
||||
SongFinished::At(time) => Some(duration.saturating_sub(
|
||||
time.saturating_duration_since(Instant::now()).as_millis() as u64,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,6 +292,8 @@ pub fn run_server_caching_thread_opt(
|
||||
use crate::player::playback_rs::PlayerBackendPlaybackRs;
|
||||
#[cfg(feature = "playback-via-rodio")]
|
||||
use crate::player::rodio::PlayerBackendRodio;
|
||||
#[cfg(feature = "playback-via-sleep")]
|
||||
use crate::player::sleep::PlayerBackendSleep;
|
||||
#[cfg(any(
|
||||
feature = "playback",
|
||||
feature = "playback-via-playback-rs",
|
||||
@@ -305,6 +307,8 @@ pub fn run_server_caching_thread_opt(
|
||||
|
||||
#[cfg(feature = "playback")]
|
||||
let mut player = if play_audio {
|
||||
#[cfg(feature = "playback-via-sleep")]
|
||||
let backend = PlayerBackendSleep::new(Some(command_sender.clone())).unwrap();
|
||||
#[cfg(feature = "playback-via-playback-rs")]
|
||||
let backend = PlayerBackendPlaybackRs::new(command_sender.clone()).unwrap();
|
||||
#[cfg(feature = "playback-via-rodio")]
|
||||
|
||||
Reference in New Issue
Block a user