Files
pmomusic/pmoqobuz/DISK_CACHE_USAGE.md

8.4 KiB
Raw Blame History

Utilisation du cache disque pour favoris/bibliothèque

Intégration dans QobuzClient

Étape 1 : Ajouter le cache disque au client

// Dans src/client.rs

use crate::disk_cache::DiskCache;

pub struct QobuzClient {
    api: QobuzApi,
    cache: Arc<QobuzCache>,          // Cache mémoire (existant)
    disk_cache: Arc<DiskCache>,      // Cache disque (nouveau)
    auth_info: Option<AuthInfo>,
}

impl QobuzClient {
    pub async fn from_config_obj(config: &Config) -> Result<Self> {
        // ... code existant ...

        // Créer le cache disque (utilise le répertoire configuré)
        let disk_cache_dir = config.get_qobuz_cache_dir()?;
        let disk_cache = Arc::new(DiskCache::new(disk_cache_dir)?);

        Ok(Self {
            api,
            cache: Arc::new(QobuzCache::new()),
            disk_cache,
            auth_info: Some(auth_info),
        })
    }
}

Étape 2 : Utiliser le cache pour get_favorite_albums

// Dans src/client.rs

impl QobuzClient {
    /// Récupère les albums favoris (avec cache disque)
    pub async fn get_favorite_albums(&self) -> Result<Vec<Album>> {
        let user_id = self.auth_info
            .as_ref()
            .map(|a| &a.user_id)
            .ok_or_else(|| QobuzError::Unauthorized("Not authenticated".to_string()))?;

        let cache_key = format!("favorites_albums_{}", user_id);

        // 1. Essayer de charger depuis le cache disque (TTL: 1 heure)
        if let Ok(Some(albums)) = self.disk_cache.load_with_ttl::<Vec<Album>>(
            &cache_key,
            Duration::from_secs(3600)
        ) {
            info!("✓ Loaded {} favorite albums from disk cache", albums.len());
            return Ok(albums);
        }

        // 2. Sinon, requête API
        info!("Fetching favorite albums from API...");
        let albums = self.api.get_favorite_albums().await?;

        // 3. Sauvegarder dans le cache disque
        if let Err(e) = self.disk_cache.save(&cache_key, &albums) {
            debug!("Failed to save favorites to disk cache: {}", e);
        } else {
            info!("✓ Saved {} favorite albums to disk cache", albums.len());
        }

        Ok(albums)
    }

    /// Récupère les tracks favoris (avec cache disque)
    pub async fn get_favorite_tracks(&self) -> Result<Vec<Track>> {
        let user_id = self.auth_info
            .as_ref()
            .map(|a| &a.user_id)
            .ok_or_else(|| QobuzError::Unauthorized("Not authenticated".to_string()))?;

        let cache_key = format!("favorites_tracks_{}", user_id);

        // 1. Cache disque (TTL: 1 heure)
        if let Ok(Some(tracks)) = self.disk_cache.load_with_ttl::<Vec<Track>>(
            &cache_key,
            Duration::from_secs(3600)
        ) {
            info!("✓ Loaded {} favorite tracks from disk cache", tracks.len());
            return Ok(tracks);
        }

        // 2. API
        info!("Fetching favorite tracks from API...");
        let tracks = self.api.get_favorite_tracks().await?;

        // 3. Sauvegarder
        if let Err(e) = self.disk_cache.save(&cache_key, &tracks) {
            debug!("Failed to save favorites to disk cache: {}", e);
        } else {
            info!("✓ Saved {} favorite tracks to disk cache", tracks.len());
        }

        Ok(tracks)
    }

    /// Récupère les playlists (avec cache disque)
    pub async fn get_user_playlists(&self) -> Result<Vec<Playlist>> {
        let user_id = self.auth_info
            .as_ref()
            .map(|a| &a.user_id)
            .ok_or_else(|| QobuzError::Unauthorized("Not authenticated".to_string()))?;

        let cache_key = format!("playlists_{}", user_id);

        // 1. Cache disque (TTL: 30 minutes - les playlists changent plus souvent)
        if let Ok(Some(playlists)) = self.disk_cache.load_with_ttl::<Vec<Playlist>>(
            &cache_key,
            Duration::from_secs(1800)
        ) {
            info!("✓ Loaded {} playlists from disk cache", playlists.len());
            return Ok(playlists);
        }

        // 2. API
        info!("Fetching playlists from API...");
        let playlists = self.api.get_user_playlists().await?;

        // 3. Sauvegarder
        if let Err(e) = self.disk_cache.save(&cache_key, &playlists) {
            debug!("Failed to save playlists to disk cache: {}", e);
        } else {
            info!("✓ Saved {} playlists to disk cache", playlists.len());
        }

        Ok(playlists)
    }

