Files
pmomusic/pmoaudiocache/src/lib.rs
2025-12-06 12:48:21 +01:00

251 lines
8.2 KiB
Rust
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! # pmoaudiocache Cache de pistes audio pour PMOMusic
//!
//! `pmoaudiocache` s'appuie sur [`pmocache`] pour fournir un cache spécialisé
//! dans les fichiers audio. Il assure la conversion transparente au format FLAC,
//! l'extraction des métadonnées et la mise à disposition d'outils pour les exposer.
//!
//! ## Fonctionnalités
//!
//! - conversion automatique des entrées en FLAC grâce à un `StreamTransformer` ;
//! - extraction des tags (artiste, album, titre, etc.) via [`metadata::AudioMetadata`] ;
//! - stockage des métadonnées dans la table `metadata` de `pmocache::DB` ;
//! - helpers pour renseigner les collections à partir des tags ;
//! - intégration optionnelle avec `pmoserver` (routes REST + diffusion de fichiers).
//!
//! ## Exemple rapide
//!
//! ```rust,no_run
//! use pmoaudiocache::cache;
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let cache = cache::new_cache("./audio_cache", 500)?;
//!
//! // Télécharge la piste, déclenche la conversion FLAC et stocke les métadonnées.
//! let pk = cache::add_with_metadata_extraction(
//! &cache,
//! "https://example.com/track.mp3",
//! None,
//! ).await?;
//!
//! // Lecture des métadonnées extraites via TrackMetadata
//! use pmoaudiocache::metadata_ext::AudioTrackMetadataExt;
//! let track_meta = cache.track_metadata(&pk);
//! let title = track_meta.read().await.get_title().await?;
//! println!("Titre: {}", title.unwrap_or_else(|| "Inconnu".to_string()));
//!
//! // Accès au fichier FLAC converti
//! let flac_path = cache.get(&pk).await?;
//! println!("Fichier converti: {flac_path:?}");
//!
//! Ok(())
//! }
//! ```
//!
//! ## Intégration serveur (feature `pmoserver`)
//!
//! Lorsque la feature `pmoserver` est activée, [`AudioCacheExt`] permet
//! d'enregistrer automatiquement les routes suivantes :
//!
//! - `GET /audio/tracks/{pk}` : téléchargement/stream du FLAC original ;
//! - `GET /audio/tracks/{pk}/{qualifier}` : variantes (ex: `orig`) ;
//! - `GET /api/audio` / `POST /api/audio` / `DELETE /api/audio` : API REST générique ;
//! - `GET /api/audio/{pk}/status` : suivi de téléchargement ;
//! - endpoints OpenAPI/Swagger lorsqu'`openapi` est activée.
//!
//! ## Métadonnées gérées
//!
//! Le module [`metadata`] extrait notamment :
//! - titre, artiste, album, genre ;
//! - numéros de piste/disque et totaux associés ;
//! - année, durée, bitrate, sample rate, nombre de canaux.
//!
//! En l'absence d'artiste/album, aucune collection automatique n'est créée.
//!
//! ## Modules
//!
//! - [`cache`] : instanciation du cache et helpers de téléchargement ;
//! - [`metadata`] : extraction/structure des métadonnées audio ;
//! - [`config_ext`] *(feature `pmoconfig`)* : dérivation de la configuration depuis `pmoconfig`;
//! - [`openapi`] *(feature `pmoserver`)* : documentation des routes REST.
//!
//! ## Crates voisines
//!
//! - [`pmocache`] : fondation générique ;
//! - [`pmocovers`] : spécialisation images (architecture similaire) ;
//! - [`pmoserver`] : serveur HTTP optionnel.
pub mod cache;
pub mod metadata;
pub mod metadata_ext;
pub mod streaming;
pub mod track_metadata;
#[cfg(feature = "pmoserver")]
pub mod api;
#[cfg(feature = "pmoserver")]
pub mod openapi;
#[cfg(feature = "pmoconfig")]
pub mod config_ext;
// Re-exports principaux
pub use cache::{
add_with_metadata_extraction, new_cache, new_cache_with_consolidation, AudioConfig, Cache,
};
pub use metadata::AudioMetadata;
pub use metadata_ext::{AudioMetadataExt, AudioTrackMetadataExt, TrackMetadataDidlExt};
pub use track_metadata::AudioCacheTrackMetadata;
#[cfg(feature = "pmoconfig")]
pub use config_ext::AudioCacheConfigExt;
#[cfg(feature = "pmoserver")]
pub use openapi::ApiDoc;
// ============================================================================
// Registre global singleton
// ============================================================================
use once_cell::sync::OnceCell;
use std::sync::Arc;
static AUDIO_CACHE: OnceCell<Arc<Cache>> = OnceCell::new();
/// Enregistre le cache audio global
///
/// Cette fonction doit être appelée au démarrage de l'application
/// pour rendre le cache audio disponible globalement.
///
/// # Arguments
///
/// * `cache` - Instance partagée du cache audio à enregistrer
///
/// # Behavior
///
/// - Si appelée plusieurs fois, seul le premier appel prend effet
/// - Thread-safe: peut être appelée depuis plusieurs threads simultanément
/// - Une fois enregistré, le cache est accessible via [`get_audio_cache`]
///
/// # Examples
///
/// ```rust,ignore
/// use pmoaudiocache::{new_cache, register_audio_cache};
/// use std::sync::Arc;
///
/// let cache = Arc::new(new_cache("./cache", 1000)?);
/// register_audio_cache(cache);
/// ```
pub fn register_audio_cache(cache: Arc<Cache>) {
let _ = AUDIO_CACHE.set(cache);
}
/// Accès global au cache audio
///
/// Retourne une référence au cache audio enregistré via [`register_audio_cache`],
/// ou `None` si aucun cache n'a été enregistré.
///
/// # Returns
///
/// * `Some(Arc<Cache>)` - Instance partagée du cache audio si enregistré
/// * `None` - Si aucun cache n'a été enregistré
///
/// # Thread Safety
///
/// Cette fonction est thread-safe et peut être appelée depuis plusieurs threads.
///
/// # Examples
///
/// ```rust,ignore
/// use pmoaudiocache::get_audio_cache;
///
/// if let Some(cache) = get_audio_cache() {
/// // Utiliser le cache
/// }
/// ```
pub fn get_audio_cache() -> Option<Arc<Cache>> {
AUDIO_CACHE.get().cloned()
}
// ============================================================================
// Extension pmoserver (inline comme pmocovers)
// ============================================================================
/// Trait pour étendre un serveur HTTP avec des fonctionnalités de cache audio.
#[cfg(feature = "pmoserver")]
pub trait AudioCacheExt {
/// Initialise le cache audio et enregistre les routes HTTP.
///
/// # Arguments
///
/// * `cache_dir` - Répertoire de stockage du cache
/// * `limit` - Limite de taille du cache (en nombre de pistes)
///
/// # Returns
///
/// * `Arc<Cache>` - Instance partagée du cache
async fn init_audio_cache(
&mut self,
cache_dir: &str,
limit: usize,
) -> anyhow::Result<std::sync::Arc<Cache>>;
/// Initialise le cache audio avec la configuration par défaut.
///
/// Utilise automatiquement les paramètres de `pmoconfig::Config`.
async fn init_audio_cache_configured(&mut self) -> anyhow::Result<std::sync::Arc<Cache>>;
}
#[cfg(feature = "pmoserver")]
use pmocache::pmoserver_ext::{create_api_router, create_file_router};
#[cfg(feature = "pmoserver")]
use utoipa::OpenApi;
#[cfg(feature = "pmoserver")]
impl AudioCacheExt for pmoserver::Server {
async fn init_audio_cache(
&mut self,
cache_dir: &str,
limit: usize,
) -> anyhow::Result<Arc<Cache>> {
let cache = Arc::new(crate::cache::new_cache(cache_dir, limit)?);
// Router de fichiers pour servir les pistes FLAC
// Routes: GET /audio/tracks/{pk} et GET /audio/tracks/{pk}/{param}
let file_router = create_file_router(
cache.clone(),
"audio/flac", // Content-Type
);
self.add_router("/", file_router).await;
// API REST générique (pmocache)
// Routes: GET/POST/DELETE /api/audio, etc.
let mut api_router = create_api_router(cache.clone());
// Ajouter les endpoints audio spécifiques
// Route: GET /api/audio/{pk}/cover-url
api_router = api_router.merge(
axum::Router::new()
.route(
"/{pk}/cover-url",
axum::routing::get(crate::api::get_cover_url),
)
.with_state(cache.clone())
);
let openapi = crate::ApiDoc::openapi();
self.add_openapi(api_router, openapi, "audio").await;
Ok(cache)
}
async fn init_audio_cache_configured(&mut self) -> anyhow::Result<Arc<Cache>> {
use crate::AudioCacheConfigExt;
let config = pmoconfig::get_config();
let cache_dir = config.get_audiocache_dir()?;
let limit = config.get_audiocache_size()?;
self.init_audio_cache(&cache_dir, limit).await
}
}