1use std::{
2 io::{self, Read, Write},
3 sync::mpsc::Sender,
4};
5
6use selene_core::library::collection::Collectable;
7
8use blake3::Hash;
9
10use crate::{
11 IpcCommand, IpcHandleError, IpcRequest, IpcTx, PacketError, PlayerEvent,
12 player::{PlayerQueryFlags, QueryResult},
13 playlist::{LoopMode, PlaybackStatus, ShuffleMode},
14};
15
16pub struct SeleneDaemon {
21 handle_tx: Sender<IpcRequest>,
22}
23
24impl SeleneDaemon {
26 pub fn connect<F>(callback: Option<F>) -> Result<SeleneDaemon, IpcHandleError>
34 where
35 Self: Sized,
36 F: FnMut(PlayerEvent) + Send + Sync + 'static,
37 {
38 #[cfg(unix)]
39 {
40 Ok(Self {
41 handle_tx: unix_socket_handle::UnixSocketHandle::connect(callback)?,
42 })
43 }
44
45 #[cfg(not(any(unix)))]
46 {
47 Err(IpcHandleError::UnsupportedPlatform)
48 }
49 }
50}
51
52impl SeleneDaemon {
53 pub fn ipc_generic(&self, command: IpcCommand) -> Result<(), PacketError> {
55 self.handle_tx.no_response(command)
56 }
57
58 pub fn ipc_flush(&self) -> Result<(), PacketError> {
59 self.handle_tx.request(IpcCommand::Flush)
60 }
61
62 pub fn ipc_disconnect(&self) {
63 self.handle_tx.action(IpcCommand::Disconnect);
64 }
65
66 pub fn ipc_reload_config(&self) -> Result<Result<(), String>, PacketError> {
67 self.handle_tx.request(IpcCommand::ReloadConfig)
68 }
69
70 pub fn ipc_play(&self, collectable: Collectable) {
72 self.handle_tx.action(IpcCommand::Play { collectable });
73 }
74
75 pub fn ipc_stop(&self) {
76 self.handle_tx.action(IpcCommand::Stop);
77 }
78
79 pub fn ipc_set_is_playing(&self, is_playing: bool) {
80 self.handle_tx
81 .action(IpcCommand::SetIsPlaying { is_playing });
82 }
83
84 pub fn ipc_toggle_is_playing(&self) -> Result<PlaybackStatus, PacketError> {
85 self.handle_tx.request(IpcCommand::TogglePlaying)
86 }
87
88 pub fn ipc_seek(&self, time: f64, increment: bool) -> Result<Option<f64>, PacketError> {
89 self.handle_tx.request(IpcCommand::Seek {
90 seconds: time,
91 increment,
92 })
93 }
94
95 pub fn ipc_set_volume(&self, volume: f32, increment: bool) -> Result<f32, PacketError> {
96 self.handle_tx
97 .request(IpcCommand::SetVolume { volume, increment })
98 }
99
100 pub fn ipc_queue_set(
102 &self,
103 tracks: Vec<Collectable>,
104 expected_state: Hash,
105 ) -> Result<bool, PacketError> {
106 self.handle_tx.request(IpcCommand::QueueSet {
107 tracks,
108 expected_state,
109 })
110 }
111
112 pub fn ipc_queue_extend(&self, tracks: Vec<Collectable>) {
113 self.handle_tx.action(IpcCommand::QueueExtend(tracks));
114 }
115
116 pub fn ipc_queue_shuffle(&self) {
117 self.handle_tx.action(IpcCommand::QueueShuffle);
118 }
119
120 pub fn ipc_queue_clear(&self) {
121 self.handle_tx.action(IpcCommand::QueueClear);
122 }
123
124 pub fn ipc_playlist_set(
126 &self,
127 collectables: Vec<Collectable>,
128 expected_state: Hash,
129 ) -> Result<bool, PacketError> {
130 self.handle_tx.request(IpcCommand::PlaylistSet {
131 collectables,
132 expected_state,
133 })
134 }
135
136 pub fn ipc_playlist_extend(&self, collectables: Vec<Collectable>) {
137 self.handle_tx
138 .action(IpcCommand::PlaylistExtend(collectables));
139 }
140
141 pub fn ipc_playlist_clear(&self) {
142 self.handle_tx.action(IpcCommand::PlaylistClear);
143 }
144
145 pub fn ipc_playlist_set_shuffle_mode(&self, shuffle_mode: ShuffleMode) {
146 self.handle_tx
147 .action(IpcCommand::PlaylistSetShuffleMode { shuffle_mode });
148 }
149
150 pub fn ipc_playlist_set_loop_mode(&self, loop_mode: LoopMode) {
151 self.handle_tx
152 .action(IpcCommand::PlaylistSetLoopMode { loop_mode });
153 }
154
155 pub fn ipc_tracklist_rebuild(&self) {
157 self.handle_tx.action(IpcCommand::TracklistRebuild);
158 }
159
160 pub fn ipc_tracklist_seek(&self, index: isize, increment: bool) -> Result<usize, PacketError> {
161 self.handle_tx
162 .request(IpcCommand::TracklistSeek { index, increment })
163 }
164
165 pub fn ipc_query(&self, flags: PlayerQueryFlags) -> Result<QueryResult, PacketError> {
167 self.handle_tx.request(IpcCommand::GetState { flags })
168 }
169}
170
171impl Drop for SeleneDaemon {
172 fn drop(&mut self) {
173 let () = self.handle_tx.disconnect();
174 }
175}
176
177pub trait SeleneDaemonHandle: Send {
179 fn connect<F>(callback: Option<F>) -> Result<Sender<IpcRequest>, IpcHandleError>
181 where
182 Self: Sized,
183 F: FnMut(PlayerEvent) + Send + Sync + 'static;
184
185 fn reconnect(&mut self) -> bool;
187
188 fn run<F>(self, callback: Option<F>)
190 where
191 F: FnMut(PlayerEvent) + Send + Sync + 'static;
192}
193
194fn read_packet_data(stream: &mut impl Read) -> io::Result<Vec<u8>> {
202 let mut len_buf = [0u8; 4];
203 stream.read_exact(&mut len_buf)?;
204 let len = u32::from_be_bytes(len_buf) as usize;
205
206 let mut data = vec![0u8; len];
207 stream.read_exact(&mut data)?;
208
209 Ok(data)
210}
211
212fn send_command(
213 stream: &mut impl Write,
214 command: &IpcCommand,
215) -> Result<(), Box<dyn std::error::Error>> {
216 let mut buf = Vec::new();
217 ciborium::into_writer(&command, &mut buf).expect("Serialization should not fail.");
218 stream.write_all(&(buf.len() as u32).to_be_bytes())?;
219 stream.write_all(&buf)?;
220 Ok(())
221}
222
223#[cfg(unix)]
224pub mod unix_socket_handle;