selene-daemon 0.9.0-alpha.2

Official music player daemon for Selene
Documentation
use std::{
    collections::VecDeque,
    ops::Deref,
    sync::{Arc, atomic::AtomicBool},
};

use lunar_lib::{
    database::{Db, Entry, TransactionError, caching::IdCacheIterExt},
    id::Id,
    iterator_ext::IteratorExtensions,
};
use rand::{rngs::StdRng, seq::SliceRandom};
use selene_core::{
    database::Library,
    library::{album::Album, artist::Artist, collectable::Collectable, track::Track},
};

mod playlist_rng;
pub(crate) use playlist_rng::*;

use crate::{LoopMode, ShuffleMode};

mod core_impls;

#[derive(Clone)]
pub(crate) struct PlayingTrack {
    pub source: Arc<Entry<Track>>,
    pub position: Option<u32>,
}

impl PlayingTrack {
    fn new(track: Arc<Entry<Track>>, position: Option<u32>) -> Self {
        Self {
            source: track,
            position,
        }
    }

    fn from_queue(track: Arc<Entry<Track>>) -> Self {
        Self::new(track, None)
    }

    fn from_tracklist(track: Arc<Entry<Track>>, id: u32) -> Self {
        Self::new(track, Some(id))
    }
}

impl Deref for PlayingTrack {
    type Target = Track;

    fn deref(&self) -> &Self::Target {
        &self.source
    }
}

pub(crate) struct Playlist {
    rng: PlaylistRng,

    queue: VecDeque<Arc<Entry<Track>>>,
    playlist: Vec<Playable>,

    tracklist: Vec<Arc<Entry<Track>>>,
    tracklist_index: Option<u32>,

    shuffle_mode: ShuffleMode,
    loop_mode: LoopMode,
    looping: Arc<AtomicBool>,
}

impl Playlist {
    /// Clears the playlist and the tracklist
    pub(crate) fn clear(&mut self) {
        self.playlist.clear();
        self.tracklist.clear();
        self.tracklist_index = None;
    }

    // Rebuilds the tracklist with new rng, moving whatever was currently playing to the front
    fn rebuild_tracklist(&mut self) {
        self.rng = PlaylistRng::new();
        let mut rng = self.rng.current_rng();

        let mut tracklist = self.build_tracklist(&mut rng);
        if let Some(current) = self
            .tracklist_index
            .and_then(|i| self.tracklist.get(i as usize))
            && let Some(new_pos) = tracklist.iter().position(|t| Arc::ptr_eq(t, current))
        {
            tracklist.swap(0, new_pos);
            self.tracklist_index = Some(0);
        } else {
            self.tracklist_index = None;
        }

        self.tracklist = tracklist;
    }

    pub fn reshuffle_tracklist(&mut self) {
        self.rng = PlaylistRng::new();
        let mut rng = self.rng.current_rng();
        let mut tracklist = self.build_tracklist(&mut rng);
        if let Some(current) = self
            .tracklist_index
            .and_then(|i| self.tracklist.get(i as usize))
            && let Some(new_pos) = tracklist.iter().position(|t| Arc::ptr_eq(t, current))
        {
            tracklist.swap(0, new_pos);
            self.tracklist_index = Some(0);
        } else {
            self.tracklist_index = None;
        }
    }

    fn shuffle_tracklist(&mut self, rng: &mut StdRng) {
        self.tracklist = self.build_tracklist(rng);
    }

    fn build_tracklist(&self, rng: &mut StdRng) -> Vec<Arc<Entry<Track>>> {
        let mut tracklist = self
            .playlist
            .iter()
            .flat_map(|p| p.flatten_shuffle(self.shuffle_mode, rng))
            .to_vec();

        if matches!(self.shuffle_mode, ShuffleMode::Full) {
            tracklist.shuffle(rng);
        }

        tracklist
    }
}

#[derive(Clone)]
pub(crate) struct PlayableAlbum {
    pub album: Arc<Entry<Album>>,
    pub tracks: Vec<Arc<Entry<Track>>>,
}
impl PlayableAlbum {
    fn from_album(
        album: Arc<Entry<Album>>,
        db: &Db<Library>,
    ) -> Result<PlayableAlbum, TransactionError> {
        let tracks = album.tracks().cache_get(db)?;
        Ok(Self { album, tracks })
    }
}

#[derive(Clone)]
pub(crate) enum Playable {
    Track {
        track: Arc<Entry<Track>>,
    },
    Album {
        album: PlayableAlbum,
    },
    Artist {
        artist: Id<Artist>,
        singles: Vec<Arc<Entry<Track>>>,
        albums: Vec<PlayableAlbum>,
    },
    // Collection {
    //     items: CollectionItems,
    //     playables: Vec<Playable>,
    // },
}

impl Playable {
    #[must_use]
    pub(crate) fn flatten(&self) -> Vec<Arc<Entry<Track>>> {
        let mut buf: Vec<Arc<Entry<Track>>> = Vec::new();
        let mut stack = vec![self];

        while let Some(last) = stack.pop() {
            match last {
                Playable::Track { track } => buf.push(track.clone()),
                Playable::Album { album } => buf.extend(album.tracks.iter().map(Arc::clone)),
                Playable::Artist {
                    singles: tracks,
                    albums,
                    ..
                } => {
                    buf.extend(albums.iter().flat_map(|a| a.tracks.iter().map(Arc::clone)));
                    buf.extend(tracks.iter().map(Arc::clone));
                } // Playable::Collection { playables, .. } => stack.extend(playables),
            }
        }

        buf
    }

