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 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    // Generic
21    Flush {
22        callback: Sender<()>,
23    },
24
25    // Playback
26    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    // The override queue
50    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    // The playlist
62    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    // Tracklist
79    TracklistSeek {
80        index: isize,
81        increment: bool,
82        callback: Option<Sender<usize>>,
83    },
84    TracklistRebuild,
85
86    // Queries
87    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            // Generic
147            PlayerCommand::Flush { callback } => {
148                let _ = callback.send(());
149            }
150
151            // Queries
152            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            // Playback
237            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            // Queue
280            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            // Playlist
305            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            // Tracklist
335            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}