selene-daemon 0.9.0-alpha.2

Official music player daemon for Selene
Documentation
use std::sync::atomic::Ordering;

use lunar_lib::{
    database::{Db, TransactionError},
    id::Id,
    iterator_ext::IteratorExtensions,
};
use selene_core::{database::Library, library::collectable::Collectable};

use selene_plugin_sdk::{self as sdk, define_stable_logger};

use crate::{
    LoopMode, ShuffleMode,
    decoder::DecoderCommandExt,
    library_db,
    local_session::{LocalSessionEventExt, LocalSessionHandle},
    playlist::Playable,
    session::SessionRunnerExt,
};

pub trait PluginCollectableExt {
    fn into_collectable(self) -> Collectable;
    fn into_playable(self, db: &Db<Library>) -> Result<Playable, TransactionError>;
}

impl PluginCollectableExt for sdk::local_session::Collectable {
    fn into_collectable(self) -> Collectable {
        self.match_owned(
            |id| Collectable::Track(Id::from(id)),
            |id| Collectable::Artist(Id::from(id)),
            |id| Collectable::Album(Id::from(id)),
        )
    }

    fn into_playable(self, db: &Db<Library>) -> Result<Playable, TransactionError> {
        Playable::from_collectable(self.into_collectable(), db)
    }
}

impl From<sdk::local_session::ShuffleMode> for ShuffleMode {
    fn from(value: sdk::local_session::ShuffleMode) -> Self {
        match value {
            sdk::local_session::ShuffleMode::None => ShuffleMode::None,
            sdk::local_session::ShuffleMode::CollectionsOnly => ShuffleMode::CollectionsOnly,
            sdk::local_session::ShuffleMode::TracksOnly => ShuffleMode::TracksOnly,
            sdk::local_session::ShuffleMode::CollectionsAndTracks => {
                ShuffleMode::CollectionsAndTracks
            }
            sdk::local_session::ShuffleMode::Full => ShuffleMode::Full,
        }
    }
}

impl From<ShuffleMode> for sdk::local_session::ShuffleMode {
    fn from(value: ShuffleMode) -> Self {
        match value {
            ShuffleMode::None => sdk::local_session::ShuffleMode::None,
            ShuffleMode::CollectionsOnly => sdk::local_session::ShuffleMode::CollectionsOnly,
            ShuffleMode::TracksOnly => sdk::local_session::ShuffleMode::TracksOnly,
            ShuffleMode::CollectionsAndTracks => {
                sdk::local_session::ShuffleMode::CollectionsAndTracks
            }
            ShuffleMode::Full => sdk::local_session::ShuffleMode::Full,
        }
    }
}

impl From<sdk::local_session::LoopMode> for LoopMode {
    fn from(value: sdk::local_session::LoopMode) -> Self {
        match value {
            sdk::local_session::LoopMode::None => LoopMode::None,
            sdk::local_session::LoopMode::Loop => LoopMode::Loop,
            sdk::local_session::LoopMode::LoopAndReshuffle => LoopMode::LoopAndReshuffle,
            sdk::local_session::LoopMode::RepeatTrack => LoopMode::RepeatTrack,
        }
    }
}

impl From<LoopMode> for sdk::local_session::LoopMode {
    fn from(value: LoopMode) -> Self {
        match value {
            LoopMode::None => sdk::local_session::LoopMode::None,
            LoopMode::Loop => sdk::local_session::LoopMode::Loop,
            LoopMode::LoopAndReshuffle => sdk::local_session::LoopMode::LoopAndReshuffle,
            LoopMode::RepeatTrack => sdk::local_session::LoopMode::RepeatTrack,
        }
    }
}

impl sdk::local_session::StableLocalSessionHandle for LocalSessionHandle {
    extern "C" fn play(&self, collectables: sdk::Vec<sdk::local_session::Collectable>) {
        let playables = {
            collectables
                .into_iter()
                .flat_map(|p| p.into_playable(library_db()))
                .collect()
        };

        self.session_runner.play(playables).unwrap().unwrap();
    }

    extern "C" fn stop(&self) {
        self.session_runner.stop().unwrap().unwrap();
    }

    extern "C" fn set_is_playing(&self, is_playing: bool) -> bool {
        self.session_runner
            .set_is_playing(is_playing)
            .unwrap()
            .unwrap()
    }

