selene-daemon 0.9.0-alpha.2

Official music player daemon for Selene
Documentation
use std::{io, sync::Arc};

use crate::{LoopMode, ShuffleMode, dto};
use bitflags::bitflags;
use lunar_lib::id::Id;
use selene_core::library::{collectable::Collectable, track::Track};
use serde::{Deserialize, Serialize};

// ######
// COMMON
// ######

pub(crate) trait Packetable: Serialize + for<'a> Deserialize<'a> {
    fn serialize_packet(&self) -> Vec<u8> {
        let data = vec![0u8; 4];
        let mut data = postcard::to_extend(&self, data).expect("Serialization should never fail");
        let payload_len = (data.len() as u32 - 4).to_be_bytes();
        data[0..4].copy_from_slice(&payload_len);
        data
    }

    fn serialize_into_arc(&self) -> Arc<[u8]> {
        Arc::from(self.serialize_packet())
    }

    fn serialize_into_writer<W: io::Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
        let data = self.serialize_packet();
        writer.write_all(&data)
    }

    fn deserialize_packet(bytes: &[u8]) -> io::Result<Self> {
        postcard::from_bytes(bytes).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
    }
}

impl<T: Serialize + for<'a> Deserialize<'a>> Packetable for T {}

bitflags! {
    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
    pub struct PlayerQueryFlags: u16 {
        const PLAYBACK_STATE        = 1 << 0;

        const CURRENTLY_PLAYING     = 1 << 1;

        const TIME                  = 1 << 2;

        const QUEUE                 = 1 << 3;

        const PLAYLIST              = 1 << 4;

        const TRACKLIST             = 1 << 5;
        const TRACKLIST_POSITION    = 1 << 6;

        const SHUFFLE_MODE          = 1 << 7;
        const LOOP_MODE             = 1 << 8;
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueryResult {
    pub playback_state: Option<bool>,

    pub currently_playing: Option<Option<(Id<Track>, Option<u32>)>>,

    pub time: Option<Option<f64>>,

    pub queue: Option<Vec<Id<Track>>>,

    pub playlist: Option<Vec<Collectable>>,

    pub tracklist: Option<Vec<Id<Track>>>,
    pub tracklist_position: Option<Option<u32>>,

    pub shuffle_mode: Option<ShuffleMode>,
    pub loop_mode: Option<LoopMode>,
}

// ######
// DAEMON
// ######

#[derive(Debug, Serialize, Deserialize)]
pub enum RemoteEvent {
    CurrentlyPlayingChanged {
        currently_playing: Box<dto::Track>,
        position: Option<u32>,
    },
    PlaybackIsPlayingChanged {
        is_playing: bool,
        changed_at: f64,
    },
    PlaybackStopped,

    ShuffleModeChanged {
        shuffle_mode: ShuffleMode,
    },
    LoopModeChanged {
        loop_mode: LoopMode,
    },

    VolumeChanged {
        volume: f32,
    },
    SeekOccured {
        time: Option<f64>,
    },

    QueueChanged {
        length: u32,
    },
    PlaylistChanged {
        length: u32,
    },
    TracklistChanged {
        length: u32,
        position: Option<usize>,
    },

    Shutdown,
    Scrobble {
        start_time: u64,
    },
    NowPlaying,
}

// ############
// COMMON AUDIO
// ############

#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub struct TrackInfo {
    pub sample_rate: u32,
    pub channels: u16,
    pub track_gain: Option<f32>,
    pub album_gain: Option<f32>,
}

impl TrackInfo {
    pub fn new(
        sample_rate: u32,
        channels: u16,
        track_gain: Option<f32>,
        album_gain: Option<f32>,
    ) -> Self {
        Self {
            sample_rate,
            channels,
            track_gain,
            album_gain,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) enum AudioPacket {
    TrackInfo(TrackInfo),
    Audio(Vec<f32>),
    Disconnect,
}