Skip to main content

selene_daemon/player/
ipc.rs

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    // Generic
24    Flush {
25        callback: Sender<()>,
26    },
27
28    // Playback
29    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    // The override queue
53    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    // The playlist
65    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    // Tracklist
82    TracklistSeek {
83        index: isize,
84        increment: bool,
85        callback: Option<Sender<Option<usize>>>,
86    },
87
88    // Queries
89    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            // Generic
210            PlayerCommand::Flush { callback } => {
211                let _ = callback.send(());
212            }
213
214            // Queries
215            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            // Playback
285            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            // Queue
330            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            // Playlist
358            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            // Tracklist
390            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}