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