use std::{
fmt::{Debug, Display},
sync::{atomic::Ordering, mpsc::Sender},
};
use blake3::Hash;
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 {
playables: Vec<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 {
items: 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<Option<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.contains(PlayerQueryFlags::CURRENTLY_PLAYING) {
let currently_playing = self.decoder_tx.get_playing()?;
query.currently_playing = 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(|t| t.id()).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::TRACKLIST) {
query.tracklist =
Some(self.playlist.tracklist().iter().map(|t| t.id()).collect());
}
if flags.contains(PlayerQueryFlags::TRACKLIST_POSITION) {
query.tracklist_position = Some(self.playlist.position().into());
}
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 {
playables: playable,
} => {
self.playlist.set_playlist(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.set_queue(&to);
}
if let Some(callback) = callback {
let _ = callback.send(equals);
}
}
PlayerCommand::QueueExtend { with } => self.playlist.extend_queue(&with),
PlayerCommand::QueueShuffle => self.playlist.shuffle_queue(),
PlayerCommand::QueueClear => self.playlist.clear_queue(),
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 { items } => {
self.playlist.extend_playlist(items);
}
PlayerCommand::PlaylistClear => self.playlist.clear(),
PlayerCommand::PlaylistSetShuffleMode { shuffle_mode } => {
self.playlist.set_shuffle_mode(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 => {
todo!()
}
}
Ok(())
}
}