On continue le refactoring des sources

This commit is contained in:
2025-10-26 13:44:41 +01:00
parent de40b5b90c
commit e5fbf372c4
18 changed files with 647 additions and 325 deletions

View File

@@ -17,21 +17,19 @@ use tokio::io::AsyncRead;
use tokio::sync::RwLock;
use tracing;
/// Trait pour définir les paramètres du cache
/// Paramètres statiques d'un cache spécialisé.
pub trait CacheConfig: Send + Sync {
/// Extension des fichiers (ex: "webp", "flac")
/// Extension des fichiers générés (ex: `"webp"`, `"flac"`).
fn file_extension() -> &'static str;
/// Nom de la table dans la base de données (ex: "covers", "audio")
/// Type de cache (ex: "audio", "image")
/// Type logique exposé (ex: `"audio"`, `"image"`). Sert notamment pour les routes HTTP.
fn cache_type() -> &'static str {
"file"
}
/// Cache name (ex: "covers", "audio", "cache")
/// Nom du cache (ex: `"covers"`, `"audio"`). Utilisé pour composer les chemins d'accès.
fn cache_name() -> &'static str {
"cache"
}
/// Default param extension ("orig")
/// Qualifier par défaut associé au fichier original (ex: `"orig"`).
fn default_param() -> &'static str {
"orig"
}

View File

