Skip to main content

chilen_backend/playback/
state.rs

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