feat(slint): MPRIS album art (mpris:artUrl) — Qobuz pass-through + local file://
Port the Tauri normalizeCoverUrlForMetadata workaround as ArtworkRef::to_mpris_url (qbz-models): remote Qobuz covers pass through as-is, local-file covers become a percent-encoded file:// URI (MPRIS clients cannot read bare paths or asset://), Plex thumbs become a tokenized HTTP URL. Wired into the Slint now-playing push so the media widget shows album art. The app icon was already fixed via DesktopEntry. cc
This commit is contained in:
4 files changed
+39
-3
No files matched your search
Generated
+1
@@ -5700,6 +5700,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -21,3 +21,6 @@ thiserror = { workspace = true }
|
||||
|
||||
# Time
|
||||
chrono = { workspace = true }
|
||||
|
||||
# Correct file:// URI building for media-controls art (ArtworkRef::to_mpris_url)
|
||||
url = "2"
|
||||
@@ -144,6 +144,37 @@ impl ArtworkRef {
|
||||
ArtworkRef::PlexThumb { path, .. } => path.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A URL suitable for the MPRIS `mpris:artUrl` property (and other OS media
|
||||
/// controls). Mirrors the Tauri frontend's `normalizeCoverUrlForMetadata`:
|
||||
/// - **Remote** HTTP(S) covers (Qobuz) pass through — clients fetch them.
|
||||
/// - **LocalFile** bare paths become a proper percent-encoded `file://`
|
||||
/// URI (MPRIS clients cannot read a bare path or an `asset://` URL).
|
||||
/// - **PlexThumb** becomes the tokenized HTTP URL the client can fetch.
|
||||
/// - **Embedded** bytes / **None** have no URL (`None`).
|
||||
pub fn to_mpris_url(&self) -> Option<String> {
|
||||
match self {
|
||||
ArtworkRef::Remote(s) if !s.is_empty() => Some(s.clone()),
|
||||
ArtworkRef::LocalFile(p) if !p.is_empty() => {
|
||||
// Already a file URL? keep it. Otherwise build one (absolute
|
||||
// paths only — `from_file_path` rejects relative, → None).
|
||||
if p.starts_with("file://") {
|
||||
Some(p.clone())
|
||||
} else {
|
||||
url::Url::from_file_path(p).ok().map(|u| u.to_string())
|
||||
}
|
||||
}
|
||||
ArtworkRef::PlexThumb {
|
||||
base_url,
|
||||
token,
|
||||
path,
|
||||
} if !path.is_empty() => {
|
||||
let sep = if path.contains('?') { '&' } else { '?' };
|
||||
Some(format!("{base_url}{path}{sep}X-Plex-Token={token}"))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl QueueTrack {
|
||||
|
||||
@@ -562,15 +562,16 @@ pub(crate) async fn refresh_now_playing_meta(runtime: &Runtime, weak: &slint::We
|
||||
}
|
||||
|
||||
// Push to the OS media controls (MPRIS / SMTC / MediaRemote). The app icon
|
||||
// GNOME shows comes from the MPRIS DesktopEntry, not from album art; art_url
|
||||
// (album art) is a later refinement.
|
||||
// GNOME shows comes from the MPRIS DesktopEntry; `art_url` is the album art
|
||||
// (`mpris:artUrl`) — remote Qobuz covers pass through, local covers become a
|
||||
// file:// URI, matching the Tauri `normalizeCoverUrlForMetadata` workaround.
|
||||
if let Some(mc) = crate::media_controls::handle() {
|
||||
mc.set_metadata(&qbz_media_controls::TrackMeta {
|
||||
title: title.clone(),
|
||||
artist: artist.clone(),
|
||||
album: album.clone(),
|
||||
duration: (duration > 0).then(|| std::time::Duration::from_secs(duration as u64)),
|
||||
art_url: None,
|
||||
art_url: artwork.to_mpris_url(),
|
||||
});
|
||||
mc.set_playback(
|
||||
qbz_media_controls::PlaybackStatus::Playing,
|
||||
|
||||
Reference in new issue
Block a user