feat: add Qobuz playlist caching with versioning and infinite scroll UI

- Add qobuz_debug to .gitignore
- Bump PMOMusic version to 0.3.26
- Add .cargo/ copy in Dockerfile for registry config
- Implement infinite scroll in ServerDrawer.vue with IntersectionObserver and sentinel element
- Add source/source_version fields to playlists for cache invalidation
- Update pmocache and pmoplaylist DB schemas with versioning (SCHEMA_VERSION)
- Add Qobuz API metadata fetching and pagination for playlist tracks
- Implement album/playlist cache invalidation based on released_at/updated_at timestamps
- Refactor adapt_playlist_items_to_qobuz → adapt_items_to_qobuz with cleaner logic
- Add debug mode to save Qobuz API responses when QOBUZ_DEBUG_DIR is set
This commit is contained in:
2026-03-24 17:10:38 +01:00
parent 402c3399e4
commit 0137a4675f
17 changed files with 688 additions and 160 deletions

View File

@@ -36,6 +36,10 @@ pub struct Playlist {
role: RwLock<PlaylistRole>,
cover_pk: RwLock<Option<String>>,
artist: RwLock<Option<String>>,
/// Source externe (ex: "qobuz")
source: RwLock<Option<String>>,
/// Version de la source (ex: timestamp updated_at de Qobuz)
source_version: RwLock<Option<String>>,
state: Arc<AtomicU8>,
pub core: Arc<RwLock<PlaylistCore>>,
pub persistent: bool,
@@ -59,6 +63,8 @@ impl Playlist {
role: RwLock::new(role),
cover_pk: RwLock::new(cover_pk),
artist: RwLock::new(None),
source: RwLock::new(None),
source_version: RwLock::new(None),
state: Arc::new(AtomicU8::new(PlaylistState::Active as u8)),
core: Arc::new(RwLock::new(PlaylistCore::new(config))),
persistent,
@@ -127,6 +133,28 @@ impl Playlist {
self.touch().await;
}
/// Retourne la source externe associée à la playlist.
pub async fn source(&self) -> Option<String> {
self.source.read().await.clone()
}
/// Modifie la source de la playlist.
pub async fn set_source(&self, value: Option<String>) {
*self.source.write().await = value;
self.touch().await;
}
/// Retourne la version de la source (ex: updated_at timestamp).
pub async fn source_version(&self) -> Option<String> {
self.source_version.read().await.clone()
}
/// Modifie la version de la source.
pub async fn set_source_version(&self, value: Option<String>) {
*self.source_version.write().await = value;
self.touch().await;
}
/// Timestamp du dernier changement
pub async fn last_change(&self) -> SystemTime {
*self.last_change.read().await