Persist provider_item_id through fsdy link tomls
A link/queue/bookmark toml now carries the source track's provider id, so a later capture of a linked track can de-duplicate by provider id before downloading instead of falling back to hashing fetched bytes. Empty when the source has none (e.g. a local /fs file). The scan CLI fills the new field explicitly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b50cf862b3
commit
6ca8607586
|
|
@ -235,6 +235,8 @@ async fn scan_file(
|
|||
artist: String::new(),
|
||||
duration: None,
|
||||
album: None,
|
||||
// A scanned local file has no provider-internal id.
|
||||
provider_item_id: None,
|
||||
playable,
|
||||
};
|
||||
tokio::fs::write(&sidecar, track_file.to_toml()?).await?;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,14 @@ pub struct TrackFile {
|
|||
/// Optional album metadata.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub album: Option<AlbumMeta>,
|
||||
/// Provider-internal id of the source item (`Track.provider_item_id`),
|
||||
/// preserved so it survives a link/queue/bookmark round trip. Without it
|
||||
/// a later capture of a linked track cannot de-duplicate by provider id
|
||||
/// *before* downloading — it falls back to hashing the fetched bytes
|
||||
/// (architecture/crabidy-store.md D3/D4). Omitted when the source has
|
||||
/// none (e.g. a local `/fs` file).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub provider_item_id: Option<String>,
|
||||
/// The playable reference; exactly one of its fields must be set.
|
||||
pub playable: PlayableSpec,
|
||||
}
|
||||
|
|
@ -218,9 +226,10 @@ impl TrackFile {
|
|||
release_date: a.release_date.clone(),
|
||||
}),
|
||||
is_skipped: matches!(playable, Ok(Playable::Skipped)),
|
||||
// The provider that owns a link target sets this at its end; a
|
||||
// local file/store/skipped track has no provider-internal id.
|
||||
provider_item_id: String::new(),
|
||||
// Restored from the toml when present (a link/queue/bookmark keeps
|
||||
// the source's provider id so a later capture can de-dup by id);
|
||||
// empty when the source had none.
|
||||
provider_item_id: self.provider_item_id.clone().unwrap_or_default(),
|
||||
// A store-backed track is captured by construction; other
|
||||
// playables are marked (or not) by the server's store index.
|
||||
is_captured: matches!(playable, Ok(Playable::Store(_))),
|
||||
|
|
@ -264,6 +273,10 @@ impl TrackFile {
|
|||
title: a.title.clone(),
|
||||
release_date: a.release_date.clone(),
|
||||
}),
|
||||
// Keep the source's provider id so a capture of this saved entry
|
||||
// can de-dup by id before downloading (crabidy-store.md D4).
|
||||
provider_item_id: (!track.provider_item_id.is_empty())
|
||||
.then(|| track.provider_item_id.clone()),
|
||||
playable,
|
||||
}
|
||||
}
|
||||
|
|
@ -1503,6 +1516,33 @@ mod tests {
|
|||
assert_eq!(restored, track);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn link_files_preserve_the_provider_item_id() {
|
||||
// A streamed track (e.g. Tidal) carries a provider id; a link save
|
||||
// (queue/bookmark) must keep it so a later capture of that save can
|
||||
// de-duplicate by id before downloading (crabidy-store.md D4).
|
||||
let track = Track {
|
||||
path: "/tidal/albums/1/125169484".to_string(),
|
||||
artist: "Efence".to_string(),
|
||||
title: "Bay of Lost Dreams".to_string(),
|
||||
duration: Some(216),
|
||||
album: None,
|
||||
is_skipped: false,
|
||||
provider_item_id: "125169484".to_string(),
|
||||
is_captured: false,
|
||||
};
|
||||
let toml_text = TrackFile::from_track(&track).to_toml().expect("serialize");
|
||||
assert!(
|
||||
toml_text.contains("provider_item_id = \"125169484\""),
|
||||
"the id is written to the link toml: {toml_text}"
|
||||
);
|
||||
let restored = TrackFile::parse(&toml_text)
|
||||
.expect("reparse")
|
||||
.to_track("/crabidy/current/0001 Bay of Lost Dreams.cbd-track.toml");
|
||||
assert_eq!(restored.provider_item_id, "125169484");
|
||||
assert_eq!(restored, track);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_track_with_file_plays_the_relative_sibling() {
|
||||
let track = Track {
|
||||
|
|
|
|||
Loading…
Reference in New Issue