Skip to main content

selene_daemon/
playlist.rs

1use std::{
2    collections::{HashSet, VecDeque},
3    fmt::Display,
4    sync::{
5        atomic::{AtomicU8, Ordering},
6        mpsc::Sender,
7    },
8};
9
10use blake3::Hash;
11use lunar_lib::database::{DatabaseEntry, DatabaseError};
12use rand::{rng, seq::SliceRandom};
13use selene_core::{
14    database::LibraryDb,
15    library::{
16        album::Album,
17        artist::{Artist, ArtistId},
18        collection::{Collectable, Collection, CollectionId},
19        track::{Track, TrackId, track_meta::TrackMeta},
20    },
21    media_container::MediaContainer,
22};
23use serde::{Deserialize, Serialize};
24
25use crate::{PlayerEvent, event_handler::EventTx};
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ResolvedTrack {
29    pub track: PlayableTrack,
30    pub position: usize,
31
32    pub album: Option<Album>,
33    pub album_artists: Option<Vec<Artist>>,
34
35    pub artists: Vec<Artist>,
36}
37
38impl ResolvedTrack {
39    #[cfg(feature = "mpris")]
40    pub fn mpris_id(&self) -> mpris_server::TrackId {
41        use mpris_server::zbus::zvariant::ObjectPath;
42
43        ObjectPath::from_string_unchecked(format!(
44            "/org/mpris/MediaPlayer2/TrackList/{}",
45            self.position,
46        ))
47        .into()
48    }
49
50    pub fn from_tracklist(
51        track: PlayableTrack,
52        id: usize,
53        db: &LibraryDb,
54    ) -> Result<Self, DatabaseError> {
55        let album = track.metadata().album(db)?;
56        let album_artists = album
57            .as_ref()
58            .map(|a| a.artists().artists(db))
59            .transpose()?;
60
61        Ok(Self {
62            position: id,
63            album,
64            album_artists,
65            artists: track.metadata().artists(db)?,
66            track,
67        })
68    }
69}
70
71#[derive(Debug)]
72pub struct Playlist {
73    event_tx: Sender<PlayerEvent>,
74
75    pub queue: VecDeque<PlayableTrack>,
76    playlist: Vec<Playable>,
77    tracklist: Vec<PlayableTrack>,
78    tracklist_index: Option<usize>,
79
80    pub shuffle_mode: ShuffleMode,
81    pub loop_mode: LoopMode,
82}
83
84impl Playlist {
85    #[must_use]
86    pub fn new(event_tx: Sender<PlayerEvent>) -> Self {
87        Self {
88            event_tx,
89
90            queue: VecDeque::new(),
91            playlist: Vec::new(),
92            tracklist: Vec::new(),
93            tracklist_index: None,
94
95            shuffle_mode: ShuffleMode::None,
96            loop_mode: LoopMode::None,
97        }
98    }
99
100    /// Clears the playlist and the tracklist
101    pub fn clear(&mut self) {
102        self.playlist.clear();
103        self.tracklist.clear();
104        self.tracklist_index = None;
105        self.event_tx.event(PlayerEvent::TracklistChanged {
106            tracklist: Vec::new(),
107        });
108    }
109
110    pub fn position(&self) -> (usize, usize) {
111        (self.tracklist_index.unwrap_or(0), self.tracklist.len())
112    }
113
114    /// Peeks at the next item
115    ///
116    /// If this function returns none, it means the end of the track list has been reached, and the next consumption will follow the [`LoopMode`]
117    pub fn peek_next(&self, db: &LibraryDb) -> Result<Option<ResolvedTrack>, DatabaseError> {
118        if self.tracklist.is_empty() {
119            return Ok(None);
120        }
121
122        match self.loop_mode {
123            LoopMode::None | LoopMode::LoopAndReshuffle => {
124                let start = self.tracklist_index.map_or(0, |i| i + 1);
125
126                for i in start..self.tracklist.len() {
127                    if self.tracklist[i].can_play() {
128                        let resolved =
129                            ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
130                        return Ok(Some(resolved));
131                    }
132                }
133            }
134            LoopMode::Loop => {
135                let start = self.tracklist_index.map_or(0, |i| i + 1);
136
137                for n in 0..self.tracklist.len() {
138                    let i = (start + n) % self.tracklist.len();
139
140                    if self.tracklist[i].can_play() {
141                        let resolved =
142                            ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
143                        return Ok(Some(resolved));
144                    }
145                }
146            }
147            LoopMode::RepeatTrack => {
148                let Some(i) = self.tracklist_index else {
149                    return Ok(None);
150                };
151                if self.tracklist[i].can_play() {
152                    let resolved = ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
153                    return Ok(Some(resolved));
154                }
155            }
156        }
157        Ok(None)
158    }
159
160    /// Returns the item at the current index
161    pub fn current(&mut self, db: &LibraryDb) -> Result<Option<ResolvedTrack>, DatabaseError> {
162        let Some(i) = self.tracklist_index else {
163            return Ok(None);
164        };
165        let resolved = ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
166        Ok(Some(resolved))
167    }
168
169    /// Moves to the next item and returns it
170    ///
171    /// If this function returns none, it means there is nothing left to play or nothing to play
172    ///
173    /// This function will skip tracks without a container.
174    pub fn pop_next(&mut self, db: &LibraryDb) -> Result<Option<ResolvedTrack>, DatabaseError> {
175        if self.tracklist.is_empty() {
176            return Ok(None);
177        }
178
179        match self.loop_mode {
180            LoopMode::None => {
181                let start = self.tracklist_index.map_or(0, |i| i + 1);
182
183                for i in start..self.tracklist.len() {
184                    if self.tracklist[i].can_play() {
185                        self.tracklist_index = Some(i);
186                        let resolved =
187                            ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
188                        return Ok(Some(resolved));
189                    }
190                }
191            }
192            LoopMode::Loop => {
193                let start = self.tracklist_index.map_or(0, |i| i + 1);
194
195                for n in 0..self.tracklist.len() {
196                    let i = (start + n) % self.tracklist.len();
197
198                    if self.tracklist[i].can_play() {
199                        self.tracklist_index = Some(i);
200                        let resolved =
201                            ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
202                        return Ok(Some(resolved));
203                    }
204                }
205            }
206            LoopMode::LoopAndReshuffle => {
207                let start = self.tracklist_index.map_or(0, |i| i + 1);
208
209                for n in 0..self.tracklist.len() {
210                    let i = (start + n) % self.tracklist.len();
211
212                    if i == 0 && n != 0 {
213                        self.rebuild_tracklist();
214                    }
215
216                    if self.tracklist[i].can_play() {
217                        self.tracklist_index = Some(i);
218                        let resolved =
219                            ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
220                        return Ok(Some(resolved));
221                    }
222                }
223            }
224            LoopMode::RepeatTrack => {
225                let Some(i) = self.tracklist_index else {
226                    return Ok(None);
227                };
228                if self.tracklist[i].can_play() {
229                    let resolved = ResolvedTrack::from_tracklist(self.tracklist[i].clone(), i, db)?;
230                    return Ok(Some(resolved));
231                }
232            }
233        }
234        Ok(None)
235    }
236
237    /// Rebuilds the interior tracklist
238    pub fn rebuild_tracklist(&mut self) {
239        let mut index_map: Vec<usize> = (0..self.playlist.len()).collect();
240        if matches!(
241            self.shuffle_mode,
242            ShuffleMode::CollectionsOnly | ShuffleMode::CollectionsAndTracks
243        ) {
244            index_map.shuffle(&mut rng());
245        }
246
247        let mut tracklist: Vec<_> = index_map
248            .into_iter()
249            .flat_map(|i| self.playlist[i].clone().flatten_shuffle(self.shuffle_mode))
250            .collect();
251
252        if matches!(self.shuffle_mode, ShuffleMode::Full) {
253            tracklist.shuffle(&mut rng());
254        }
255
256        self.tracklist_index = None;
257        self.tracklist = tracklist;
258        self.event_tx.event(PlayerEvent::TracklistChanged {
259            tracklist: self.tracklist().iter().map(|t| t.id()).collect(),
260        });
261    }
262
263    /// If the looping mode is set to `Loop`, this function will modulo the input
264    ///
265    /// If the looping mode is set to `LoopAndReshuffle`, this function will modulo the input and reshuffle the tracklist
266    pub fn tracklist_seek(&mut self, to: isize, increment: bool) -> usize {
267        let to = if increment {
268            self.tracklist_index.unwrap_or(0) as isize + to
269        } else {
270            to
271        };
272
273        let len = self.tracklist.len() as isize;
274
275        match self.loop_mode {
276            LoopMode::None => {
277                self.tracklist_index = Some(to.clamp(0, len) as usize);
278            }
279            LoopMode::Loop => {
280                self.tracklist_index = Some(to.rem_euclid(len) as usize);
281            }
282            LoopMode::LoopAndReshuffle => {
283                if to >= len && !matches!(self.shuffle_mode, ShuffleMode::None) {
284                    self.rebuild_tracklist();
285                }
286                self.tracklist_index = Some(to.rem_euclid(len) as usize);
287            }
288            LoopMode::RepeatTrack => {
289                if !increment {
290                    self.tracklist_index = Some(to.clamp(0, len) as usize);
291                }
292            }
293        }
294        self.tracklist_index.unwrap()
295    }
296
297    /// Gets the current state of the playlist
298    #[must_use]
299    pub fn playlist(&self) -> &[Playable] {
300        &self.playlist
301    }
302
303    /// Sets the current state of the playlist
304    pub fn set_playlist(&mut self, playlist: impl IntoIterator<Item = Playable>) {
305        self.playlist = playlist.into_iter().collect();
306        self.rebuild_tracklist();
307    }
308
309    pub fn queue_extend(&mut self, with: impl IntoIterator<Item = Playable>) {
310        self.queue.extend(
311            with.into_iter()
312                .flat_map(|p| p.flatten_shuffle(ShuffleMode::None)),
313        );
314    }
315
316    #[must_use]
317    pub fn tracklist(&self) -> &[PlayableTrack] {
318        &self.tracklist
319    }
320
321    // Returns the hash of the queue
322    #[must_use]
323    pub fn queue_hash(&self) -> Hash {
324        let mut hash_seed = Vec::with_capacity(self.queue.len() * 32);
325        for playable in &self.queue {
326            hash_seed.extend(*playable.track);
327        }
328        blake3::hash(&hash_seed)
329    }
330
331    // Returns the hash of the playlist
332    #[must_use]
333    pub fn playlist_hash(&self) -> Hash {
334        let mut hash_seed = Vec::with_capacity(self.playlist.len() * 32);
335        for playable in &self.playlist {
336            hash_seed.extend(playable.id_as_bytes());
337        }
338        blake3::hash(&hash_seed)
339    }
340}
341
342impl Extend<Playable> for Playlist {
343    fn extend<T: IntoIterator<Item = Playable>>(&mut self, iter: T) {
344        let mut vec: Vec<_> = iter.into_iter().collect();
345        if matches!(
346            self.shuffle_mode,
347            ShuffleMode::CollectionsOnly | ShuffleMode::CollectionsAndTracks
348        ) {
349            vec.shuffle(&mut rng());
350        }
351
352        self.tracklist.extend(
353            vec.iter()
354                .cloned()
355                .flat_map(|p| p.flatten_shuffle(self.shuffle_mode)),
356        );
357        self.event_tx.event(PlayerEvent::TracklistChanged {
358            tracklist: self.tracklist().iter().map(|t| t.id()).collect(),
359        });
360        self.playlist.extend(vec);
361    }
362}
363
364#[derive(Debug, Serialize, Deserialize, Clone)]
365pub struct PlayableTrack {
366    container: Option<MediaContainer>,
367    metadata: TrackMeta,
368    track: TrackId,
369}
370
371impl PartialEq for PlayableTrack {
372    fn eq(&self, other: &Self) -> bool {
373        self.track == other.track
374    }
375}
376
377impl Eq for PlayableTrack {}
378
379impl PlayableTrack {
380    #[must_use]
381    pub fn container(&self) -> Option<&MediaContainer> {
382        self.container.as_ref()
383    }
384
385    #[must_use]
386    pub fn can_play(&self) -> bool {
387        self.container.is_some()
388    }
389
390    #[must_use]
391    pub fn metadata(&self) -> &TrackMeta {
392        &self.metadata
393    }
394
395    #[must_use]
396    pub fn id(&self) -> TrackId {
397        self.track
398    }
399
400    #[must_use]
401    pub fn from_track(track: &Track, fallback: bool) -> Self {
402        Self {
403            container: if fallback {
404                Some(
405                    track
406                        .lib_container()
407                        .unwrap_or(track.src_container())
408                        .clone(),
409                )
410            } else {
411                track.lib_container().cloned()
412            },
413            metadata: track.metadata.clone(),
414            track: track.id(),
415        }
416    }
417}
418
419#[derive(Debug, Serialize, Deserialize, Clone)]
420pub struct PlayableAlbum {
421    album: Album,
422    tracks: Vec<PlayableTrack>,
423}
424impl PlayableAlbum {
425    fn from_album(album: Album, db: &LibraryDb) -> Result<PlayableAlbum, DatabaseError> {
426        let tracks = album
427            .tracks(db)?
428            .iter()
429            .map(|t| PlayableTrack::from_track(t, false))
430            .collect();
431
432        Ok(Self { album, tracks })
433    }
434}
435
436#[derive(Debug, Serialize, Deserialize, Clone)]
437pub enum Playable {
438    Track {
439        track: Box<PlayableTrack>,
440    },
441    Album {
442        album: Box<PlayableAlbum>,
443    },
444    Artist {
445        artist: ArtistId,
446        tracks: Vec<PlayableTrack>,
447        albums: Vec<PlayableAlbum>,
448    },
449    Collection {
450        collection: CollectionId,
451        playables: Vec<Playable>,
452    },
453}
454
455impl Playable {
456    #[must_use]
457    pub fn flatten_shuffle(self, shuffle_mode: ShuffleMode) -> Vec<PlayableTrack> {
458        let mut buf = Vec::new();
459        let mut stack = vec![self];
460
461        while let Some(last) = stack.pop() {
462            match last {
463                Playable::Track { track } => buf.push(*track),
464                Playable::Album { mut album, .. } => {
465                    if matches!(
466                        shuffle_mode,
467                        ShuffleMode::TracksOnly | ShuffleMode::CollectionsAndTracks
468                    ) {
469                        album.tracks.shuffle(&mut rng());
470                    }
471
472                    buf.extend(album.tracks);
473                }
474                Playable::Artist {
475                    mut tracks,
476                    mut albums,
477                    ..
478                } => {
479                    if matches!(
480                        shuffle_mode,
481                        ShuffleMode::Full | ShuffleMode::CollectionsAndTracks
482                    ) {
483                        albums.shuffle(&mut rng());
484                    }
485
486                    buf.extend(albums.into_iter().flat_map(|mut a| {
487                        if matches!(
488                            shuffle_mode,
489                            ShuffleMode::TracksOnly | ShuffleMode::CollectionsAndTracks
490                        ) {
491                            a.tracks.shuffle(&mut rng());
492                        }
493
494                        a.tracks
495                    }));
496
497                    if matches!(
498                        shuffle_mode,
499                        ShuffleMode::TracksOnly | ShuffleMode::CollectionsAndTracks
500                    ) {
501                        tracks.shuffle(&mut rng());
502                    }
503
504                    buf.extend(tracks);
505                }
506                Playable::Collection { mut playables, .. } => {
507                    if matches!(
508                        shuffle_mode,
509                        ShuffleMode::Full | ShuffleMode::CollectionsAndTracks
510                    ) {
511                        playables.shuffle(&mut rng());
512                    }
513
514                    stack.extend(playables);
515                }
516            }
517        }
518
519        if matches!(shuffle_mode, ShuffleMode::Full) {
520            buf.shuffle(&mut rng());
521        }
522
523        buf
524    }
525
526    #[must_use]
527    pub fn flatten(self) -> Vec<PlayableTrack> {
528        let mut buf = Vec::new();
529        let mut stack = vec![self];
530
531        while let Some(last) = stack.pop() {
532            match last {
533                Playable::Track { track } => buf.push(*track),
534                Playable::Album { album } => buf.extend(album.tracks),
535                Playable::Artist { tracks, albums, .. } => {
536                    buf.extend(albums.into_iter().flat_map(|a| a.tracks));
537                    buf.extend(tracks);
538                }
539                Playable::Collection { playables, .. } => stack.extend(playables),
540            }
541        }
542
543        buf
544    }
545
546    pub fn from_collectable(
547        collectable: Collectable,
548        db: &LibraryDb,
549    ) -> Result<Playable, DatabaseError> {
550        let playable = match collectable {
551            Collectable::Track(track_id) => {
552                let track = Track::db_get_from(track_id, db)?.ok_or(DatabaseError::MissingEntry)?;
553                Playable::Track {
554                    track: Box::new(PlayableTrack::from_track(&track, false)),
555                }
556            }
557            Collectable::Artist(artist_id) => {
558                let artist =
559                    Artist::db_get_from(artist_id, db)?.ok_or(DatabaseError::MissingEntry)?;
560
561                let tracks = artist
562                    .all_tracks(db)?
563                    .iter()
564                    .map(|t| PlayableTrack::from_track(t, false))
565                    .collect();
566
567                let albums: Vec<PlayableAlbum> = artist
568                    .albums(db)?
569                    .into_iter()
570                    .map(|a| PlayableAlbum::from_album(a, db))
571                    .collect::<Result<_, _>>()?;
572
573                Playable::Artist {
574                    artist: artist_id,
575                    tracks,
576                    albums,
577                }
578            }
579            Collectable::Album(album_id) => {
580                let album = Album::db_get_from(album_id, db)?.ok_or(DatabaseError::MissingEntry)?;
581
582                Playable::Album {
583                    album: Box::new(PlayableAlbum::from_album(album, db)?),
584                }
585            }
586            Collectable::Collection(collection_id) => {
587                struct Frame<I: Iterator<Item = Collectable>> {
588                    collection_id: CollectionId,
589                    remaining: I,
590                    playables: Vec<Playable>,
591                    ancestors: HashSet<CollectionId>,
592                }
593
594                let root = Collection::db_get_from(collection_id, db)?
595                    .ok_or(DatabaseError::MissingEntry)?;
596                let mut frames = vec![Frame {
597                    collection_id,
598                    remaining: root.collectables(db)?.into_iter(),
599                    playables: Vec::new(),
600                    ancestors: HashSet::from([collection_id]),
601                }];
602
603                loop {
604                    let frame = frames.last_mut().unwrap();
605                    if let Some(item) = frame.remaining.next() {
606                        match item {
607                            Collectable::Collection(inner_id) => {
608                                assert!(
609                                    !frame.ancestors.contains(&inner_id),
610                                    "Invalid collection: Cyclical reference"
611                                );
612
613                                let mut child_ancestors = frame.ancestors.clone();
614                                child_ancestors.insert(inner_id);
615
616                                let inner = Collection::db_get_from(inner_id, db)?
617                                    .ok_or(DatabaseError::MissingEntry)?;
618                                frames.push(Frame {
619                                    collection_id: inner_id,
620                                    remaining: inner.collectables(db)?.into_iter(),
621                                    playables: Vec::new(),
622                                    ancestors: child_ancestors,
623                                });
624                            }
625                            other => {
626                                frame.playables.push(Playable::from_collectable(other, db)?);
627                            }
628                        }
629                    } else {
630                        let completed = frames.pop().unwrap();
631                        let result = Playable::Collection {
632                            collection: completed.collection_id,
633                            playables: completed.playables,
634                        };
635                        match frames.last_mut() {
636                            Some(parent) => parent.playables.push(result),
637                            None => return Ok(result),
638                        }
639                    }
640                }
641            }
642        };
643        Ok(playable)
644    }
645
646    #[must_use]
647    pub fn to_collectable(&self) -> Collectable {
648        match self {
649            Playable::Track { track } => Collectable::Track(track.id()),
650            Playable::Album { album } => Collectable::Album(album.album.id()),
651            Playable::Artist { artist, .. } => Collectable::Artist(*artist),
652            Playable::Collection { collection, .. } => Collectable::Collection(*collection),
653        }
654    }
655
656    fn id_as_bytes(&self) -> [u8; 32] {
657        match self {
658            Playable::Track { track } => *track.track,
659            Playable::Album { album, .. } => *album.album.id(),
660            Playable::Artist { artist, .. } => **artist,
661            Playable::Collection { collection, .. } => **collection,
662        }
663    }
664
665    pub fn display(&self) -> Result<String, DatabaseError> {
666        match self {
667            Playable::Track { track } => Ok(format!("{} (Track)", track.metadata.safe_title())),
668            Playable::Album { album, .. } => Ok(format!("{} (Album)", album.album.name())),
669            Playable::Artist { artist, .. } => {
670                let artist = Artist::db_get(*artist)?.ok_or(DatabaseError::MissingEntry)?;
671                Ok(format!("{} (Artist)", artist.name()))
672            }
673            Playable::Collection { collection, .. } => {
674                let collection =
675                    Collection::db_get(*collection)?.ok_or(DatabaseError::MissingEntry)?;
676                Ok(format!("{} (Collection)", collection.name))
677            }
678        }
679    }
680}
681
682#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
683#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
684pub enum ShuffleMode {
685    /// Tracks are not shuffled, they are played as is
686    None,
687
688    /// Only collections are shuffled, meaning the collections are shuffled, but not the contents
689    CollectionsOnly,
690
691    /// Randomize tracks only, meaning the contents of collections are shuffled, but not the collections themselves
692    TracksOnly,
693
694    /// Randomize collection order and the tracks inside the collection
695    CollectionsAndTracks,
696
697    /// Randomizes the order of everything
698    Full,
699}
700
701impl Display for ShuffleMode {
702    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
703        match self {
704            ShuffleMode::None => f.write_str("None"),
705            ShuffleMode::CollectionsOnly => f.write_str("Collections Only"),
706            ShuffleMode::TracksOnly => f.write_str("Tracks Only"),
707            ShuffleMode::CollectionsAndTracks => f.write_str("Collections And Tracks"),
708            ShuffleMode::Full => f.write_str("Full"),
709        }
710    }
711}
712
713#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
714#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
715pub enum LoopMode {
716    /// Disables looping
717    None,
718
719    /// When the end of the tracklist is reached, loop back to the beginning
720    Loop,
721
722    /// When the end of the tracklist is reached, reshuffle the tracklist using the shuffle mode and loop
723    LoopAndReshuffle,
724    RepeatTrack,
725}
726
727impl Display for LoopMode {
728    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
729        match self {
730            LoopMode::None => f.write_str("None"),
731            LoopMode::Loop => f.write_str("Loop"),
732            LoopMode::LoopAndReshuffle => f.write_str("Loop And Reshuffle"),
733            LoopMode::RepeatTrack => f.write_str("Repeat Track"),
734        }
735    }
736}
737
738#[repr(u8)]
739#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
740pub enum PlaybackStatus {
741    Playing,
742    Paused,
743    Stopped,
744}
745
746impl Display for PlaybackStatus {
747    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
748        match self {
749            PlaybackStatus::Playing => f.write_str("Playing"),
750            PlaybackStatus::Paused => f.write_str("Paused"),
751            PlaybackStatus::Stopped => f.write_str("Stopped"),
752        }
753    }
754}
755
756pub struct AtomicPlaybackStatus(AtomicU8);
757
758impl AtomicPlaybackStatus {
759    #[must_use]
760    pub fn new(state: PlaybackStatus) -> Self {
761        Self(AtomicU8::new(state as u8))
762    }
763
764    pub fn load(&self, order: Ordering) -> PlaybackStatus {
765        match self.0.load(order) {
766            0 => PlaybackStatus::Playing,
767            1 => PlaybackStatus::Paused,
768            2 => PlaybackStatus::Stopped,
769            _ => unreachable!(),
770        }
771    }
772
773    pub fn store(&self, state: PlaybackStatus, order: Ordering) {
774        self.0.store(state as u8, order);
775    }
776}