fix(media-controls): decode notification artwork by content, not extension
image::open resolves the format from the path extension only (no content
sniffing in image 0.25), so every notification artwork served from the
shared image cache - files named {md5}.img since slice 3b/B11 - failed as
Unsupported and the toast fired icon-less on online Qobuz plays. Decode
via fs::read + image::load_from_memory (magic bytes, extension-agnostic),
which also heals mislabeled downloads in the md5 cache. file:// paths now
percent-decode (cached_file_url_for builds them via Url::from_file_path),
fixing the latent spaces/non-ASCII fall-through to an HTTP GET of a
file:// URL. Offline semantics unchanged.
cc
This commit is contained in:
1 file changed
+17
-2
@@ -102,7 +102,14 @@ fn artwork_cache_dir() -> Result<PathBuf, String> {
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
fn resolve_local_artwork(url: &str) -> Option<PathBuf> {
|
||||
if let Some(path) = url.strip_prefix("file://") {
|
||||
return Some(PathBuf::from(path));
|
||||
// file:// URLs built with url::Url::from_file_path (e.g. the shared
|
||||
// disk-image cache hits handed over by playback) are percent-encoded;
|
||||
// decode so paths with spaces/non-ASCII resolve. Fall back to the raw
|
||||
// string on invalid UTF-8 escapes (a plain unencoded path).
|
||||
let decoded = urlencoding::decode(path)
|
||||
.map(|c| c.into_owned())
|
||||
.unwrap_or_else(|_| path.to_string());
|
||||
return Some(PathBuf::from(decoded));
|
||||
}
|
||||
if let Some(path) = url.strip_prefix("asset://localhost/") {
|
||||
let decoded = urlencoding::decode(path).ok()?;
|
||||
@@ -184,11 +191,19 @@ const PORTAL_ICON_MAX_BYTES: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Center-crop to a square, downscale to <=512px, re-encode PNG. Mirrors the
|
||||
/// Tauri `v2_prepare_notification_icon_bytes`.
|
||||
///
|
||||
/// Decodes by CONTENT, never by extension: the shared disk-image cache names
|
||||
/// files `{md5}.img` (no real image extension), and `image::open` resolves the
|
||||
/// format from the path extension only — it returned `Unsupported` for every
|
||||
/// cache hit, which is exactly the common online case.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn prepare_icon_bytes(path: &std::path::Path) -> Result<Vec<u8>, String> {
|
||||
use std::io::Cursor;
|
||||
|
||||
let source = image::open(path).map_err(|e| format!("Failed to decode artwork {path:?}: {e}"))?;
|
||||
let bytes =
|
||||
std::fs::read(path).map_err(|e| format!("Failed to read artwork {path:?}: {e}"))?;
|
||||
let source = image::load_from_memory(&bytes)
|
||||
.map_err(|e| format!("Failed to decode artwork {path:?}: {e}"))?;
|
||||
let (w, h) = (source.width(), source.height());
|
||||
let square = if w == h {
|
||||
source
|
||||
|
||||
Reference in new issue
Block a user