@@ -1,66 +1,48 @@
//! # pmocache - Système de cache générique pour PMOMusic
//! # pmocache Système de cache générique pour PMOMusic
//!
//! Cette crate fournit un système de cache générique avec support de base de données SQLite
//! et stockage sur disque. Elle est utilisée comme base pour des caches spécialisés comme
//! `pmocovers` (cache d'images) et `pmoaudiocache` (cache de pistes audio).
//! Cette crate fournit les briques communes utilisées par les caches de PMOMusic.
//! Elle gère l'association entre fichiers stockés sur disque et métadonnées
//! conservées dans une base SQLite, ainsi que les opérations de téléchargement,
//! d'éviction et de mise à jour.
//!
//! ## Vue d'ensemble
//!
//! `pmocache` fournit les composants de base pour :
//! - Stocker des fichiers sur disque avec une base de données SQLite pour les métadonnées
//! - Gérer des collections d'éléments (albums, playlists, etc.)
//! - Suivre les statistiques d'utilisation (hits, dernière utilisation)
//! - Télécharger automatiquement depuis des URLs
//! - Consolider et purger le cache
//! `pmocache` met à disposition :
//! - un modèle `Cache` asynchrone pour stocker des fichiers et leurs métadonnées ;
//! - un module `db` encapsulant l'accès SQLite (table `asset` + table `metadata`) ;
//! - des utilitaires de téléchargement (`download`) réutilisables par les caches spécialisés ;
//! - un trait `CacheConfig` permettant de paramétrer l'extension, le nom et le type du cache.
//!
//! ## Architecture
//! Les crates `pmocovers` (images) et `pmoaudiocache` (pistes audio) s'appuient sur ces
//! composants et ajoutent leurs propres contraintes métier (conversion WebP, métadonnées audio…).
//!
//! `pmocache` est conçu comme une base générique :
//!
//! ```text
//! pmocache (générique)
//! ├── db.rs - Base de données SQLite générique
//! └── cache.rs - Système de cache générique
//!
//! pmocovers (spécialisé pour les images)
//! └── Utilise pmocache + conversion WebP
//!
//! pmoaudiocache (spécialisé pour l'audio)
//! └── Utilise pmocache + métadonnées audio
//! ```
//!
//! ## Utilisation
//!
//! ### Exemple basique
//! ## Exemple basique
//!
//! ```rust,no_run
//! use pmocache::{Cache, CacheConfig};
//!
//! // Définir la configuration du cache
//! struct MyConfig;
//! impl CacheConfig for MyConfig {
//! fn file_extension() -> &'static str { "dat" }
//! fn table_name() -> &'static str { "my_cache" }
//! fn cache_type() -> &'static str { "generic" }
//! }
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let cache = Cache::<MyConfig>::new("./cache", 1000, "http://localhost:8080")?;
//! let cache = Cache::<MyConfig>::new("./cache", 1000)?;
//!
//! // Ajouter un fichier depuis une URL
//! let pk = cache.add_from_url("http://example.com/file.dat", None).await?;
//! println!("Fichier ajouté avec clé: {}", pk);
//! // Ajout d'un fichier depuis une URL
//! let pk = cache.add_from_url("https://example.com/file.dat", None).await?;
//! println!("Fichier ajouté avec la clé {pk}");
//!
//! // Récupérer le fichier
//! // Récupération du fichier local
//! let path = cache.get(&pk).await?;
//! println!("Fichier stocké à: {:?}", path);
//! println!("Fichier disponible à {path:?}");
//!
//! Ok(())
//! }
//! ```
//!
//! ### Utilisation avec des collections
//! ## Collections
//!
//! ```rust,no_run
//! use pmocache::{Cache, CacheConfig};
@@ -68,59 +50,66 @@
//! struct AudioConfig;
//! impl CacheConfig for AudioConfig {
//! fn file_extension() -> &'static str { "flac" }
//! fn table_name() -> &'static str { "audio" }
//! fn cache_type() -> &'static str { "audio" }
//! fn cache_name() -> &'static str { "tracks" }
//! }
//!
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let cache = Cache::<AudioConfig>::new("./cache", 1000, "http://localhost:8080")?;
//! let cache = Cache::<AudioConfig>::new("./audio-cache", 200)?;
//!
//! // Ajouter des pistes d'un album
//! let album_id = "album:the_wall";
//! cache.add_from_url("http://example.com/track1.flac", Some(album_id)).await?;
//! cache.add_from_url("http://example.com/track2.flac", Some(album_id)).await?;
//! let album = "album:the_wall";
//! cache.add_from_url("https://example.com/track1.flac", Some(album)).await?;
//! cache.add_from_url("https://example.com/track2.flac", Some(album)).await?;
//!
//! // Récupérer toutes les pistes de l'album
//! let tracks = cache.get_collection(album_id).await?;
//! println!("Album contient {} pistes", tracks.len());
//! let files = cache.get_collection(album).await?;
//! println!("Album {album} : {} fichiers en cache", files.len());
//!
//! Ok(())
//! }
//! ```
//!
//! ## Structure des fichiers
//! ## Structure sur disque
//!
//! ```text
//! cache/
//! ├── cache.db # Base de données SQLite
//! ├── 1a2b3c4d.webp # Fichier 1
//! └── 5e6f7a8b.flac # Fichier 2
//! ├── cache.db # Base SQLite
//! ├── 1a2b3c4d.orig.dat # Fichier original
//! └── 1a2b3c4d.thumb.dat # Variante (qualifier différent)
//! ```
//!
//! ## Schéma de base de données
//! Les métadonnées sont conservées dans deux tables :
//!
//! ```sql
//! CREATE TABLE {table_name} (
//! pk TEXT PRIMARY KEY, -- Clé unique (hash SHA1 de l'URL)
//! source_url TEXT, -- URL source
//! collection TEXT, -- Collection (album, playlist, etc.)
//! hits INTEGER DEFAULT 0, -- Nombre d'accès
//! last_used TEXT -- Dernière utilisation (RFC3339)
//! CREATE TABLE asset (
//! pk TEXT PRIMARY KEY,
//! collection TEXT,
//! id TEXT,
//! hits INTEGER DEFAULT 0,
//! last_used TEXT
//! );
//!
//! CREATE TABLE metadata (
//! pk TEXT,
//! key TEXT,
//! value_type TEXT CHECK(value_type IN ('string','number','boolean','null')),
//! value TEXT,
//! PRIMARY KEY (pk, key),
//! FOREIGN KEY (pk) REFERENCES asset(pk) ON DELETE CASCADE
//! );
//! ```
//!
//! ## Dépendances principales
//! ## Modules principaux
//!
//! - `rusqlite` : Base de données SQLite
//! - `reqwest` : Téléchargement HTTP
//! - `sha1` : Génération de clés
//! - `tokio` : Runtime asynchrone
//! - [`cache`] : gestion du cache sur disque + opérations asynchrones ;
//! - [`db`] : accès SQLite, contraintes et helpers métadonnées ;
//! - [`download`] : primitives de téléchargement et de transformation ;
//! - [`cache_trait`] : trait partagé entre implémentations spécialisées.
//!
//! ## Voir aussi
//! ## Crates associées
//!
//! - [`pmocovers`] : Cache d'images avec conversion WebP
//! - [`pmoaudiocache`] : Cache de pistes audio
//! - [`pmocovers`] : cache d'images reposant sur `pmocache` ;
//! - [`pmoaudiocache`] : spécialisation audio avec extraction de métadonnées.
pub mod cache;
pub mod cache_trait;