selene-core 0.7.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, formatter::TemplateOwned};
use serde::{Deserialize, Serialize};

use crate::lyrics::LyricFormat;

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;

#[derive(Debug, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct CommonConfig {
    pub main: MainSettings,
    pub loudnorm: LoudnormSettings,
}

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

impl Default for MainSettings {
    fn default() -> Self {
        Self {
            multithreading: true,
            source_directories: Vec::new(),
        }
    }
}

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

#[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,
        }
    }
}

pub struct ExportConfig {
    // Filesystem
    pub file_name_format: TemplateOwned,
    pub artist_separator: String,
    pub alt_artist_separator: String,

    pub album_data_path: Option<TemplateOwned>,

    pub export_synced_lyrics_as: Option<LyricFormat>,
    pub plain_lyrics_as_txt: bool,

    // Metadata
    pub singles_as_albums: bool,
    pub multi_value_strategy: MultiValueStrategy,
}

impl ExportConfig {
    pub fn selene_export() -> Self {
        Self {
        file_name_format: TemplateOwned::new("{$album>/}{$main_track_artist?UNKNOWN ARTIST} - {$title?UNKNOWN TITLE}{$feat_track_artists< (feat. >)}").unwrap(),
        artist_separator: ", ".to_owned(),
        alt_artist_separator: " & ".to_owned(),
        album_data_path: Some(TemplateOwned::new("{$album}/").unwrap()),
        export_synced_lyrics_as: Some(LyricFormat::SeleneLrc),
        plain_lyrics_as_txt: true,
        singles_as_albums: false,
        multi_value_strategy: MultiValueStrategy::MultipleTags,
    }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum MultiValueStrategy {
    MultipleTags,
    JoinBy(String),
    First,
}