selene-core 0.5.1

selene-core is the backend for Selene, a local-first music player
Documentation
use std::{
    path::PathBuf,
    sync::{LazyLock, RwLock, RwLockReadGuard, RwLockWriteGuard},
};

use lunar_lib::{config::Config, error};
use serde::{Deserialize, Serialize};

mod encoding;
pub use encoding::*;

static COMMON_CONFIG: LazyLock<RwLock<CommonConfig>> = LazyLock::new(|| {
    let config = match CommonConfig::load() {
        Ok(config) => config,
        Err(err) => {
            error!(
                "Could not load common config from file, assuming defaults. Reason: {:?}",
                err
            );
            CommonConfig::default()
        }
    };
    RwLock::new(config)
});

/// Returns a read-only guard to the common config.
///
/// # Panics
///
/// This function will panic if the internal read/write lock is poisoned
pub fn common_config() -> RwLockReadGuard<'static, CommonConfig> {
    COMMON_CONFIG.read().unwrap()
}

/// Returns a mutable guard to the common config.
///
/// # Panics
///
/// This function will panic if the internal read/write lock is poisoned
pub fn common_config_mut() -> RwLockWriteGuard<'static, CommonConfig> {
    COMMON_CONFIG.write().unwrap()
}

mod core_impls;

use crate::media_container::ContainerFormat;

#[derive(Debug, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct CommonConfig {
    pub library: LibrarySettings,
    pub loudnorm: LoudnormSettings,
    pub track_name: TrackNameSettings,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct LibrarySettings {
    source_directories: Vec<PathBuf>,

    file_name_format: TrackNameSettings,
    transcode_options: Vec<TranscodeSettings>,
}

impl Default for LibrarySettings {
    fn default() -> Self {
        Self {
            source_directories: Default::default(),
            file_name_format: Default::default(),
            transcode_options: vec![
                // Lossless formats -> Flac
                TranscodeSettings::to_flac(Some(ContainerFormat::Wav), None),
                TranscodeSettings::to_flac(Some(ContainerFormat::Aiff), None),
                TranscodeSettings::to_flac(Some(ContainerFormat::Ape), None),
            ],
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct TrackNameSettings {
    pub format_string: String,
    pub artist_separator: String,
    pub alt_artist_separator: String,
}

impl Default for TrackNameSettings {
    fn default() -> Self {
        const DEFAULT_FORMAT_STRING: &str = if cfg!(windows) {
            "{$album>\\\\}{$main_track_artist?UNKNOWN ARTIST} - {$title?UNKNOWN TITLE}{$feat_track_artists< (feat. >)}"
        } else {
            "{$album>/}{$main_track_artist?UNKNOWN ARTIST} - {$title?UNKNOWN TITLE}{$feat_track_artists< (feat. >)}"
        };

        Self {
            format_string: DEFAULT_FORMAT_STRING.to_owned(),
            artist_separator: ", ".to_owned(),
            alt_artist_separator: " & ".to_owned(),
        }
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[serde(default)]
pub struct LoudnormSettings {
    pub accurate_true_peak: bool,

    pub target_offset: f64,
    pub target_i: f64,
    pub target_tp: f64,
}

impl Default for LoudnormSettings {
    fn default() -> Self {
        Self {
            accurate_true_peak: true,

            target_offset: 0.0,
            target_i: -14.0,
            target_tp: -2.0,
        }
    }
}