    extern "C" fn toggle_is_playing(&self) -> bool {
        self.session_runner.toggle_playing().unwrap().unwrap()
    }

    extern "C" fn previous(&self) -> sdk::Option<u32> {
        self.session_runner.previous().unwrap().unwrap().into()
    }

    extern "C" fn next(&self) -> sdk::Option<u32> {
        self.session_runner.next().unwrap().unwrap().into()
    }

    extern "C" fn seek(&self, seconds: f64, increment: bool) -> sdk::Option<f64> {
        self.session_runner
            .seek(seconds, increment)
            .unwrap()
            .unwrap()
            .into()
    }

    extern "C" fn get_time(&self) -> selene_plugin_sdk::Option<f64> {
        self.decoder_runner.get_time().unwrap().into()
    }

    extern "C" fn playlist_set_shuffle_mode(&self, shuffle_mode: sdk::local_session::ShuffleMode) {
        self.session_runner
            .set_shuffle_mode(shuffle_mode.into())
            .unwrap()
            .unwrap();
    }

    extern "C" fn playlist_get_shuffle_mode(
        &self,
    ) -> selene_plugin_sdk::local_session::ShuffleMode {
        self.session_runner.get_shuffle_mode().unwrap().into()
    }

    extern "C" fn playlist_set_loop_mode(&self, loop_mode: sdk::local_session::LoopMode) {
        self.session_runner.set_loop_mode(loop_mode.into()).unwrap();
    }

    extern "C" fn playlist_get_loop_mode(&self) -> selene_plugin_sdk::local_session::LoopMode {
        self.session_runner.get_loop_mode().unwrap().into()
    }

    extern "C" fn playlist_set(&self, collectables: sdk::Vec<sdk::local_session::Collectable>) {
        let db = library_db();
        self.session_runner
            .playlist_set(
                collectables
                    .into_iter()
                    .map(|c| sdk::local_session::Collectable::into_playable(c, db))
                    .try_to_vec()
                    .expect("Plugins should return valid IDs"),
            )
            .unwrap()
            .unwrap();
    }

    extern "C" fn playlist_extend(&self, collectables: sdk::Vec<sdk::local_session::Collectable>) {
        let db = library_db();
        self.session_runner
            .playlist_extend(
                collectables
                    .into_iter()
                    .map(|c| sdk::local_session::Collectable::into_playable(c, db))
                    .try_to_vec()
                    .expect("Plugins should return valid IDs"),
            )
            .unwrap()
            .unwrap();
    }

    extern "C" fn playlist_clear(&self) {
        self.session_runner.playlist_clear().unwrap().unwrap();
    }

    extern "C" fn queue_set(&self, collectables: sdk::Vec<sdk::local_session::Collectable>) {
        let db = library_db();
        self.session_runner
            .queue_set(
                collectables
                    .into_iter()
                    .map(|c| sdk::local_session::Collectable::into_playable(c, db))
                    .try_to_vec()
                    .expect("Plugins should return valid IDs"),
            )
            .unwrap()
            .unwrap();
    }

    extern "C" fn queue_extend(&self, collectables: sdk::Vec<sdk::local_session::Collectable>) {
        let db = library_db();
        self.session_runner
            .queue_extend(
                collectables
                    .into_iter()
                    .map(|c| sdk::local_session::Collectable::into_playable(c, db))
                    .try_to_vec()
                    .expect("Plugins should return valid IDs"),
            )
            .unwrap()
            .unwrap();
    }

    extern "C" fn queue_clear(&self) {
        self.session_runner.queue_clear().unwrap().unwrap();
    }

    extern "C" fn queue_shuffle(&self) {
        self.session_runner.queue_shuffle().unwrap().unwrap();
    }

    extern "C" fn tracklist_seek(&self, index: i32, increment: bool) -> sdk::Option<u32> {
        self.session_runner
            .tracklist_seek(index, increment)
            .unwrap()
            .unwrap()
            .into()
    }

    extern "C" fn volume_set(&self, volume: f32) -> f32 {
        let volume = volume.clamp(0.0, 1.0);
        self.volume.store(volume.to_bits(), Ordering::Relaxed);
        self.session.event_runner.volume_changed(volume).unwrap();
        volume
    }

    extern "C" fn volume_get(&self) -> f32 {
        f32::from_bits(self.volume.load(Ordering::Relaxed))
    }
}

define_stable_logger!();