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, warn};
11use rmp_serde::{Deserializer, Serializer};
12use serde::{Deserialize, Serialize};
13
14use rand::seq::SliceRandom;
15
16use crate::{
17 Error,
18 music_lib::{Album, Artist, CACHE_DIR, Genre, Playlist, 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, PartialEq)]
26pub enum Event {
27 StateInitialized(PlayerState),
28 PositionChanged(usize),
29 PlayerPositionChanged(Duration),
30 PlayerVolumeChanged(PlayerVolume),
31 PlaybackStateChanged(PlaybackState),
32 TracksChanged {
34 position: usize,
35 tracks: Vec<Arc<Track>>,
36 shuffled_indices: Vec<usize>,
37 },
38 ShuffledTrackIndicesChanged(Vec<usize>),
39 QueueSourceChanged(QueueSource),
40 ShuffleStateChanged(ShuffleState),
41 LoopStateChanged(LoopState),
42}
43
44impl Event {
45 fn send(self) {
46 crate::send_event(crate::Event::Playback(self));
47 }
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub enum Queue {
52 Playlist(Arc<Playlist>),
53 Album(Arc<Album>),
54 Artist(Arc<Artist>),
55 Genre(Arc<Genre>),
56 AllTracks(Vec<Arc<Track>>),
57 Custom {
58 label: String,
59 tracks: Vec<Arc<Track>>,
60 },
61}
62
63impl Queue {
64 pub fn source(&self) -> QueueSource {
65 match self {
66 Self::Playlist(playlist) => QueueSource::Playlist {
67 name: playlist.name.clone(),
68 },
69 Self::Album(album) => QueueSource::Album {
70 title: album.title.clone(),
71 },
72 Self::Artist(artist) => QueueSource::Artist {
73 name: artist.name.clone(),
74 },
75 Self::Genre(genre) => QueueSource::Genre {
76 name: genre.name.clone(),
77 },
78 Self::AllTracks(_) => QueueSource::AllTracks,
79 Self::Custom { label, tracks: _ } => QueueSource::Custom {
80 label: label.clone(),
81 },
82 }
83 }
84
85 pub fn tracks(self) -> Vec<Arc<Track>> {
86 match self {
87 Self::Playlist(playlist) => playlist.tracks.clone(),
88 Self::Album(album) => album.tracks.clone(),
89 Self::Artist(artist) => artist.tracks.clone(),
90 Self::Genre(genre) => genre.tracks.clone(),
91 Self::AllTracks(tracks) => tracks,
92 Self::Custom { label: _, tracks } => tracks,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
98pub enum QueueSource {
99 #[default]
100 None,
101 AllTracks,
102 Custom {
103 label: String,
104 },
105 Playlist {
106 name: String,
107 },
108 Album {
109 title: String,
110 },
111 Artist {
112 name: String,
113 },
114 Genre {
115 name: String,
116 },
117}
118
119impl QueueSource {
120 pub fn identity(&self) -> Option<&str> {
121 match self {
122 Self::None => None,
123 Self::AllTracks => Some("All tracks"),
124 Self::Custom { label } => Some(label),
125 Self::Playlist { name } => Some(name),
126 Self::Album { title } => Some(title),
127 Self::Artist { name } => Some(name),
128 Self::Genre { name } => Some(name),
129 }
130 }
131}
132
133#[derive(Debug, Clone, Default, PartialEq)]
135pub struct PlayerState {
136 pub position: usize,
141 pub player_position: Duration,
142 pub player_volume: PlayerVolume,
143 pub playback_state: PlaybackState,
144 pub tracks: Vec<Arc<Track>>,
145 pub shuffled_track_indices: Vec<usize>,
146 pub queue_source: QueueSource,
147 pub shuffle_state: ShuffleState,
148 pub loop_state: LoopState,
149}
150
151impl TryFrom<PlayerStateRaw> for PlayerState {
152 type Error = Error;
153 fn try_from(value: PlayerStateRaw) -> Result<Self, Self::Error> {
154 let (tracks, shuffled_track_indices) = {
155 let result = tracks_from_hashes(value.track_hashes)?;
156 if !result.unmatched.is_empty() {
157 warn!("{} missing tracks in the queue", result.unmatched.len());
158 let indices = result.matched.iter().enumerate().map(|(i, _)| i).collect();
159 (result.matched, indices)
160 } else {
161 let mut indices_deduped = value.shuffled_track_indices;
162 indices_deduped.dedup();
163 if result.matched.len() == indices_deduped.len()
164 && indices_deduped.iter().max().unwrap_or(&0) < &result.matched.len()
165 {
166 (result.matched, indices_deduped)
167 } else {
168 let indices = result.matched.iter().enumerate().map(|(i, _)| i).collect();
169 (result.matched, indices)
170 }
171 }
172 };
173 let playback_state = if tracks.is_empty() {
174 PlaybackState::Stopped
175 } else {
176 PlaybackState::Paused
177 };
178 let position = value.position.min(tracks.len().saturating_sub(1));
179 Ok(Self {
180 position,
181 player_position: value.player_position,
182 player_volume: value.player_volume,
183 playback_state,
184 tracks,
185 shuffled_track_indices,
186 queue_source: value.queue_source,
187 shuffle_state: value.shuffle_state,
188 loop_state: value.loop_state,
189 })
190 }
191}
192
193impl PlayerState {
194 pub fn queue_empty(&self) -> bool {
195 self.tracks.is_empty()
196 }
197
198 pub fn current(&self) -> Option<&Arc<Track>> {
199 match self.shuffle_state {
200 ShuffleState::Off => {
201 return self.tracks.get(self.position);
202 }
203 ShuffleState::On => {
204 if let Some(index) = self.shuffled_track_indices.get(self.position) {
205 return self.tracks.get(*index);
206 }
207 }
208 }
209 None
210 }
211
212 pub fn real_track_index(&self, index: usize) -> Option<usize> {
220 match self.shuffle_state {
221 ShuffleState::Off => {
222 if index >= self.tracks.len() {
223 None
224 } else {
225 Some(index)
226 }
227 }
228 ShuffleState::On => self.shuffled_track_indices.get(index).copied(),
229 }
230 }
231
232 pub fn can_seek(&self) -> bool {
233 self.playback_state != PlaybackState::Stopped
234 }
235
236 pub fn can_play(&self) -> bool {
237 self.position < self.tracks.len() && !self.is_playing()
238 }
239
240 pub fn can_pause(&self) -> bool {
241 self.playback_state == PlaybackState::Playing
242 }
243
244 pub fn can_toggle_playing(&self) -> bool {
245 match self.playback_state {
246 PlaybackState::Playing => self.can_pause(),
247 PlaybackState::Paused | PlaybackState::Stopped => self.can_play(),
248 }
249 }
250
251 pub fn can_go_next(&self) -> bool {
252 match self.loop_state {
253 LoopState::Off => !self.tracks.is_empty() && self.position < self.tracks.len() - 1,
254 _ => !self.tracks.is_empty(),
255 }
256 }
257
258 pub fn can_go_previous(&self) -> bool {
259 match self.loop_state {
260 LoopState::Off => !self.tracks.is_empty() && self.position > 0,
261 _ => !self.tracks.is_empty(),
262 }
263 }
264
265 pub fn is_playing(&self) -> bool {
266 self.playback_state == PlaybackState::Playing
267 }
268
269 pub fn is_paused(&self) -> bool {
270 self.playback_state == PlaybackState::Paused
271 }
272
273 pub fn stopped(&self) -> bool {
274 self.playback_state == PlaybackState::Stopped
275 }
276
277 pub fn shuffle_enabled(&self) -> bool {
278 self.shuffle_state.enabled()
279 }
280
281 pub fn handle_event(&mut self, event: Event) {
282 match event {
283 Event::StateInitialized(player_state) => *self = player_state,
284 Event::PositionChanged(pos) => self.position = pos,
285 Event::PlayerPositionChanged(player_position) => self.player_position = player_position,
286 Event::PlayerVolumeChanged(player_volume) => self.player_volume = player_volume,
287 Event::PlaybackStateChanged(playback_state) => self.playback_state = playback_state,
288 Event::ShuffleStateChanged(shuffle_state) => self.shuffle_state = shuffle_state,
289 Event::LoopStateChanged(loop_state) => self.loop_state = loop_state,
290 Event::TracksChanged {
291 position,
292 tracks,
293 shuffled_indices,
294 } => {
295 self.position = position;
296 self.tracks = tracks;
297 self.shuffled_track_indices = shuffled_indices;
298 }
299 Event::QueueSourceChanged(queue_source) => self.queue_source = queue_source,
300 Event::ShuffledTrackIndicesChanged(indices) => self.shuffled_track_indices = indices,
301 }
302 }
303
304 pub(crate) fn on_playback_state_changed(&self) {
305 trace!("Playback state changed: {}", self.playback_state);
306
307 Event::PlaybackStateChanged(self.playback_state).send();
308
309 #[cfg(feature = "mpris")]
310 {
311 use mpris_server::Property;
312 mpris::update_properties(vec![
313 Property::PlaybackStatus(mpris::playback_state_2_mpris(&self.playback_state)),
314 Property::CanPlay(self.can_play()),
315 Property::CanPause(self.can_pause()),
316 Property::CanSeek(self.can_seek()),
317 ]);
318 }
319 }
320
321 pub(crate) fn set_queue(&mut self, queue: Queue) {
322 self.position = 0;
323 self.queue_source = queue.source();
324 self.tracks = queue.tracks();
325 self.set_playback_state(PlaybackState::Stopped);
326 if self.shuffle_state.enabled() {
327 self.shuffle();
328 }
329 Event::QueueSourceChanged(self.queue_source.clone()).send();
330 self.on_track_changed();
331 }
332
333 pub(crate) fn play_new_queue(&mut self, queue: Queue, index: Option<usize>) {
334 trace!("Setting a new queue");
335 let shuffle_enabled = self.shuffle_state.enabled();
336 if shuffle_enabled {
337 self.shuffle_state = ShuffleState::Off;
339 }
340 self.position = index.unwrap_or_default();
341 self.queue_source = queue.source();
342 Event::QueueSourceChanged(self.queue_source.clone()).send();
343 self.tracks = queue.tracks();
344 if shuffle_enabled {
345 self.shuffle_state = ShuffleState::On;
346 if index.is_some() {
347 self.shuffle();
348 } else {
349 self.full_shuffle();
350 }
351 }
352 Event::TracksChanged {
353 position: self.position,
354 tracks: self.tracks.clone(),
355 shuffled_indices: self.shuffled_track_indices.clone(),
356 }
357 .send();
358 self.on_track_changed();
359 self.set_player_position(Duration::default());
360 }
361
362 pub(crate) fn append_tracks(&mut self, tracks: &mut Vec<Arc<Track>>) {
363 let start = self.shuffled_track_indices.len();
364 let count = tracks.len();
365 self.tracks.append(tracks);
366 self.shuffled_track_indices.extend(start..start + count);
367
368 Event::TracksChanged {
369 position: self.position,
370 tracks: self.tracks.clone(),
371 shuffled_indices: self.shuffled_track_indices.clone(),
372 }
373 .send();
374 #[cfg(feature = "mpris")]
375 {
376 use mpris_server::{Metadata, Property};
377
378 mpris::update_properties(vec![
379 match self.current() {
380 Some(track) => Property::Metadata(track.get_meta(self.position)),
381 None => Property::Metadata(Metadata::new()),
382 },
383 Property::CanGoPrevious(self.can_go_previous()),
384 Property::CanGoNext(self.can_go_next()),
385 Property::CanPlay(self.can_play()),
386 Property::CanPause(self.can_pause()),
387 ]);
388 }
389 }
390
391 pub(crate) fn remove_tracks(&mut self, mut indices: Vec<usize>) -> Result<(), Error> {
392 indices.dedup();
393 let position_shift = indices.iter().filter(|i| **i < self.position).count();
394 let mut to_remove = vec![false; self.tracks.len()];
395 for i in &indices {
396 if let Some(val) = to_remove.get_mut(*i) {
397 *val = true;
398 } else {
399 error!("Cannot remove tracks from the queue: Track index {i} is out of bounds");
400 return Err(Error::NoTrackAtIndex(*i));
401 }
402 }
403 let prev_track = self.current().cloned();
404 if self.shuffle_enabled() {
405 let mut remove_real = vec![false; self.tracks.len()];
406 for &queue_index in &indices {
407 let real_index = self.shuffled_track_indices[queue_index];
408 remove_real[real_index] = true;
409 }
410
411 let mut remap = vec![0; self.tracks.len()];
412 let mut new_index = 0;
413 for old_index in 0..self.tracks.len() {
414 if !remove_real[old_index] {
415 remap[old_index] = new_index;
416 new_index += 1;
417 }
418 }
419
420 self.shuffled_track_indices = std::mem::take(&mut self.shuffled_track_indices)
421 .into_iter()
422 .enumerate()
423 .filter_map(|(queue_index, old_real_index)| {
424 (!to_remove[queue_index]).then_some(remap[old_real_index])
425 })
426 .collect();
427
428 self.tracks = std::mem::take(&mut self.tracks)
429 .into_iter()
430 .enumerate()
431 .filter_map(|(real_index, track)| (!remove_real[real_index]).then_some(track))
432 .collect();
433 } else {
434 self.tracks = std::mem::take(&mut self.tracks)
435 .into_iter()
436 .enumerate()
437 .filter_map(|(i, track)| (!to_remove[i]).then_some(track))
438 .collect();
439 }
440 self.position = self.position.saturating_sub(position_shift);
441 if prev_track != self.current().cloned() {
442 self.set_player_position(Duration::default());
443 self.on_track_changed();
444 }
445
446 Event::TracksChanged {
447 position: self.position,
448 tracks: self.tracks.clone(),
449 shuffled_indices: self.shuffled_track_indices.clone(),
450 }
451 .send();
452 Ok(())
453 }
454
455 fn full_shuffle(&mut self) {
457 if self.tracks.is_empty() {
458 use log::warn;
459 warn!("Refusing to shuffle an empty queue");
460 return;
461 }
462
463 self.shuffled_track_indices = self.tracks.iter().enumerate().map(|(i, _)| i).collect();
464 let mut rng = rand::rng();
465 self.shuffled_track_indices.shuffle(&mut rng);
466
467 Event::ShuffledTrackIndicesChanged(self.shuffled_track_indices.clone()).send();
468 }
469
470 pub(crate) fn shuffle(&mut self) {
475 if self.tracks.is_empty() {
476 use log::warn;
477
478 warn!("Refusing to shuffle an empty queue");
479 return;
480 }
481
482 self.shuffled_track_indices = self.tracks.iter().enumerate().map(|(i, _)| i).collect();
483 let len = self.shuffled_track_indices.len();
484 let pos = self.position;
485
486 self.shuffled_track_indices.swap(pos, 0);
487 let mut rng = rand::rng();
488 self.shuffled_track_indices[1..len].shuffle(&mut rng);
489 self.position = 0;
490
491 Event::PositionChanged(self.position).send();
492 Event::ShuffledTrackIndicesChanged(self.shuffled_track_indices.clone()).send();
493 }
494
495 pub(crate) fn set_shuffle_state(&mut self, shuffle_state: ShuffleState) {
496 if !shuffle_state.enabled()
497 && let Some(track) = self.current()
498 {
499 match self.tracks.iter().position(|t| t == track) {
500 Some(pos) => {
501 self.position = pos;
502 Event::PositionChanged(self.position).send();
503 }
504 None => {
505 log::warn!(
506 "Could not find the previous track in the queue, this should never happen"
507 );
508 thread::spawn(crate::playback::stop);
509 }
510 }
511 } else {
512 self.shuffle();
513 }
514 self.shuffle_state = shuffle_state;
515 Event::ShuffleStateChanged(self.shuffle_state).send();
516 #[cfg(feature = "mpris")]
517 {
518 use mpris_server::Property;
519
520 let properties = vec![
521 Property::Shuffle(self.shuffle_state.enabled()),
522 Property::CanGoPrevious(self.can_go_previous()),
523 Property::CanGoNext(self.can_go_next()),
524 ];
525 mpris::update_properties(properties);
526 }
527 }
528
529 pub(crate) fn increment_player_position(&mut self, duration: Duration) {
530 self.player_position += duration;
531 Event::PlayerPositionChanged(self.player_position).send();
532 #[cfg(feature = "mpris")]
533 {
534 use mpris_server::{Metadata, Property};
535
536 let meta = match self.current() {
537 Some(track) => track.get_meta(self.position),
538 None => Metadata::new(),
539 };
540 mpris::update_properties(vec![Property::Metadata(meta)]);
541 mpris::set_position(self.player_position);
542 }
543 }
544
545 pub(crate) fn set_player_position(&mut self, player_position: Duration) {
546 self.player_position = player_position;
547 Event::PlayerPositionChanged(self.player_position).send();
548 #[cfg(feature = "mpris")]
549 {
550 use mpris_server::{Metadata, Property};
551
552 let meta = match self.current() {
553 Some(track) => track.get_meta(self.position),
554 None => Metadata::new(),
555 };
556 mpris::update_properties(vec![Property::Metadata(meta)]);
557 mpris::set_position(player_position);
558 }
559 }
560
561 pub(crate) fn set_player_volume(&mut self, player_volume: PlayerVolume) {
562 self.player_volume = player_volume;
563 Event::PlayerVolumeChanged(self.player_volume).send();
564 #[cfg(feature = "mpris")]
565 {
566 use mpris_server::Property;
567
568 let properties = vec![Property::Volume(self.player_volume.get())];
569 mpris::update_properties(properties);
570 }
571 }
572
573 pub(crate) fn set_loop_state(&mut self, loop_state: LoopState) {
574 self.loop_state = loop_state;
575 Event::LoopStateChanged(self.loop_state).send();
576 #[cfg(feature = "mpris")]
577 {
578 use mpris_server::Property;
579
580 let properties = vec![
581 Property::LoopStatus(mpris::loop_state_2_mpris(&self.loop_state)),
582 Property::CanGoPrevious(self.can_go_previous()),
583 Property::CanGoNext(self.can_go_next()),
584 ];
585 mpris::update_properties(properties);
586 }
587 }
588
589 pub(crate) fn set_playback_state(&mut self, playback_state: PlaybackState) {
590 self.playback_state = playback_state;
591 if self.playback_state == PlaybackState::Stopped {
592 self.set_player_position(Duration::default());
593 }
594 self.on_playback_state_changed();
595 }
596
597 pub(crate) fn play_track(&mut self, index: usize) -> Option<&Arc<Track>> {
598 if index < self.tracks.len() {
599 self.position = index;
600 self.on_track_changed();
601 self.set_player_position(Duration::default());
602 self.current()
603 } else {
604 None
605 }
606 }
607
608 pub(crate) fn next_track(&mut self) -> Option<&Arc<Track>> {
609 match self.loop_state {
610 LoopState::Off => {
611 if !self.tracks.is_empty() && self.position < self.tracks.len() - 1 {
612 self.position += 1;
613 self.on_track_changed();
614 return self.current();
615 }
616 None
617 }
618 LoopState::Track => {
619 self.on_track_changed();
620 self.current()
621 }
622 LoopState::Playlist => {
623 if self.tracks.is_empty() {
624 None
625 } else if !self.tracks.is_empty() && self.position < self.tracks.len() - 1 {
626 self.position += 1;
627 self.on_track_changed();
628 self.current()
629 } else {
630 self.position = 0;
631 self.on_track_changed();
632 self.current()
633 }
634 }
635 }
636 }
637
638 pub(crate) fn previous_track(&mut self) -> Option<&Arc<Track>> {
639 match self.loop_state {
640 LoopState::Off => {
641 if self.position > 0 && !self.tracks.is_empty() {
642 self.position -= 1;
643 self.on_track_changed();
644 self.current()
645 } else {
646 None
647 }
648 }
649 LoopState::Track => {
650 self.on_track_changed();
651 self.current()
652 }
653 LoopState::Playlist => {
654 if self.tracks.is_empty() {
655 None
656 } else if self.position > 0 {
657 self.position -= 1;
658 self.on_track_changed();
659 self.current()
660 } else if !self.tracks.is_empty() {
661 self.position = self.tracks.len() - 1;
662 self.on_track_changed();
663 self.current()
664 } else {
665 None
666 }
667 }
668 }
669 }
670
671 fn on_track_changed(&self) {
672 Event::PositionChanged(self.position).send();
673
674 #[cfg(feature = "mpris")]
675 {
676 use mpris_server::{Metadata, Property};
677
678 let properties = vec![
679 match self.current() {
680 Some(track) => Property::Metadata(track.get_meta(self.position)),
681 None => Property::Metadata(Metadata::new()),
682 },
683 Property::CanGoPrevious(self.can_go_previous()),
684 Property::CanGoNext(self.can_go_next()),
685 ];
686 mpris::update_properties(properties);
687 mpris::set_position(self.player_position);
688 }
689 }
690
691 #[cfg(feature = "mpris")]
692 pub(crate) fn get_mpris_properties(&self) -> Vec<mpris_server::Property> {
693 use mpris_server::{Metadata, Property};
694
695 vec![
696 Property::PlaybackStatus(mpris::playback_state_2_mpris(&self.playback_state)),
697 Property::LoopStatus(mpris::loop_state_2_mpris(&self.loop_state)),
698 Property::Shuffle(self.shuffle_state.enabled()),
699 Property::Volume(self.player_volume.get()),
700 Property::CanGoNext(self.can_go_next()),
701 Property::CanGoPrevious(self.can_go_previous()),
702 Property::CanPlay(self.can_play()),
703 Property::CanPause(self.can_pause()),
704 Property::CanSeek(self.can_seek()),
705 match self.current() {
706 Some(track) => Property::Metadata(track.get_meta(self.position)),
707 None => Property::Metadata(Metadata::new()),
708 },
709 ]
710 }
711}
712
713#[derive(Debug, Clone, Serialize, Deserialize)]
714struct PlayerStateRaw {
715 position: usize,
716 player_position: Duration,
717 player_volume: PlayerVolume,
718 track_hashes: Vec<u64>,
719 shuffled_track_indices: Vec<usize>,
720 queue_source: QueueSource,
721 shuffle_state: ShuffleState,
722 loop_state: LoopState,
723}
724
725impl From<PlayerState> for PlayerStateRaw {
726 fn from(value: PlayerState) -> Self {
727 let track_hashes = Track::hash_tracks(value.tracks);
728 Self {
729 position: value.position,
730 player_position: value.player_position,
731 player_volume: value.player_volume,
732 track_hashes,
733 shuffled_track_indices: value.shuffled_track_indices,
734 queue_source: value.queue_source,
735 shuffle_state: value.shuffle_state,
736 loop_state: value.loop_state,
737 }
738 }
739}
740
741static STATE_FILE: LazyLock<PathBuf> = LazyLock::new(|| {
742 let mut data = CACHE_DIR.read().unwrap().clone().unwrap();
743 data.push("player_state");
744 data
745});
746
747pub(crate) static PLAYER_STATE: LazyLock<Arc<RwLock<Option<PlayerState>>>> =
748 LazyLock::new(|| Arc::new(RwLock::new(None)));
749
750pub(crate) fn unwrap_state_ref(maybe_state: Option<&PlayerState>) -> Result<&PlayerState, Error> {
751 match maybe_state {
752 Some(state) => Ok(state),
753 None => Err(Error::StateNotInitialized),
754 }
755}
756
757pub(crate) fn unwrap_state_mut(
758 maybe_state: Option<&mut PlayerState>,
759) -> Result<&mut PlayerState, Error> {
760 match maybe_state {
761 Some(state) => Ok(state),
762 None => Err(Error::StateNotInitialized),
763 }
764}
765
766pub(crate) fn save_state(state: PlayerState) -> Result<(), String> {
767 let state_raw: PlayerStateRaw = state.into();
768 let state_file = STATE_FILE.clone();
769
770 let mut data = Vec::new();
771 if let Err(e) = state_raw.serialize(&mut Serializer::new(&mut data)) {
772 error!("Could not serialize the player state: {e}");
773 return Err(e.to_string());
774 }
775
776 let mut file = match File::create(state_file) {
777 Ok(file) => file,
778 Err(e) => {
779 error!("Could not open the player state cache in write-only mode: {e}");
780 return Err(e.to_string());
781 }
782 };
783
784 match file.write_all(&data) {
785 Ok(_) => {
786 trace!("Saved player state to cache");
787 Ok(())
788 }
789 Err(e) => {
790 error!("Could not write to the player state cache: {e}");
791 Err(e.to_string())
792 }
793 }
794}
795
796pub(crate) fn background_save_state(state: PlayerState) {
797 thread::spawn(|| {
798 if let Err(e) = save_state(state) {
799 error!("Could not save player state to cache: {e}");
800 }
801 });
802}
803
804pub(crate) fn restore_state_from_cache() -> Result<PlayerState, String> {
805 let state_file = STATE_FILE.clone();
806
807 trace!("Restoring player state from {state_file:?}");
808
809 let state_exists = match state_file.try_exists() {
810 Ok(exists) => exists,
811 Err(e) => {
812 error!("Could not check if the player state file exists: {e}");
813 return Err(e.to_string());
814 }
815 };
816
817 if state_exists {
818 let data = match read(state_file) {
819 Ok(data) => data,
820 Err(e) => {
821 error!("Could not read the player state cache: {e}");
822 return Err(e.to_string());
823 }
824 };
825
826 let state_raw = match PlayerStateRaw::deserialize(&mut Deserializer::from_read_ref(&data)) {
827 Ok(data) => data,
828 Err(e) => {
829 error!("Could not decode the contents of the player state file: {e}");
830 return Err(e.to_string());
831 }
832 };
833
834 match PlayerState::try_from(state_raw) {
835 Ok(state) => Ok(state),
836 Err(e) => {
837 error!("Could not restore player state from cache: {e}");
838 Err(e.to_string())
839 }
840 }
841 } else {
842 Ok(PlayerState::default())
843 }
844}