    #[must_use]
    pub(crate) fn flatten_shuffle(
        &self,
        shuffle_mode: ShuffleMode,
        rng: &mut StdRng,
    ) -> Vec<Arc<Entry<Track>>> {
        let mut buf = Vec::new();
        let mut stack = vec![self];

        while let Some(last) = stack.pop() {
            match last {
                Playable::Track { track } => buf.push(track.clone()),
                Playable::Album { album, .. } => {
                    let mut album = album.tracks.iter().map(Arc::clone).collect::<Vec<_>>();
                    if matches!(
                        shuffle_mode,
                        ShuffleMode::TracksOnly | ShuffleMode::CollectionsAndTracks
                    ) {
                        album.shuffle(rng);
                    }

                    buf.extend(album);
                }
                Playable::Artist {
                    singles: tracks,
                    albums,
                    ..
                } => {
                    let mut items: Vec<_> = albums
                        .iter()
                        .map(|a| a.tracks.iter().map(Arc::clone).collect::<Vec<_>>())
                        .chain(tracks.iter().map(|t| vec![t.clone()]))
                        .collect();

                    if matches!(
                        shuffle_mode,
                        ShuffleMode::Full | ShuffleMode::CollectionsAndTracks
                    ) {
                        items.shuffle(rng);
                    }

                    buf.extend(items.into_iter().flat_map(|mut tracks| {
                        if matches!(
                            shuffle_mode,
                            ShuffleMode::TracksOnly | ShuffleMode::CollectionsAndTracks
                        ) {
                            tracks.shuffle(rng);
                        }

                        tracks
                    }));
                } // Playable::Collection { playables, .. } => {
                  //     let mut playables = playables.iter().collect::<Vec<_>>();

                  //     if matches!(
                  //         shuffle_mode,
                  //         ShuffleMode::Full | ShuffleMode::CollectionsAndTracks
                  //     ) {
                  //         playables.shuffle(rng);
                  //     }

                  //     stack.extend(playables.into_iter().rev());
                  // }
            }
        }

        buf
    }

    pub(crate) fn from_collectable(
        collectable: Collectable,
        db: &Db<Library>,
    ) -> Result<Playable, TransactionError> {
        let playable = match collectable {
            Collectable::Track(track_id) => {
                let track = track_id
                    .cache_get(db)?
                    .ok_or(TransactionError::MissingEntry)?;
                Playable::Track { track }
            }
            Collectable::Artist(artist_id) => {
                let artist = artist_id
                    .cache_get(db)?
                    .ok_or(TransactionError::MissingEntry)?;

                let mut singles = artist.tracks().cache_get(db)?;
                singles.retain(|t| t.is_single());

                let albums: Vec<PlayableAlbum> = artist
                    .albums()
                    .cache_get(db)?
                    .into_iter()
                    .map(|a| PlayableAlbum::from_album(a, db))
                    .collect::<Result<_, _>>()?;

                Playable::Artist {
                    artist: artist_id,
                    singles,
                    albums,
                }
            }
            Collectable::Album(album_id) => {
                let album = album_id
                    .cache_get(db)?
                    .ok_or(TransactionError::MissingEntry)?;

                Playable::Album {
                    album: PlayableAlbum::from_album(album, db)?,
                }
            } // Collectable::Collection(collection) => {
              //     todo!()

              // struct Frame<I: Iterator<Item = Collectable>> {
              //     collection_id: Id<Collection>,
              //     remaining: I,
              //     playables: Vec<Playable>,
              //     ancestors: HashSet<Id<Collection>>,
              // }

              // let root = collection
              //     .db_get(db)?
              //     .ok_or(TransactionError::MissingEntry)?;
              // let mut frames = vec![Frame {
              //     collection,
              //     remaining: root.collectables(db)?.into_iter(),
              //     playables: Vec::new(),
              //     ancestors: HashSet::from([collection]),
              // }];

              // loop {
              //     let frame = frames.last_mut().unwrap();
              //     if let Some(item) = frame.remaining.next() {
              //         match item {
              //             Collectable::Collection(inner_id) => {
              //                 assert!(
              //                     !frame.ancestors.contains(&inner_id),
              //                     "Invalid collection: Cyclical reference"
              //                 );

              //                 let mut child_ancestors = frame.ancestors.clone();
              //                 child_ancestors.insert(inner_id);

              //                 let inner = inner_id
              //                     .cache_get(db)?
              //                     .ok_or(TransactionError::MissingEntry)?;

              //                 frames.push(Frame {
              //                     collection_id: inner_id,
              //                     remaining: inner.collectables(db)?.into_iter(),
              //                     playables: Vec::new(),
              //                     ancestors: child_ancestors,
              //                 });
              //             }
              //             other => {
              //                 frame.playables.push(Playable::from_collectable(other, db)?);
              //             }
              //         }
              //     } else {
              //         let completed = frames.pop().unwrap();
              //         let result = Playable::Collection {
              //             collection: completed.collection_id,
              //             playables: completed.playables,
              //         };
              //         match frames.last_mut() {
              //             Some(parent) => parent.playables.push(result),
              //             None => return Ok(result),
              //         }
              //     }
              // }
              // }
        };
        Ok(playable)
    }

    #[must_use]
    pub(crate) fn to_collectable(&self) -> Collectable {
        match self {
            Playable::Track { track } => Collectable::Track(track.id()),
            Playable::Album { album } => Collectable::Album(album.album.id()),
            Playable::Artist { artist, .. } => Collectable::Artist(*artist),
            // Playable::Collection { collection, .. } => Collectable::Collection(*collection),
        }
    }
}