use std::{collections::VecDeque, fmt::Display, io};
use blake3::Hash;
#[cfg(feature = "mpris")]
use mpris_server::TrackId as ConnectorId;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::playlist::{LoopMode, Playable, PlayableTrack, ShuffleMode, TracklistTrack};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum IpcCommand {
Flush,
SetPlaying(bool),
TogglePlaying,
Seek {
seconds: f64,
increment: bool,
},
GetTime,
GetVolume,
SetVolume {
volume: f32,
increment: bool,
},
QueueGet,
QueueSet {
tracks: VecDeque<PlayableTrack>,
expected_state: Hash,
},
QueueExtend(Vec<Playable>),
QueueShuffle,
QueueClear,
PlaylistGet,
PlaylistSet {
playables: Vec<Playable>,
expected_state: Hash,
},
PlaylistExtend(Vec<Playable>),
PlaylistClear,
PlaylistSetShuffleMode {
shuffle_mode: ShuffleMode,
},
PlaylistGetShuffleMode,
TracklistRebuild,
PlaylistSetLoopMode {
loop_mode: LoopMode,
},
PlaylistGetLoopMode,
TracklistSeek {
index: isize,
increment: bool,
},
GetState,
GetPlaying,
Disconnect,
TracklistGet,
QueueGetState,
PlaylistGetState,
Stop,
Play {
playable: Playable,
},
}
#[repr(u8)]
pub enum PacketType {
Unknown,
PlayerResponse,
PlayerEvent,
}
impl From<u8> for PacketType {
fn from(value: u8) -> Self {
match value {
1 => Self::PlayerResponse,
2 => Self::PlayerEvent,
_ => Self::Unknown,
}
}
}
#[derive(Serialize, Deserialize, Clone)]
pub enum PlayerEvent {
CurrentlyPlayingChanged {
currently_playing: Option<TracklistTrack>,
},
IsPlayingChanged {
is_playing: bool,
},
ShuffleModeChanged {
shuffle_mode: ShuffleMode,
},
LoopModeChanged {
loop_mode: LoopMode,
},
VolumeChanged {
volume: f32,
},
SeekOccured {
time: f64,
},
QueueChanged,
PlaylistChanged,
TracklistChanged,
Shutdown,
}
impl Display for PlayerEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PlayerEvent::CurrentlyPlayingChanged { .. } => f.write_str("CurrentlyPlayingChanged"),
PlayerEvent::IsPlayingChanged { .. } => f.write_str("IsPlayingChanged"),
PlayerEvent::ShuffleModeChanged { .. } => f.write_str("ShuffleModeChanged"),
PlayerEvent::LoopModeChanged { .. } => f.write_str("LoopModeChanged"),
PlayerEvent::VolumeChanged { .. } => f.write_str("VolumeChanged"),
PlayerEvent::SeekOccured { .. } => f.write_str("SeekOccured"),
PlayerEvent::QueueChanged => f.write_str("QueueChanged"),
PlayerEvent::PlaylistChanged => f.write_str("PlaylistChanged"),
PlayerEvent::TracklistChanged => f.write_str("TracklistChanged"),
PlayerEvent::Shutdown => f.write_str("Shutdown"),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub enum PlayerResponse {
Done,
IsPlaying(bool),
Volume(f32),
Time(Option<f64>),
TracklistPosition {
index: usize,
max: usize,
},
ShuffleMode(crate::playlist::ShuffleMode),
Playlist(Vec<Playable>),
PlaylistState(Hash),
Tracklist(Vec<TracklistTrack>),
Queue(Vec<PlayableTrack>),
QueueState(Hash),
LoopMode(LoopMode),
Playing(Option<Box<TracklistTrack>>),
#[cfg(feature = "mpris")]
PlayingConnectorId(Option<ConnectorId>),
}
impl Display for PlayerResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PlayerResponse::Done => f.write_str("Done"),
PlayerResponse::IsPlaying(_) => f.write_str("IsPlaying"),
PlayerResponse::Volume(_) => f.write_str("Volume"),
PlayerResponse::Time(_) => f.write_str("Time"),
PlayerResponse::TracklistPosition { .. } => f.write_str("TracklistPosition"),
PlayerResponse::ShuffleMode(_) => f.write_str("ShuffleMode"),
PlayerResponse::Playlist(_) => f.write_str("Playlist"),
PlayerResponse::PlaylistState(_) => f.write_str("PlaylistState"),
PlayerResponse::Tracklist(_) => f.write_str("Tracklist"),
PlayerResponse::Queue(_) => f.write_str("Queue"),
PlayerResponse::QueueState(_) => f.write_str("QueueState"),
PlayerResponse::LoopMode(_) => f.write_str("LoopMode"),
PlayerResponse::Playing(_) => f.write_str("Playing"),
#[cfg(feature = "mpris")]
PlayerResponse::PlayingConnectorId(_) => f.write_str("PlayingConnectorId"),
}
}
}
#[derive(Debug)]
pub enum ConnectErrorKind {
DaemonNotRunning,
ConnectionRefused,
Other(io::Error),
}
impl Display for ConnectErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConnectErrorKind::DaemonNotRunning => f.write_str("The daemon is not running"),
ConnectErrorKind::ConnectionRefused => {
f.write_str("The daemon listener thread halted and must be restarted")
}
ConnectErrorKind::Other(error) => error.fmt(f),
}
}
}
#[derive(Debug, Error)]
pub enum IpcHandleError {
#[error("Failed to connect: {0}")]
FailedToConnect(ConnectErrorKind),
#[error("The handling thread cannot be communicated with")]
HandleDied,
#[error("The current platform is not supported")]
UnsupportedPlatform,
}
#[derive(Debug, Serialize, Deserialize, Error)]
pub enum IpcActionError {
#[error("Nothing playing")]
NothingPlaying,
#[error("Index {0} was out of bounds")]
IndexOutOfBounds(usize),
#[error("State mismatch")]
StateMismatch,
#[error("The client is disconnected from the daemon")]
Disconnected,
#[error("Packet too large: Recieved {packet} bytes of {max} max bytes")]
PacketTooLarge { packet: usize, max: usize },
}