1use std::{
2 fs::{File, read},
3 io::Write,
4 path::PathBuf,
5 sync::{Arc, LazyLock, RwLock},
6 thread,
7 time::Duration,
8};
9
10use log::{error, trace};
11use rmp_serde::{Deserializer, Serializer};
12use serde::{Deserialize, Serialize};
13
14use rand::seq::SliceRandom;
15
16use crate::{
17 Error, Event,
18 music_lib::{CACHE_DIR, state::Track, tracks_from_hashes},
19 playback::{LoopState, PlaybackState, PlayerVolume, ShuffleState},
20};
21
22#[cfg(feature = "mpris")]
23use crate::playback::mpris;
24
25#[derive(Debug, Clone, Default, PartialEq)]
27pub struct PlayerState {
28 pub position: usize,
33 pub player_position: Duration,
34 pub player_volume: PlayerVolume,
35 pub playback_state: PlaybackState,
36 pub tracks: Vec<Track>,
37 pub shuffled_tracks: Vec<Track>,
38 pub shuffle_state: ShuffleState,
39 pub loop_state: LoopState,
40}
41
42impl TryFrom<PlayerStateRaw> for PlayerState {
43 type Error = Error;
44 fn try_from(value: PlayerStateRaw) -> Result<Self, Self::Error> {
45 Ok(Self {
46 position: value.position,
47 player_position: value.player_position,
48 player_volume: value.player_volume,
49 playback_state: PlaybackState::Stopped,
50 tracks: tracks_from_hashes(value.track_hashes)?
51 .into_iter()
52 .map(|t| t.as_ref().clone())
53 .collect(),
54 shuffled_tracks: tracks_from_hashes(value.shuffled_track_hashes)?
55 .into_iter()
56 .map(|t| t.as_ref().clone())
57 .collect(),
58 shuffle_state: value.shuffle_state,
59 loop_state: value.loop_state,
60 })
61 }
62}
63
64impl PlayerState {
65 fn on_track_changed(&self) {
66 crate::send_event(Event::PlayerStateChanged(self.clone()));
67
68 #[cfg(feature = "mpris")]
69 {
70 use mpris_server::{Metadata, Property};
71
72 let properties = vec![
73 match self.current() {
74 Some(track) => Property::Metadata(track.get_meta(self.position)),
75 None => Property::Metadata(Metadata::new()),
76 },
77 Property::CanGoPrevious(self.can_go_previous()),
78 Property::CanGoNext(self.can_go_next()),
79 ];
80 mpris::update_properties(properties);
81 }
82 }
83
84 pub(crate) fn on_playback_state_changed(&self) {
85 trace!("Playback state changed: {}", self.playback_state);
86
87 crate::send_event(Event::PlayerStateChanged(self.clone()));
88
89 #[cfg(feature = "mpris")]
90 {
91 use mpris_server::Property;
92 mpris::update_properties(vec![
93 Property::PlaybackStatus(mpris::playback_state_2_mpris(&self.playback_state)),
94 Property::CanPlay(self.can_play()),
95 Property::CanPause(self.can_pause()),
96 Property::CanSeek(self.can_seek()),
97 ]);
98 }
99 }
100
101 #[cfg(feature = "mpris")]
102 pub(crate) fn get_mpris_properties(&self) -> Vec<mpris_server::Property> {
103 use mpris_server::{Metadata, Property};
104
105 vec![
106 Property::PlaybackStatus(mpris::playback_state_2_mpris(&self.playback_state)),
107 Property::LoopStatus(mpris::loop_state_2_mpris(&self.loop_state)),
108 Property::Shuffle(self.shuffle_state.into()),
109 Property::Volume(self.player_volume.get()),
110 Property::CanGoNext(self.can_go_next()),
111 Property::CanGoPrevious(self.can_go_previous()),
112 Property::CanPlay(self.can_play()),
113 Property::CanPause(self.can_pause()),
114 Property::CanSeek(self.can_seek()),
115 match self.current() {
116 Some(track) => Property::Metadata(track.get_meta(self.position)),
117 None => Property::Metadata(Metadata::new()),
118 },
119 ]
120 }
121
122 pub fn set_tracks(&mut self, tracks: Vec<Track>) {
123 self.position = 0;
124 self.tracks = tracks;
125 self.set_playback_state(PlaybackState::Stopped);
126 if self.shuffle_state == ShuffleState::On {
127 self.shuffle();
128 }
129 self.on_track_changed();
130 }
131
132 pub fn append_tracks(&mut self, tracks: &mut Vec<Track>) {
133 self.tracks.append(tracks);
134 if self.shuffle_state == ShuffleState::On {
135 self.shuffle();
136 }
137 crate::send_event(Event::PlayerStateChanged(self.clone()));
138 #[cfg(feature = "mpris")]
139 {
140 use mpris_server::{Metadata, Property};
141
142 mpris::update_properties(vec![
143 match self.current() {
144 Some(track) => Property::Metadata(track.get_meta(self.position)),
145 None => Property::Metadata(Metadata::new()),
146 },
147 Property::CanGoPrevious(self.can_go_previous()),
148 Property::CanGoNext(self.can_go_next()),
149 Property::CanPlay(self.can_play()),
150 Property::CanPause(self.can_pause()),
151 ]);
152 }
153 }
154
155 pub fn shuffle(&mut self) {
157 if self.tracks.is_empty() {
158 use log::warn;
159
160 warn!("Refusing to shuffle an empty queue");
161 return;
162 }
163 let mut tracks = self.tracks.clone();
165 let prev_pos = self.position;
166 let track = tracks.swap_remove(prev_pos);
167 let mut rng = rand::rng();
168 tracks.shuffle(&mut rng);
169 tracks.insert(prev_pos, track);
170 self.shuffled_tracks = tracks;
171 crate::send_event(Event::PlayerStateChanged(self.clone()));
172 }
173
174 pub fn set_shuffle_state(&mut self, shuffle_state: ShuffleState) {
175 if self.shuffle_state != shuffle_state {
176 if shuffle_state == ShuffleState::Off
177 && let Some(track) = self.current().cloned()
178 {
179 match self.tracks.iter().position(|t| *t == track) {
180 Some(pos) => self.position = pos,
181 None => {
182 log::warn!(
183 "Could not find the previous track in the queue, this should never happen"
184 );
185 thread::spawn(crate::playback::stop);
186 }
187 }
188 }
189 self.shuffle_state = shuffle_state;
190 crate::send_event(Event::PlayerStateChanged(self.clone()));
191 #[cfg(feature = "mpris")]
192 {
193 use mpris_server::Property;
194
195 let properties = vec![
196 Property::Shuffle(self.shuffle_state.into()),
197 Property::CanGoPrevious(self.can_go_previous()),
198 Property::CanGoNext(self.can_go_next()),
199 ];
200 mpris::update_properties(properties);
201 }
202 }
203 }
204
205 pub fn increment_player_position(&mut self, duration: Duration) {
206 self.player_position += duration;
207 crate::send_event(Event::PlayerStateChanged(self.clone()));
208 #[cfg(feature = "mpris")]
209 {
210 use mpris_server::{Metadata, Property};
211
212 let meta = match self.current() {
213 Some(track) => track.get_meta(self.position),
214 None => Metadata::new(),
215 };
216 mpris::update_properties(vec![Property::Metadata(meta)]);
217 mpris::set_position(self.player_position);
218 }
219 }
220
221 pub fn set_player_position(&mut self, player_position: Duration) {
222 if self.player_position != player_position {
223 self.player_position = player_position;
224 crate::send_event(Event::PlayerStateChanged(self.clone()));
225 #[cfg(feature = "mpris")]
226 {
227 use mpris_server::{Metadata, Property};
228
229 let meta = match self.current() {
230 Some(track) => track.get_meta(self.position),
231 None => Metadata::new(),
232 };
233 mpris::update_properties(vec![Property::Metadata(meta)]);
234 mpris::set_position(player_position);
235 }
236 }
237 }
238
239 pub fn set_player_volume(&mut self, player_volume: PlayerVolume) {
240 if self.player_volume != player_volume {
241 self.player_volume = player_volume;
242 crate::send_event(Event::PlayerStateChanged(self.clone()));
243 #[cfg(feature = "mpris")]
244 {
245 use mpris_server::Property;
246
247 let properties = vec![Property::Volume(self.player_volume.get())];
248 mpris::update_properties(properties);
249 }
250 }
251 }
252
253 pub fn set_loop_state(&mut self, loop_state: LoopState) {
254 if self.loop_state != loop_state {
255 self.loop_state = loop_state;
256 crate::send_event(Event::PlayerStateChanged(self.clone()));
257 #[cfg(feature = "mpris")]
258 {
259 use mpris_server::Property;
260
261 let properties = vec![
262 Property::LoopStatus(mpris::loop_state_2_mpris(&self.loop_state)),
263 Property::CanGoPrevious(self.can_go_previous()),
264 Property::CanGoNext(self.can_go_next()),
265 ];
266 mpris::update_properties(properties);
267 }
268 }
269 }
270
271 pub fn set_playback_state(&mut self, playback_state: PlaybackState) {
272 if self.playback_state != playback_state {
273 self.playback_state = playback_state;
274 if self.playback_state == PlaybackState::Stopped {
275 self.set_player_position(Duration::default());
276 }
277 self.on_playback_state_changed();
278 }
279 }
280
281 pub fn is_empty(&self) -> bool {
282 match self.shuffle_state {
283 ShuffleState::Off => self.tracks.is_empty(),
284 ShuffleState::On => self.shuffled_tracks.is_empty(),
285 }
286 }
287
288 pub fn current(&self) -> Option<&Track> {
289 match self.shuffle_state {
290 ShuffleState::Off => {
291 if self.position < self.tracks.len() {
292 return Some(&self.tracks[self.position]);
293 }
294 }
295 ShuffleState::On => {
296 if self.position < self.shuffled_tracks.len() {
297 return Some(&self.shuffled_tracks[self.position]);
298 }
299 }
300 }
301 None
302 }
303
304 pub fn play_track(&mut self, index: usize) -> Option<&Track> {
305 if index < self.tracks.len() {
306 self.position = index;
307 self.on_track_changed();
308 self.current()
309 } else {
310 None
311 }
312 }
313
314 #[cfg(feature = "mpris")]
315 pub fn can_seek(&self) -> bool {
316 self.playback_state != PlaybackState::Stopped
317 }
318
319 #[cfg(feature = "mpris")]
320 pub fn can_play(&self) -> bool {
321 match self.shuffle_state {
322 ShuffleState::Off => {
323 self.position < self.tracks.len() && self.playback_state != PlaybackState::Playing
324 }
325 ShuffleState::On => {
326 self.position < self.shuffled_tracks.len()
327 && self.playback_state != PlaybackState::Playing
328 }
329 }
330 }
331
332 #[cfg(feature = "mpris")]
333 pub fn can_pause(&self) -> bool {
334 self.playback_state == PlaybackState::Playing
335 }
336
337 pub fn can_go_next(&self) -> bool {
338 match self.loop_state {
339 LoopState::Off => match self.shuffle_state {
340 ShuffleState::Off => {
341 !self.tracks.is_empty() && self.position < self.tracks.len() - 1
342 }
343 ShuffleState::On => {
344 !self.shuffled_tracks.is_empty()
345 && self.position < self.shuffled_tracks.len() - 1
346 }
347 },
348 _ => match self.shuffle_state {
349 ShuffleState::Off => !self.tracks.is_empty(),
350 ShuffleState::On => !self.shuffled_tracks.is_empty(),
351 },
352 }
353 }
354
355 pub fn next_track(&mut self) -> Option<&Track> {
356 match self.loop_state {
357 LoopState::Off => {
358 let tracks = match self.shuffle_state {
359 ShuffleState::Off => &self.tracks,
360 ShuffleState::On => &self.shuffled_tracks,
361 };
362 if !tracks.is_empty() && self.position < tracks.len() - 1 {
363 self.position += 1;
364 self.on_track_changed();
365 return self.current();
366 }
367 None
368 }
369 LoopState::Track => {
370 self.on_track_changed();
371 self.current()
372 }
373 LoopState::Playlist => {
374 let tracks = match self.shuffle_state {
375 ShuffleState::Off => &self.tracks,
376 ShuffleState::On => &self.shuffled_tracks,
377 };
378 if tracks.is_empty() {
379 None
380 } else if !tracks.is_empty() && self.position < tracks.len() - 1 {
381 self.position += 1;
382 self.on_track_changed();
383 self.current()
384 } else {
385 self.position = 0;
386 self.on_track_changed();
387 self.current()
388 }
389 }
390 }
391 }
392
393 pub fn can_go_previous(&self) -> bool {
394 match self.loop_state {
395 LoopState::Off => match self.shuffle_state {
396 ShuffleState::Off => !self.tracks.is_empty() && self.position > 0,
397 ShuffleState::On => !self.shuffled_tracks.is_empty() && self.position > 0,
398 },
399 _ => match self.shuffle_state {
400 ShuffleState::Off => !self.tracks.is_empty(),
401 ShuffleState::On => !self.shuffled_tracks.is_empty(),
402 },
403 }
404 }
405
406 pub fn previous_track(&mut self) -> Option<&Track> {
407 match self.loop_state {
408 LoopState::Off => {
409 let tracks = match self.shuffle_state {
410 ShuffleState::Off => &self.tracks,
411 ShuffleState::On => &self.shuffled_tracks,
412 };
413 if self.position > 0 && !tracks.is_empty() {
414 self.position -= 1;
415 self.on_track_changed();
416 self.current()
417 } else {
418 None
419 }
420 }
421 LoopState::Track => {
422 self.on_track_changed();
423 self.current()
424 }
425 LoopState::Playlist => {
426 let tracks = match self.shuffle_state {
427 ShuffleState::Off => &self.tracks,
428 ShuffleState::On => &self.shuffled_tracks,
429 };
430 if tracks.is_empty() {
431 None
432 } else if self.position > 0 {
433 self.position -= 1;
434 self.on_track_changed();
435 self.current()
436 } else if !tracks.is_empty() {
437 self.position = tracks.len() - 1;
438 self.on_track_changed();
439 self.current()
440 } else {
441 None
442 }
443 }
444 }
445 }
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize)]
449struct PlayerStateRaw {
450 position: usize,
451 player_position: Duration,
452 player_volume: PlayerVolume,
453 track_hashes: Vec<u64>,
454 shuffled_track_hashes: Vec<u64>,
455 shuffle_state: ShuffleState,
456 loop_state: LoopState,
457}
458
459impl From<PlayerState> for PlayerStateRaw {
460 fn from(value: PlayerState) -> Self {
461 let track_hashes = Track::hash_tracks(&value.tracks);
462 let shuffled_track_hashes = Track::hash_tracks(&value.shuffled_tracks);
463 Self {
464 position: value.position,
465 player_position: value.player_position,
466 player_volume: value.player_volume,
467 track_hashes,
468 shuffled_track_hashes,
469 shuffle_state: value.shuffle_state,
470 loop_state: value.loop_state,
471 }
472 }
473}
474
475static STATE_FILE: LazyLock<PathBuf> = LazyLock::new(|| {
476 let mut data = CACHE_DIR.read().unwrap().clone().unwrap();
477 data.push("player_state");
478 data
479});
480
481pub(crate) static PLAYER_STATE: LazyLock<Arc<RwLock<Option<PlayerState>>>> =
482 LazyLock::new(|| Arc::new(RwLock::new(None)));
483
484pub(crate) fn unwrap_state_ref(maybe_state: Option<&PlayerState>) -> Result<&PlayerState, Error> {
485 match maybe_state {
486 Some(state) => Ok(state),
487 None => Err(Error::StateNotInitialized),
488 }
489}
490
491pub(crate) fn unwrap_state_mut(
492 maybe_state: Option<&mut PlayerState>,
493) -> Result<&mut PlayerState, Error> {
494 match maybe_state {
495 Some(state) => Ok(state),
496 None => Err(Error::StateNotInitialized),
497 }
498}
499
500fn save_state(state: PlayerState) -> Result<(), String> {
501 let state_raw: PlayerStateRaw = state.into();
502 let state_file = STATE_FILE.clone();
503
504 let mut data = Vec::new();
505 if let Err(e) = state_raw.serialize(&mut Serializer::new(&mut data)) {
506 error!("Could not serialize the player state: {e}");
507 return Err(e.to_string());
508 }
509
510 let mut file = match File::create(state_file) {
511 Ok(file) => file,
512 Err(e) => {
513 error!("Could not open the player state cache in write-only mode: {e}");
514 return Err(e.to_string());
515 }
516 };
517
518 match file.write_all(&data) {
519 Ok(_) => {
520 trace!("Saved player state to cache");
521 Ok(())
522 }
523 Err(e) => {
524 error!("Could not write to the player state cache: {e}");
525 Err(e.to_string())
526 }
527 }
528}
529
530pub(crate) fn background_save_state(state: PlayerState) {
531 thread::spawn(|| {
532 if let Err(e) = save_state(state) {
533 error!("Could not save player state to cache: {e}");
534 }
535 });
536}
537
538pub(crate) fn restore_state_from_cache() -> Result<PlayerState, String> {
539 let state_file = STATE_FILE.clone();
540
541 trace!("Restoring player state from {state_file:?}");
542
543 let state_exists = match state_file.try_exists() {
544 Ok(exists) => exists,
545 Err(e) => {
546 error!("Could not check if the player state file exists: {e}");
547 return Err(e.to_string());
548 }
549 };
550
551 if state_exists {
552 let data = match read(state_file) {
553 Ok(data) => data,
554 Err(e) => {
555 error!("Could not read the player state cache: {e}");
556 return Err(e.to_string());
557 }
558 };
559
560 let state_raw = match PlayerStateRaw::deserialize(&mut Deserializer::from_read_ref(&data)) {
561 Ok(data) => data,
562 Err(e) => {
563 error!("Could not decode the contents of the player state file: {e}");
564 return Err(e.to_string());
565 }
566 };
567
568 match <PlayerStateRaw as TryInto<PlayerState>>::try_into(state_raw) {
569 Ok(mut state) => {
570 state.playback_state = PlaybackState::Stopped;
571 Ok(state)
572 }
573 Err(e) => {
574 error!("Could not restore player state from cache: {e}");
575 Err(e.to_string())
576 }
577 }
578 } else {
579 Ok(PlayerState::default())
580 }
581}