use std::{
fmt::{Debug, Display},
sync::{atomic::Ordering, mpsc::Sender},
};
use blake3::Hash;
use rand::{rng, seq::SliceRandom};
use selene_core::library::track::Track;
use crate::{
PlayerEvent,
decoder::{DecoderEvent, DecoderTx},
event_handler::EventTx,
player::{Player, PlayerError, PlayerQueryFlags, QueryResult},
playlist::{LoopMode, Playable, PlaybackStatus, ShuffleMode},
};
#[derive(Debug, Clone)]
pub enum PlayerCommand {
Flush {
callback: Sender<()>,
},
Play {
playable: Playable,
},
Next,
Previous,
SetIsPlaying {
is_playing: bool,
},
TogglePlaying {
callback: Option<Sender<PlaybackStatus>>,
},
Stop,
SetVolume {
volume: f32,
increment: bool,
callback: Option<Sender<f32>>,
},
Seek {
seconds: f64,
increment: bool,
callback: Option<Sender<Option<f64>>>,
},
QueueExtend {
with: Vec<Playable>,
},
QueueSet {
to: Vec<Playable>,
expected_state: Hash,
callback: Option<Sender<bool>>,
},
QueueShuffle,
QueueClear,
PlaylistExtend {
with: Vec<Playable>,
},
PlaylistSet {
playables: Vec<Playable>,
expected_state: Hash,
callback: Option<Sender<bool>>,
},
PlaylistClear,
PlaylistSetShuffleMode {
shuffle_mode: ShuffleMode,
},
PlaylistSetLoopMode {
loop_mode: LoopMode,
},
TracklistSeek {
index: isize,
increment: bool,
callback: Option<Sender<usize>>,
},
TracklistRebuild,
GetState {
flags: PlayerQueryFlags,
callback: Sender<QueryResult>,
},
}
impl Display for PlayerCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PlayerCommand::Flush { .. } => f.write_str("Flush"),
PlayerCommand::Play { .. } => f.write_str("Play"),
PlayerCommand::Next => f.write_str("Next"),
PlayerCommand::Previous => f.write_str("Previous"),
PlayerCommand::SetIsPlaying { .. } => f.write_str("SetIsPlaying"),
PlayerCommand::TogglePlaying { .. } => f.write_str("TogglePlaying"),
PlayerCommand::Stop => f.write_str("Stop"),
PlayerCommand::SetVolume { .. } => f.write_str("SetVolume"),
PlayerCommand::Seek { .. } => f.write_str("Seek"),
PlayerCommand::QueueExtend { .. } => f.write_str("QueueExtend"),
PlayerCommand::QueueSet { .. } => f.write_str("QueueSet"),
PlayerCommand::QueueShuffle => f.write_str("QueueShuffle"),
PlayerCommand::QueueClear => f.write_str("QueueClear"),
PlayerCommand::PlaylistExtend { .. } => f.write_str("PlaylistExtend"),
PlayerCommand::PlaylistSet { .. } => f.write_str("PlaylistSet"),
PlayerCommand::PlaylistClear => f.write_str("PlaylistClear"),
PlayerCommand::PlaylistSetShuffleMode { .. } => f.write_str("PlaylistSetShuffleMode"),
PlayerCommand::PlaylistSetLoopMode { .. } => f.write_str("PlaylistSetLoopMode"),
PlayerCommand::TracklistSeek { .. } => f.write_str("TracklistSeek"),
PlayerCommand::TracklistRebuild => f.write_str("TracklistRebuild"),
PlayerCommand::GetState { .. } => f.write_str("GetState"),
}
}
}
pub enum PlayerRequest {
DecoderEvent(DecoderEvent),
Command(Box<PlayerCommand>),
}
pub trait PlayerTx {
fn decoder_event(&self, event: DecoderEvent) -> Result<(), PlayerError>;
fn command(&self, command: PlayerCommand) -> Result<(), PlayerError>;
}
impl PlayerTx for Sender<PlayerRequest> {
fn decoder_event(&self, event: DecoderEvent) -> Result<(), PlayerError> {
self.send(PlayerRequest::DecoderEvent(event))?;
Ok(())
}
fn command(&self, command: PlayerCommand) -> Result<(), PlayerError> {
self.send(PlayerRequest::Command(Box::new(command)))?;
Ok(())
}
}
impl Player {
pub(crate) fn run_command(&mut self, command: PlayerCommand) -> Result<(), PlayerError> {
match command {
PlayerCommand::Flush { callback } => {
let _ = callback.send(());
}
PlayerCommand::GetState { flags, callback } => {
let mut query = QueryResult::default();
if flags.contains(PlayerQueryFlags::PLAYBACK_STATE) {
query.playback_state = Some(self.playback_state.load(Ordering::SeqCst));
}
if flags.intersects(
PlayerQueryFlags::CURRENTLY_PLAYING | PlayerQueryFlags::CURRENTLY_PLAYING_RAW,
) {
let currently_playing = self.decoder_tx.get_playing()?.map(|p| (*p).clone());
if flags.contains(PlayerQueryFlags::CURRENTLY_PLAYING) {
query.currently_playing =
Some(currently_playing.as_ref().map(|t| t.track.id()).into());
}
if flags.contains(PlayerQueryFlags::CURRENTLY_PLAYING_RAW) {
query.currently_playing_raw = Some(currently_playing.into());
}
}
if flags.contains(PlayerQueryFlags::VOLUME) {
query.volume = Some(f32::from_bits(self.volume.load(Ordering::SeqCst)));
}
if flags.contains(PlayerQueryFlags::TIME) {
query.time = Some(self.decoder_tx.get_time()?.into());
}
if flags.contains(PlayerQueryFlags::QUEUE) {
query.queue = Some(self.playlist.queue.iter().map(Track::id).collect());
}
if flags.contains(PlayerQueryFlags::QUEUE_RAW) {
query.queue_raw = Some(self.playlist.queue.iter().cloned().collect());
}
if flags.contains(PlayerQueryFlags::QUEUE_STATE) {
query.queue_state = Some(self.playlist.queue_hash());
}
if flags.contains(PlayerQueryFlags::PLAYLIST) {
query.playlist = Some(
self.playlist
.playlist()
.iter()
.map(Playable::to_collectable)
.collect(),
);
}
if flags.contains(PlayerQueryFlags::PLAYLIST_RAW) {
query.playlist_raw = Some(self.playlist.playlist().to_vec());
}
if flags.contains(PlayerQueryFlags::TRACKLIST) {
query.tracklist =
Some(self.playlist.tracklist().iter().map(Track::id).collect());
}
if flags.contains(PlayerQueryFlags::TRACKLIST_RAW) {
query.tracklist_raw = Some(self.playlist.tracklist().to_vec());
}
if flags.contains(PlayerQueryFlags::TRACKLIST_POSITION) {
query.tracklist_position = Some(self.playlist.position());
}
if flags.contains(PlayerQueryFlags::PLAYLIST_STATE) {
query.playlist_state = Some(self.playlist.playlist_hash());
}
if flags.contains(PlayerQueryFlags::SHUFFLE_MODE) {
query.shuffle_mode = Some(self.playlist.shuffle_mode);
}
if flags.contains(PlayerQueryFlags::LOOP_MODE) {
query.loop_mode = Some(self.playlist.loop_mode);
}
let _ = callback.send(query);
}
PlayerCommand::Play { playable } => {
self.playlist.set_playlist(std::iter::once(playable));
let loaded = self.replace_decoders(true)?;
if loaded {
self.decoder_tx.set_playing(true)?;
}
}
PlayerCommand::SetIsPlaying { is_playing } => {
self.decoder_tx.set_playing(is_playing)?;
}
PlayerCommand::TogglePlaying { callback } => {
let state = self.decoder_tx.toggle_playing()?;
if let Some(callback) = callback {
let _ = callback.send(state);
}
}
PlayerCommand::Stop => {
self.decoder_tx.stop()?;
}
PlayerCommand::SetVolume {
volume,
increment,
callback,
} => {
let vol = self.set_volume(volume, increment);
if let Some(callback) = callback {
let _ = callback.send(vol);
}
}
PlayerCommand::Seek {
seconds,
increment,
callback,
} => {
let time = self.decoder_tx.seek(seconds, increment)?;
if let Some(callback) = callback {
let _ = callback.send(time);
}
}
PlayerCommand::QueueSet {
expected_state,
to,
callback,
} => {
let equals = expected_state == self.playlist.queue_hash();
if equals {
self.playlist.queue = to
.into_iter()
.flat_map(super::super::playlist::Playable::flatten)
.collect();
}
if let Some(callback) = callback {
let _ = callback.send(equals);
}
}
PlayerCommand::QueueExtend { with } => self.playlist.queue_extend(with),
PlayerCommand::QueueShuffle => {
let mut shuffled: Vec<_> = self.playlist.queue.drain(..).collect();
shuffled.shuffle(&mut rng());
self.playlist.queue = shuffled.into();
}
PlayerCommand::QueueClear => self.playlist.queue.clear(),
PlayerCommand::PlaylistSet {
expected_state,
playables,
callback,
} => {
let equals = expected_state == self.playlist.playlist_hash();
if equals {
self.playlist.set_playlist(playables);
}
if let Some(callback) = callback {
let _ = callback.send(equals);
}
}
PlayerCommand::PlaylistExtend { with } => self.playlist.extend(with),
PlayerCommand::PlaylistClear => self.playlist.clear(),
PlayerCommand::PlaylistSetShuffleMode { shuffle_mode } => {
self.playlist.shuffle_mode = shuffle_mode;
self.playlist.rebuild_tracklist();
self.event_tx
.event(PlayerEvent::ShuffleModeChanged { shuffle_mode });
}
PlayerCommand::PlaylistSetLoopMode { loop_mode } => {
self.playlist.loop_mode = loop_mode;
self.event_tx
.event(PlayerEvent::LoopModeChanged { loop_mode });
}
PlayerCommand::TracklistSeek {
index,
increment,
callback,
} => {
let position = self.playlist.tracklist_seek(index, increment);
self.replace_decoders(false)?;
if let Some(callback) = callback {
let _ = callback.send(position);
}
}
PlayerCommand::Next => {
self.decoder_tx.skip()?;
}
PlayerCommand::Previous => {
self.playlist.tracklist_seek(-1, true);
self.replace_decoders(false)?;
}
PlayerCommand::TracklistRebuild => self.playlist.rebuild_tracklist(),
}
Ok(())
}
}