1use std::{
2 fmt::{Debug, Display},
3 sync::{atomic::Ordering, mpsc::Sender},
4};
5
6use blake3::Hash;
7use rand::{rng, seq::SliceRandom};
8use selene_core::library::track::Track;
9
10use crate::{
11 PlayerEvent,
12 decoder::{DecoderEvent, DecoderTx},
13 event_handler::EventTx,
14 player::{Player, PlayerError, PlayerQueryFlags, QueryResult},
15 playlist::{LoopMode, Playable, PlaybackStatus, ShuffleMode},
16};
17
18#[derive(Debug, Clone)]
19pub enum PlayerCommand {
20 Flush {
22 callback: Sender<()>,
23 },
24
25 Play {
27 playable: Playable,
28 },
29 Next,
30 Previous,
31 SetIsPlaying {
32 is_playing: bool,
33 },
34 TogglePlaying {
35 callback: Option<Sender<PlaybackStatus>>,
36 },
37 Stop,
38 SetVolume {
39 volume: f32,
40 increment: bool,
41 callback: Option<Sender<f32>>,
42 },
43 Seek {
44 seconds: f64,
45 increment: bool,
46 callback: Option<Sender<Option<f64>>>,
47 },
48
49 QueueExtend {
51 with: Vec<Playable>,
52 },
53 QueueSet {
54 to: Vec<Playable>,
55 expected_state: Hash,
56 callback: Option<Sender<bool>>,
57 },
58 QueueShuffle,
59 QueueClear,
60
61 PlaylistExtend {
63 with: Vec<Playable>,
64 },
65 PlaylistSet {
66 playables: Vec<Playable>,
67 expected_state: Hash,
68 callback: Option<Sender<bool>>,
69 },
70 PlaylistClear,
71 PlaylistSetShuffleMode {
72 shuffle_mode: ShuffleMode,
73 },
74 PlaylistSetLoopMode {
75 loop_mode: LoopMode,
76 },
77
78 TracklistSeek {
80 index: isize,
81 increment: bool,
82 callback: Option<Sender<usize>>,
83 },
84 TracklistRebuild,
85
86 GetState {
88 flags: PlayerQueryFlags,
89 callback: Sender<QueryResult>,
90 },
91}
92
93impl Display for PlayerCommand {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 PlayerCommand::Flush { .. } => f.write_str("Flush"),
97 PlayerCommand::Play { .. } => f.write_str("Play"),
98 PlayerCommand::Next => f.write_str("Next"),
99 PlayerCommand::Previous => f.write_str("Previous"),
100 PlayerCommand::SetIsPlaying { .. } => f.write_str("SetIsPlaying"),
101 PlayerCommand::TogglePlaying { .. } => f.write_str("TogglePlaying"),
102 PlayerCommand::Stop => f.write_str("Stop"),
103 PlayerCommand::SetVolume { .. } => f.write_str("SetVolume"),
104 PlayerCommand::Seek { .. } => f.write_str("Seek"),
105 PlayerCommand::QueueExtend { .. } => f.write_str("QueueExtend"),
106 PlayerCommand::QueueSet { .. } => f.write_str("QueueSet"),
107 PlayerCommand::QueueShuffle => f.write_str("QueueShuffle"),
108 PlayerCommand::QueueClear => f.write_str("QueueClear"),
109 PlayerCommand::PlaylistExtend { .. } => f.write_str("PlaylistExtend"),
110 PlayerCommand::PlaylistSet { .. } => f.write_str("PlaylistSet"),
111 PlayerCommand::PlaylistClear => f.write_str("PlaylistClear"),
112 PlayerCommand::PlaylistSetShuffleMode { .. } => f.write_str("PlaylistSetShuffleMode"),
113 PlayerCommand::PlaylistSetLoopMode { .. } => f.write_str("PlaylistSetLoopMode"),
114 PlayerCommand::TracklistSeek { .. } => f.write_str("TracklistSeek"),
115 PlayerCommand::TracklistRebuild => f.write_str("TracklistRebuild"),
116 PlayerCommand::GetState { .. } => f.write_str("GetState"),
117 }
118 }
119}
120
121pub enum PlayerRequest {
122 DecoderEvent(DecoderEvent),
123 Command(Box<PlayerCommand>),
124}
125
126pub trait PlayerTx {
127 fn decoder_event(&self, event: DecoderEvent) -> Result<(), PlayerError>;
128 fn command(&self, command: PlayerCommand) -> Result<(), PlayerError>;
129}
130
131impl PlayerTx for Sender<PlayerRequest> {
132 fn decoder_event(&self, event: DecoderEvent) -> Result<(), PlayerError> {
133 self.send(PlayerRequest::DecoderEvent(event))?;
134 Ok(())
135 }
136
137 fn command(&self, command: PlayerCommand) -> Result<(), PlayerError> {
138 self.send(PlayerRequest::Command(Box::new(command)))?;
139 Ok(())
140 }
141}
142
143impl Player {
144 pub(crate) fn run_command(&mut self, command: PlayerCommand) -> Result<(), PlayerError> {
145 match command {
146 PlayerCommand::Flush { callback } => {
148 let _ = callback.send(());
149 }
150
151 PlayerCommand::GetState { flags, callback } => {
153 let mut query = QueryResult::default();
154
155 if flags.contains(PlayerQueryFlags::PLAYBACK_STATE) {
156 query.playback_state = Some(self.playback_state.load(Ordering::SeqCst));
157 }
158
159 if flags.intersects(
160 PlayerQueryFlags::CURRENTLY_PLAYING | PlayerQueryFlags::CURRENTLY_PLAYING_RAW,
161 ) {
162 let currently_playing = self.decoder_tx.get_playing()?;
163
164 if flags.contains(PlayerQueryFlags::CURRENTLY_PLAYING) {
165 query.currently_playing =
166 Some(currently_playing.as_ref().map(|t| t.track.id()).into());
167 }
168
169 if flags.contains(PlayerQueryFlags::CURRENTLY_PLAYING_RAW) {
170 query.currently_playing_raw = Some(currently_playing.into());
171 }
172 }
173
174 if flags.contains(PlayerQueryFlags::VOLUME) {
175 query.volume = Some(f32::from_bits(self.volume.load(Ordering::SeqCst)));
176 }
177
178 if flags.contains(PlayerQueryFlags::TIME) {
179 query.time = Some(self.decoder_tx.get_time()?.into());
180 }
181
182 if flags.contains(PlayerQueryFlags::QUEUE) {
183 query.queue = Some(self.playlist.queue.iter().map(Track::id).collect());
184 }
185
186 if flags.contains(PlayerQueryFlags::QUEUE_RAW) {
187 query.queue_raw = Some(self.playlist.queue.iter().cloned().collect());
188 }
189
190 if flags.contains(PlayerQueryFlags::QUEUE_STATE) {
191 query.queue_state = Some(self.playlist.queue_hash());
192 }
193
194 if flags.contains(PlayerQueryFlags::PLAYLIST) {
195 query.playlist = Some(
196 self.playlist
197 .playlist()
198 .iter()
199 .map(Playable::to_collectable)
200 .collect(),
201 );
202 }
203
204 if flags.contains(PlayerQueryFlags::PLAYLIST_RAW) {
205 query.playlist_raw = Some(self.playlist.playlist().to_vec());
206 }
207
208 if flags.contains(PlayerQueryFlags::TRACKLIST) {
209 query.tracklist =
210 Some(self.playlist.tracklist().iter().map(Track::id).collect());
211 }
212
213 if flags.contains(PlayerQueryFlags::TRACKLIST_RAW) {
214 query.tracklist_raw = Some(self.playlist.tracklist().to_vec());
215 }
216
217 if flags.contains(PlayerQueryFlags::TRACKLIST_POSITION) {
218 query.tracklist_position = Some(self.playlist.position());
219 }
220
221 if flags.contains(PlayerQueryFlags::PLAYLIST_STATE) {
222 query.playlist_state = Some(self.playlist.playlist_hash());
223 }
224
225 if flags.contains(PlayerQueryFlags::SHUFFLE_MODE) {
226 query.shuffle_mode = Some(self.playlist.shuffle_mode);
227 }
228
229 if flags.contains(PlayerQueryFlags::LOOP_MODE) {
230 query.loop_mode = Some(self.playlist.loop_mode);
231 }
232
233 let _ = callback.send(query);
234 }
235
236 PlayerCommand::Play { playable } => {
238 self.playlist.set_playlist(std::iter::once(playable));
239
240 let loaded = self.replace_decoders(true)?;
241
242 if loaded {
243 self.decoder_tx.set_playing(true)?;
244 }
245 }
246 PlayerCommand::SetIsPlaying { is_playing } => {
247 self.decoder_tx.set_playing(is_playing)?;
248 }
249 PlayerCommand::TogglePlaying { callback } => {
250 let state = self.decoder_tx.toggle_playing()?;
251 if let Some(callback) = callback {
252 let _ = callback.send(state);
253 }
254 }
255 PlayerCommand::Stop => {
256 self.decoder_tx.stop()?;
257 }
258 PlayerCommand::SetVolume {
259 volume,
260 increment,
261 callback,
262 } => {
263 let vol = self.set_volume(volume, increment);
264 if let Some(callback) = callback {
265 let _ = callback.send(vol);
266 }
267 }
268 PlayerCommand::Seek {
269 seconds,
270 increment,
271 callback,
272 } => {
273 let time = self.decoder_tx.seek(seconds, increment)?;
274 if let Some(callback) = callback {
275 let _ = callback.send(time);
276 }
277 }
278
279 PlayerCommand::QueueSet {
281 expected_state,
282 to,
283 callback,
284 } => {
285 let equals = expected_state == self.playlist.queue_hash();
286 if equals {
287 self.playlist.queue = to
288 .into_iter()
289 .flat_map(super::super::playlist::Playable::flatten)
290 .collect();
291 }
292 if let Some(callback) = callback {
293 let _ = callback.send(equals);
294 }
295 }
296 PlayerCommand::QueueExtend { with } => self.playlist.queue_extend(with),
297 PlayerCommand::QueueShuffle => {
298 let mut shuffled: Vec<_> = self.playlist.queue.drain(..).collect();
299 shuffled.shuffle(&mut rng());
300 self.playlist.queue = shuffled.into();
301 }
302 PlayerCommand::QueueClear => self.playlist.queue.clear(),
303
304 PlayerCommand::PlaylistSet {
306 expected_state,
307 playables,
308 callback,
309 } => {
310 let equals = expected_state == self.playlist.playlist_hash();
311 if equals {
312 self.playlist.set_playlist(playables);
313 }
314
315 if let Some(callback) = callback {
316 let _ = callback.send(equals);
317 }
318 }
319 PlayerCommand::PlaylistExtend { with } => self.playlist.extend(with),
320 PlayerCommand::PlaylistClear => self.playlist.clear(),
321 PlayerCommand::PlaylistSetShuffleMode { shuffle_mode } => {
322 self.playlist.shuffle_mode = shuffle_mode;
323 self.playlist.rebuild_tracklist();
324
325 self.event_tx
326 .event(PlayerEvent::ShuffleModeChanged { shuffle_mode });
327 }
328 PlayerCommand::PlaylistSetLoopMode { loop_mode } => {
329 self.playlist.loop_mode = loop_mode;
330 self.event_tx
331 .event(PlayerEvent::LoopModeChanged { loop_mode });
332 }
333
334 PlayerCommand::TracklistSeek {
336 index,
337 increment,
338 callback,
339 } => {
340 let position = self.playlist.tracklist_seek(index, increment);
341 self.replace_decoders(false)?;
342 if let Some(callback) = callback {
343 let _ = callback.send(position);
344 }
345 }
346 PlayerCommand::Next => {
347 self.decoder_tx.skip()?;
348 }
349 PlayerCommand::Previous => {
350 self.playlist.tracklist_seek(-1, true);
351 self.replace_decoders(false)?;
352 }
353 PlayerCommand::TracklistRebuild => self.playlist.rebuild_tracklist(),
354 }
355 Ok(())
356 }
357}