1use std::{
2 fmt::{Debug, Display},
3 sync::{atomic::Ordering, mpsc::Sender},
4};
5
6use blake3::Hash;
7use selene_core::{
8 database::LibraryDb,
9 library::{collectable::Collectable, track::TrackId},
10};
11use serde::{Deserialize, Serialize};
12
13use crate::{
14 LoopMode, ShuffleMode,
15 config::daemon_config,
16 decoder::{DecoderEvent, DecoderTx},
17 player::{PlaybackStatus, Player, PlayerError, PlayerQueryFlags, QueryResult},
18 playlist::{Playable, PlayingTrack},
19};
20
21#[derive(Debug, Clone)]
22pub(crate) enum PlayerCommand {
23 Flush {
25 callback: Sender<()>,
26 },
27
28 Play {
30 playables: Vec<Playable>,
31 },
32 Next,
33 Previous,
34 SetIsPlaying {
35 is_playing: bool,
36 },
37 TogglePlaying {
38 callback: Option<Sender<PlaybackStatus>>,
39 },
40 Stop,
41 SetVolume {
42 volume: f32,
43 increment: bool,
44 callback: Option<Sender<f32>>,
45 },
46 Seek {
47 seconds: f64,
48 increment: bool,
49 callback: Option<Sender<Option<f64>>>,
50 },
51
52 QueueExtend {
54 with: Vec<Playable>,
55 },
56 QueueSet {
57 to: Vec<Playable>,
58 expected_state: Hash,
59 callback: Option<Sender<bool>>,
60 },
61 QueueShuffle,
62 QueueClear,
63
64 PlaylistExtend {
66 items: Vec<Playable>,
67 },
68 PlaylistSet {
69 playables: Vec<Playable>,
70 expected_state: Hash,
71 callback: Option<Sender<bool>>,
72 },
73 PlaylistClear,
74 PlaylistSetShuffleMode {
75 shuffle_mode: ShuffleMode,
76 },
77 PlaylistSetLoopMode {
78 loop_mode: LoopMode,
79 },
80
81 TracklistSeek {
83 index: isize,
84 increment: bool,
85 callback: Option<Sender<Option<usize>>>,
86 },
87
88 GetState {
90 flags: PlayerQueryFlags,
91 callback: Sender<QueryResult>,
92 },
93}
94
95impl Display for PlayerCommand {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 PlayerCommand::Flush { .. } => f.write_str("Flush"),
99 PlayerCommand::Play { .. } => f.write_str("Play"),
100 PlayerCommand::Next => f.write_str("Next"),
101 PlayerCommand::Previous => f.write_str("Previous"),
102 PlayerCommand::SetIsPlaying { .. } => f.write_str("SetIsPlaying"),
103 PlayerCommand::TogglePlaying { .. } => f.write_str("TogglePlaying"),
104 PlayerCommand::Stop => f.write_str("Stop"),
105 PlayerCommand::SetVolume { .. } => f.write_str("SetVolume"),
106 PlayerCommand::Seek { .. } => f.write_str("Seek"),
107 PlayerCommand::QueueExtend { .. } => f.write_str("QueueExtend"),
108 PlayerCommand::QueueSet { .. } => f.write_str("QueueSet"),
109 PlayerCommand::QueueShuffle => f.write_str("QueueShuffle"),
110 PlayerCommand::QueueClear => f.write_str("QueueClear"),
111 PlayerCommand::PlaylistExtend { .. } => f.write_str("PlaylistExtend"),
112 PlayerCommand::PlaylistSet { .. } => f.write_str("PlaylistSet"),
113 PlayerCommand::PlaylistClear => f.write_str("PlaylistClear"),
114 PlayerCommand::PlaylistSetShuffleMode { .. } => f.write_str("PlaylistSetShuffleMode"),
115 PlayerCommand::PlaylistSetLoopMode { .. } => f.write_str("PlaylistSetLoopMode"),
116 PlayerCommand::TracklistSeek { .. } => f.write_str("TracklistSeek"),
117 PlayerCommand::GetState { .. } => f.write_str("GetState"),
118 }
119 }
120}
121
122#[derive(Debug, Serialize, Deserialize, Clone)]
123#[non_exhaustive]
124pub enum PlayerEvent {
125 CurrentlyPlayingChanged {
126 currently_playing: PlayingTrack,
127 },
128 PlaybackIsPlayingChanged {
129 is_playing: bool,
130 changed_at: f64,
131 },
132 PlaybackStopped,
133
134 ShuffleModeChanged {
135 shuffle_mode: ShuffleMode,
136 },
137 LoopModeChanged {
138 loop_mode: LoopMode,
139 },
140
141 VolumeChanged {
142 volume: f32,
143 },
144 SeekOccured {
145 time: f64,
146 },
147
148 QueueChanged {
149 queue: Vec<TrackId>,
150 },
151 PlaylistChanged {
152 playlist: Vec<Collectable>,
153 },
154 TracklistChanged {
155 tracklist: Vec<TrackId>,
156 position: Option<usize>,
157 },
158
159 Shutdown,
160}
161
162impl Display for PlayerEvent {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 match self {
165 PlayerEvent::CurrentlyPlayingChanged { .. } => f.write_str("CurrentlyPlayingChanged"),
166 PlayerEvent::PlaybackIsPlayingChanged { .. } => f.write_str("PlaybackIsPlayingChanged"),
167 PlayerEvent::PlaybackStopped => f.write_str("PlaybackStopped"),
168 PlayerEvent::ShuffleModeChanged { .. } => f.write_str("ShuffleModeChanged"),
169 PlayerEvent::LoopModeChanged { .. } => f.write_str("LoopModeChanged"),
170 PlayerEvent::VolumeChanged { .. } => f.write_str("VolumeChanged"),
171 PlayerEvent::SeekOccured { .. } => f.write_str("SeekOccured"),
172 PlayerEvent::QueueChanged { .. } => f.write_str("QueueChanged"),
173 PlayerEvent::PlaylistChanged { .. } => f.write_str("PlaylistChanged"),
174 PlayerEvent::TracklistChanged { .. } => f.write_str("TracklistChanged"),
175 PlayerEvent::Shutdown => f.write_str("Shutdown"),
176 }
177 }
178}
179
180pub(crate) enum PlayerRequest {
181 DecoderEvent(DecoderEvent),
182 Command(Box<PlayerCommand>),
183}
184
185pub(crate) trait PlayerTx {
186 fn decoder_event(&self, event: DecoderEvent) -> Result<(), PlayerError>;
187 fn command(&self, command: PlayerCommand) -> Result<(), PlayerError>;
188}
189
190impl PlayerTx for Sender<PlayerRequest> {
191 fn decoder_event(&self, event: DecoderEvent) -> Result<(), PlayerError> {
192 self.send(PlayerRequest::DecoderEvent(event))?;
193 Ok(())
194 }
195
196 fn command(&self, command: PlayerCommand) -> Result<(), PlayerError> {
197 self.send(PlayerRequest::Command(Box::new(command)))?;
198 Ok(())
199 }
200}
201
202impl Player {
203 pub(crate) fn run_command(
204 &mut self,
205 command: PlayerCommand,
206 db: &LibraryDb,
207 ) -> Result<(), PlayerError> {
208 match command {
209 PlayerCommand::Flush { callback } => {
211 let _ = callback.send(());
212 }
213
214 PlayerCommand::GetState { flags, callback } => {
216 let mut query = QueryResult::default();
217
218 if flags.contains(PlayerQueryFlags::PLAYBACK_STATE) {
219 query.playback_state = Some(self.playback_state.load(Ordering::SeqCst));
220 }
221
222 if flags.contains(PlayerQueryFlags::CURRENTLY_PLAYING) {
223 let currently_playing = self.decoder_tx.get_playing()?;
224
225 if let Some((track, pos)) = currently_playing.map(|c| (c.source, c.position)) {
226 query.currently_playing = Some(Some(track.id()));
227 query.currently_playing_index = Some(pos);
228 } else {
229 query.currently_playing = Some(None);
230 query.currently_playing_index = Some(None);
231 }
232 }
233
234 if flags.contains(PlayerQueryFlags::VOLUME) {
235 query.volume = Some(f32::from_bits(self.volume.load(Ordering::SeqCst)));
236 }
237
238 if flags.contains(PlayerQueryFlags::TIME) {
239 query.time = Some(self.decoder_tx.get_time()?);
240 }
241
242 if flags.contains(PlayerQueryFlags::QUEUE) {
243 query.queue = Some(self.playlist.queue().iter().map(|t| t.id()).collect());
244 }
245
246 if flags.contains(PlayerQueryFlags::QUEUE_STATE) {
247 query.queue_state = Some(self.playlist.queue_hash());
248 }
249
250 if flags.contains(PlayerQueryFlags::PLAYLIST) {
251 query.playlist = Some(
252 self.playlist
253 .playlist()
254 .iter()
255 .map(Playable::to_collectable)
256 .collect(),
257 );
258 }
259
260 if flags.contains(PlayerQueryFlags::TRACKLIST) {
261 query.tracklist =
262 Some(self.playlist.tracklist().iter().map(|t| t.id()).collect());
263 }
264
265 if flags.contains(PlayerQueryFlags::TRACKLIST_POSITION) {
266 query.tracklist_position = Some(self.playlist.position());
267 }
268
269 if flags.contains(PlayerQueryFlags::PLAYLIST_STATE) {
270 query.playlist_state = Some(self.playlist.playlist_hash());
271 }
272
273 if flags.contains(PlayerQueryFlags::SHUFFLE_MODE) {
274 query.shuffle_mode = Some(self.playlist.shuffle_mode());
275 }
276
277 if flags.contains(PlayerQueryFlags::LOOP_MODE) {
278 query.loop_mode = Some(self.playlist.loop_mode());
279 }
280
281 let _ = callback.send(query);
282 }
283
284 PlayerCommand::Play {
286 playables: playable,
287 } => {
288 self.playlist.set_playlist(playable);
289
290 let loaded = self.replace_decoders(true)?;
291
292 if loaded {
293 self.decoder_tx.set_playing(true)?;
294 }
295 }
296 PlayerCommand::SetIsPlaying { is_playing } => {
297 self.decoder_tx.set_playing(is_playing)?;
298 }
299 PlayerCommand::TogglePlaying { callback } => {
300 let state = self.decoder_tx.toggle_playing()?;
301 if let Some(callback) = callback {
302 let _ = callback.send(state);
303 }
304 }
305 PlayerCommand::Stop => {
306 self.decoder_tx.stop()?;
307 }
308 PlayerCommand::SetVolume {
309 volume,
310 increment,
311 callback,
312 } => {
313 let vol = self.set_volume(volume, increment);
314 if let Some(callback) = callback {
315 let _ = callback.send(vol);
316 }
317 }
318 PlayerCommand::Seek {
319 seconds,
320 increment,
321 callback,
322 } => {
323 let time = self.decoder_tx.seek(seconds, increment)?;
324 if let Some(callback) = callback {
325 let _ = callback.send(time);
326 }
327 }
328
329 PlayerCommand::QueueSet {
331 expected_state,
332 to,
333 callback,
334 } => {
335 let equals = expected_state == self.playlist.queue_hash();
336 if equals {
337 self.playlist.set_queue(&to);
338 self.try_preload(db)?;
339 }
340 if let Some(callback) = callback {
341 let _ = callback.send(equals);
342 }
343 }
344 PlayerCommand::QueueExtend { with } => {
345 self.playlist.extend_queue(&with);
346 self.try_preload(db)?;
347 }
348 PlayerCommand::QueueShuffle => {
349 self.playlist.shuffle_queue();
350 self.try_preload(db)?;
351 }
352 PlayerCommand::QueueClear => {
353 self.playlist.clear_queue();
354 self.try_preload(db)?;
355 }
356
357 PlayerCommand::PlaylistSet {
359 expected_state,
360 playables,
361 callback,
362 } => {
363 let equals = expected_state == self.playlist.playlist_hash();
364 if equals {
365 self.playlist.set_playlist(playables);
366 self.try_preload(db)?;
367 }
368
369 if let Some(callback) = callback {
370 let _ = callback.send(equals);
371 }
372 }
373 PlayerCommand::PlaylistExtend { items } => {
374 self.playlist.extend_playlist(items);
375 self.try_preload(db)?;
376 }
377 PlayerCommand::PlaylistClear => {
378 self.playlist.clear();
379 self.try_preload(db)?;
380 }
381 PlayerCommand::PlaylistSetShuffleMode { shuffle_mode } => {
382 self.playlist.set_shuffle_mode(shuffle_mode);
383 self.try_preload(db)?;
384 }
385 PlayerCommand::PlaylistSetLoopMode { loop_mode } => {
386 self.playlist.set_loop_mode(loop_mode);
387 }
388
389 PlayerCommand::TracklistSeek {
391 index,
392 increment,
393 callback,
394 } => {
395 let position = self.playlist.tracklist_seek(index, increment);
396 self.replace_decoders(false)?;
397 if let Some(callback) = callback {
398 let _ = callback.send(position);
399 }
400 }
401 PlayerCommand::Next => {
402 if matches!(self.playlist.loop_mode(), LoopMode::RepeatTrack) {
403 self.decoder_tx.seek(0.0, false)?;
404 } else {
405 self.decoder_tx.skip()?;
406 }
407 }
408 PlayerCommand::Previous => {
409 if matches!(self.playlist.loop_mode(), LoopMode::RepeatTrack) {
410 self.decoder_tx.seek(0.0, false)?;
411 } else if let Some(restart_on_previous_thresh) =
412 daemon_config().playback.previous_restart_thresh
413 && let Some(time) = self.decoder_tx.get_time()?
414 && time >= restart_on_previous_thresh
415 {
416 self.decoder_tx.seek(0.0, false)?;
417 } else {
418 self.playlist.tracklist_seek(-1, true);
419 self.replace_decoders(false)?;
420 }
421 }
422 }
423 Ok(())
424 }
425}