    /// Invalide le cache des favoris (après ajout/suppression)
    pub async fn invalidate_favorites_cache(&self) -> Result<()> {
        let user_id = self.auth_info
            .as_ref()
            .map(|a| &a.user_id)
            .ok_or_else(|| QobuzError::Unauthorized("Not authenticated".to_string()))?;

        self.disk_cache.invalidate(&format!("favorites_albums_{}", user_id))?;
        self.disk_cache.invalidate(&format!("favorites_tracks_{}", user_id))?;
        self.disk_cache.invalidate(&format!("playlists_{}", user_id))?;

        info!("✓ Invalidated favorites cache");
        Ok(())
    }
}

Étape 3 : Méthodes utilitaires

impl QobuzClient {
    /// Retourne des statistiques sur le cache disque
    pub fn disk_cache_stats(&self) -> Result<(usize, u64)> {
        let count = self.disk_cache.count()?;
        let size = self.disk_cache.size()?;
        Ok((count, size))
    }

    /// Vide complètement le cache disque
    pub fn clear_disk_cache(&self) -> Result<()> {
        self.disk_cache.clear_all()
    }
}

Structure sur disque

.pmomusic/
├── config.yaml
└── cache/
    └── qobuz/
        ├── favorites_albums_1217710.json    # 375 albums (~200 KB)
        ├── favorites_tracks_1217710.json    # Tracks favoris
        └── playlists_1217710.json           # Playlists utilisateur

Bénéfices

Sans cache disque (AVANT)

# Lancement 1
INFO  Fetching 375 favorite albums from API... (2.5s)

# Lancement 2 (app redémarrée)
INFO  Fetching 375 favorite albums from API... (2.5s) ← Requête inutile !

# Lancement 3
INFO  Fetching 375 favorite albums from API... (2.5s) ← Requête inutile !

Total : 3 requêtes API × 2.5s = 7.5 secondes

Avec cache disque (APRÈS)

# Lancement 1 (cache miss)
INFO  Fetching 375 favorite albums from API... (2.5s)
INFO  ✓ Saved 375 favorite albums to disk cache

# Lancement 2 (cache hit!)
INFO  ✓ Loaded 375 favorite albums from disk cache (5ms) ← Instantané !

# Lancement 3 (cache hit!)
INFO  ✓ Loaded 375 favorite albums from disk cache (5ms) ← Instantané !

Total : 1 requête API × 2.5s + 2 cache hits × 5ms = 2.51 secondes

Amélioration : 66% plus rapide + réduction de 66% des requêtes API

TTL recommandés

Donnée TTL Justification
Albums favoris 1h Changent rarement
Tracks favoris 1h Changent rarement
Playlists 30min Modifiées plus souvent
Bibliothèque complète 24h Très volumineuse, change peu

Invalidation intelligente

Invalider le cache après modifications :

// Après ajout d'un favori
client.add_favorite_album("123").await?;
client.invalidate_favorites_cache().await?;

// Après suppression
client.remove_favorite_album("123").await?;
client.invalidate_favorites_cache().await?;

Tests

# Test du cache disque
cargo test -p pmoqobuz disk_cache

# Test d'intégration
cargo run --example basic_usage

# Logs détaillés
RUST_LOG=info,pmoqobuz::disk_cache=debug cargo run --example basic_usage

Migration

Pour ajouter le cache disque au client existant :

  1. Ajouter le champ disk_cache à QobuzClient
  2. Initialiser dans from_config_obj()
  3. Modifier get_favorite_albums(), get_favorite_tracks(), etc.
  4. Tester avec des gros catalogues (375+ albums)

Taille estimée du cache

Pour un utilisateur avec :

  • 375 albums favoris
  • 100 tracks favoris
  • 10 playlists

Taille totale : ~300 KB (négligeable)

Comparaison : pmocache vs DiskCache

Critère pmocache DiskCache
Complexité Élevée (SQLite, download, variants) Faible (fichiers JSON simples)
Taille overhead ~100 KB (SQLite + tables) 0 (juste les JSON)
Performance Excellent pour binaires Excellent pour JSON
Maintenance Complexe Simple
Adapté pour JSON Non Oui

Conclusion : DiskCache est parfaitement adapté pour le cache de favoris/bibliothèque.