fix(qbz-qt): decode local artwork file URIs
cx
This commit is contained in:
3 files changed
+42
-5
No files matched your search
@@ -228,8 +228,17 @@ impl QueueTrack {
|
||||
}
|
||||
if raw.starts_with("http://") || raw.starts_with("https://") {
|
||||
ArtworkRef::Remote(raw.to_string())
|
||||
} else if let Some(path) = raw.strip_prefix("file://") {
|
||||
ArtworkRef::LocalFile(path.to_string())
|
||||
} else if raw.starts_with("file://") {
|
||||
// Queue builders hand this layer a URI, not a filesystem path.
|
||||
// Decode it before `to_mpris_url` canonicalises it again; merely
|
||||
// stripping the scheme turns `%23` into a literal filename and
|
||||
// the second encode publishes `%2523` to MPRIS.
|
||||
let path = url::Url::parse(raw)
|
||||
.ok()
|
||||
.and_then(|url| url.to_file_path().ok())
|
||||
.map(|path| path.to_string_lossy().into_owned())
|
||||
.unwrap_or_else(|| raw.trim_start_matches("file://").to_string());
|
||||
ArtworkRef::LocalFile(path)
|
||||
} else {
|
||||
ArtworkRef::LocalFile(raw.to_string())
|
||||
}
|
||||
@@ -373,6 +382,20 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_file_uri_round_trips_special_path_chars_for_mpris() {
|
||||
let path = "/mnt/music/Disc 01 - Soundtrack #01/100%? cover.jpg";
|
||||
let queue_uri = "file:///mnt/music/Disc 01 - Soundtrack %2301/100%25%3F cover.jpg";
|
||||
let artwork = track_with(Some("local"), Some(queue_uri)).artwork_ref();
|
||||
assert_eq!(artwork, ArtworkRef::LocalFile(path.into()));
|
||||
assert_eq!(
|
||||
artwork.to_mpris_url(),
|
||||
url::Url::from_file_path(path)
|
||||
.ok()
|
||||
.map(|url| url.to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn artwork_ref_with_plex_resolves_library_path() {
|
||||
// Plex thumb path + creds → PlexThumb (the now-playing/queue/MPRIS path).
|
||||
|
||||
@@ -162,8 +162,12 @@ pub fn classify(url: &str) -> ArtUrl {
|
||||
}
|
||||
// A `file://` url is ALREADY on disk — reqwest cannot even build a
|
||||
// request for it ("builder error for url"), so it must never be fetched.
|
||||
if let Some(path) = url.strip_prefix("file://") {
|
||||
return ArtUrl::LocalFile(path.to_string());
|
||||
if url.starts_with("file://") {
|
||||
// `local_queue_track` escapes `%`, `#` and `?` before this value
|
||||
// reaches QML. The filesystem probe needs the inverse, otherwise a
|
||||
// real `Disc 01 - Soundtrack #01/cover.jpg` is stat'ed as a literal
|
||||
// `...%2301/cover.jpg` and every queue-owned surface gets a miss.
|
||||
return ArtUrl::LocalFile(local_path(url));
|
||||
}
|
||||
// A SOURCE-TAGGED token (`jellyfin:<albumId>/<tag>`,
|
||||
// `subsonic:al-<id>_<hash>`), stamped by `local_queue_track` for a media
|
||||
@@ -892,6 +896,8 @@ mod tests {
|
||||
classify("/home/u/Music/Album/cover.jpg"),
|
||||
ArtUrl::LocalFile("/home/u/Music/Album/cover.jpg".into())
|
||||
);
|
||||
let raw = "/mnt/music/Disc 01 - Soundtrack #01/100%? cover.jpg";
|
||||
assert_eq!(classify(&file_url(raw)), ArtUrl::LocalFile(raw.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -966,7 +966,10 @@ mod tests {
|
||||
.as_nanos();
|
||||
let box_dir =
|
||||
std::env::temp_dir().join(format!("qbz-queue-disc-art-{}-{nonce}", std::process::id()));
|
||||
let disc_one = box_dir.join("Disc 01 - Sanctuary");
|
||||
// `#` is the exact seam this fixture protects: QueueTrack carries a
|
||||
// file URI (`%23`), while the NPB/queue resolver must stat the decoded
|
||||
// filesystem path and MPRIS must not publish a double-escaped `%2523`.
|
||||
let disc_one = box_dir.join("Disc 01 - TV Series Soundtrack #01");
|
||||
let disc_five = box_dir.join("Disc 05 - Movie OST");
|
||||
std::fs::create_dir_all(&disc_one).unwrap();
|
||||
std::fs::create_dir_all(&disc_five).unwrap();
|
||||
@@ -998,6 +1001,11 @@ mod tests {
|
||||
assert_ne!(one.artwork_url, five.artwork_url);
|
||||
assert!(one.artwork_url.as_deref().unwrap().contains("Disc 01"));
|
||||
assert!(five.artwork_url.as_deref().unwrap().contains("Disc 05"));
|
||||
let one_resolved = crate::artwork_qt::cached_path(one.artwork_url.as_deref().unwrap());
|
||||
assert_eq!(
|
||||
crate::artwork_qt::local_path(&one_resolved),
|
||||
disc_one.join("cover.jpg").to_string_lossy()
|
||||
);
|
||||
|
||||
std::fs::remove_dir_all(&box_dir).unwrap();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user