feat(models,core): playlist revision evidence and track-ids fetch
Playlist and PlaylistWithTrackIds deserialize the API updated_at stamp (absent on some list shapes, hence optional), and the core facade exposes the lightweight playlist/get?extra=track_ids fetch the membership hydrator drives. cc
This commit is contained in:
4 files changed
+30
-12
No files matched your search
@@ -1419,6 +1419,22 @@ impl<A: FrontendAdapter + Send + Sync + 'static> QbzCore<A> {
|
||||
.map_err(CoreError::Api)
|
||||
}
|
||||
|
||||
/// Lightweight membership fetch: one playlist's track IDS only
|
||||
/// (`playlist/get?extra=track_ids`). The membership hydrator's probe —
|
||||
/// no Track objects, no pagination.
|
||||
pub async fn get_playlist_track_ids(
|
||||
&self,
|
||||
playlist_id: u64,
|
||||
) -> Result<qbz_models::PlaylistWithTrackIds, CoreError> {
|
||||
let client = self.client.read().await;
|
||||
let client = client.as_ref().ok_or(CoreError::NotInitialized)?;
|
||||
|
||||
client
|
||||
.get_playlist_track_ids(playlist_id)
|
||||
.await
|
||||
.map_err(CoreError::Api)
|
||||
}
|
||||
|
||||
/// Check how many of `track_ids` are already in the Qobuz playlist
|
||||
/// `playlist_id`. Mirrors Tauri's `v2_check_playlist_duplicates`
|
||||
/// (commands_v2/playlists.rs): fetch the playlist's existing track ids and
|
||||
|
||||
@@ -34,10 +34,7 @@ pub enum PlaylistTrackRef {
|
||||
/// A library-projected row: a local file, or a Jellyfin/Subsonic track
|
||||
/// (their playlist storage identity is the projection). `path` is the
|
||||
/// `local_tracks.file_path` value local playlists key on.
|
||||
Library {
|
||||
track_id: i64,
|
||||
path: Option<String>,
|
||||
},
|
||||
Library { track_id: i64, path: Option<String> },
|
||||
/// A Plex track by rating key.
|
||||
Plex { rating_key: String },
|
||||
}
|
||||
@@ -467,7 +464,10 @@ mod tests {
|
||||
// The Qobuz playlist holds the library row and the Plex key (2 of 3);
|
||||
// the local playlist holds all three.
|
||||
assert_eq!(contained(&rows, ContainmentTarget::Qobuz(7)), Some(2));
|
||||
assert_eq!(contained(&rows, ContainmentTarget::Local(lp.clone())), Some(3));
|
||||
assert_eq!(
|
||||
contained(&rows, ContainmentTarget::Local(lp.clone())),
|
||||
Some(3)
|
||||
);
|
||||
assert_eq!(rows[0].target, ContainmentTarget::Local(lp));
|
||||
}
|
||||
|
||||
|
||||
@@ -215,10 +215,7 @@ pub fn current_generation(conn: &Connection) -> Result<i64> {
|
||||
/// An EMPTY list is still recorded (a user can genuinely delete every
|
||||
/// playlist), but the caller must only invoke this for a fetch that
|
||||
/// succeeded — a network failure must never masquerade as an empty library.
|
||||
pub fn record_authoritative_list(
|
||||
conn: &Connection,
|
||||
entries: &[AuthoritativeEntry],
|
||||
) -> Result<i64> {
|
||||
pub fn record_authoritative_list(conn: &Connection, entries: &[AuthoritativeEntry]) -> Result<i64> {
|
||||
let ts = now_ms();
|
||||
conn.execute_batch("BEGIN IMMEDIATE")?;
|
||||
let result = (|| -> Result<i64> {
|
||||
@@ -620,9 +617,7 @@ pub fn track_ids(conn: &Connection, qobuz_playlist_id: u64) -> Result<Vec<u64>>
|
||||
WHERE qobuz_playlist_id = ?1 ORDER BY position",
|
||||
)?;
|
||||
let mut out = Vec::new();
|
||||
for r in stmt.query_map(params![qobuz_playlist_id as i64], |r| {
|
||||
r.get::<_, i64>(0)
|
||||
})? {
|
||||
for r in stmt.query_map(params![qobuz_playlist_id as i64], |r| r.get::<_, i64>(0))? {
|
||||
out.push(r? as u64);
|
||||
}
|
||||
Ok(out)
|
||||
|
||||
@@ -828,6 +828,10 @@ pub struct Playlist {
|
||||
pub image_rectangle_mini: Option<Vec<String>>,
|
||||
pub slug: Option<String>,
|
||||
pub users_count: Option<u32>,
|
||||
/// API revision stamp (unix seconds). The membership index reads it as
|
||||
/// change evidence when the API provides it; absent on some list shapes.
|
||||
#[serde(default)]
|
||||
pub updated_at: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
@@ -869,6 +873,9 @@ pub struct PlaylistWithTrackIds {
|
||||
pub images300: Option<Vec<String>>,
|
||||
pub slug: Option<String>,
|
||||
pub users_count: Option<u32>,
|
||||
/// API revision stamp (unix seconds), when the response carries one.
|
||||
#[serde(default)]
|
||||
pub updated_at: Option<i64>,
|
||||
}
|
||||
|
||||
/// Result of checking for duplicate tracks in a playlist
|
||||
|
||||
Reference in new issue
Block a user