Improve track notifications with artwork and quality info
This commit is contained in:
1 parent
2d908c7b12
commit
bc7e13d756
3 files changed
+123
-14
No files matched your search
@@ -22,7 +22,7 @@ serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
# HTTP Client
|
||||
reqwest = { version = "0.12", features = ["json", "cookies", "rustls-tls", "stream"], default-features = false }
|
||||
reqwest = { version = "0.12", features = ["json", "cookies", "rustls-tls", "stream", "blocking"], default-features = false }
|
||||
|
||||
# Async Runtime
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
@@ -1,28 +1,126 @@
|
||||
//! System notification commands
|
||||
|
||||
use notify_rust::Notification;
|
||||
use md5::{Digest, Md5};
|
||||
use std::path::PathBuf;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
/// Show a track change notification
|
||||
/// Cache directory for notification artwork
|
||||
fn get_artwork_cache_dir() -> Result<PathBuf, String> {
|
||||
let cache_dir = dirs::cache_dir()
|
||||
.ok_or_else(|| "Could not find cache directory".to_string())?
|
||||
.join("qbz")
|
||||
.join("artwork");
|
||||
|
||||
fs::create_dir_all(&cache_dir)
|
||||
.map_err(|e| format!("Failed to create artwork cache dir: {}", e))?;
|
||||
|
||||
Ok(cache_dir)
|
||||
}
|
||||
|
||||
/// Download artwork to cache and return the path
|
||||
fn cache_artwork(url: &str) -> Result<PathBuf, String> {
|
||||
// Create a hash of the URL for the filename
|
||||
let mut hasher = Md5::new();
|
||||
hasher.update(url.as_bytes());
|
||||
let hash = format!("{:x}", hasher.finalize());
|
||||
let cache_dir = get_artwork_cache_dir()?;
|
||||
let cache_path = cache_dir.join(format!("{}.jpg", hash));
|
||||
|
||||
// Return cached file if exists
|
||||
if cache_path.exists() {
|
||||
return Ok(cache_path);
|
||||
}
|
||||
|
||||
// Download the image
|
||||
let response = reqwest::blocking::Client::new()
|
||||
.get(url)
|
||||
.timeout(std::time::Duration::from_secs(5))
|
||||
.send()
|
||||
.map_err(|e| format!("Failed to download artwork: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("Failed to download artwork: HTTP {}", response.status()));
|
||||
}
|
||||
|
||||
let bytes = response.bytes()
|
||||
.map_err(|e| format!("Failed to read artwork bytes: {}", e))?;
|
||||
|
||||
// Write to cache
|
||||
let mut file = fs::File::create(&cache_path)
|
||||
.map_err(|e| format!("Failed to create artwork cache file: {}", e))?;
|
||||
file.write_all(&bytes)
|
||||
.map_err(|e| format!("Failed to write artwork cache: {}", e))?;
|
||||
|
||||
Ok(cache_path)
|
||||
}
|
||||
|
||||
/// Format quality info for notification
|
||||
fn format_quality(bit_depth: Option<u32>, sample_rate: Option<u32>) -> String {
|
||||
match (bit_depth, sample_rate) {
|
||||
(Some(bits), Some(rate)) if bits >= 24 || rate > 48 => {
|
||||
format!("Hi-Res • {}-bit/{}kHz", bits, rate)
|
||||
}
|
||||
(Some(bits), Some(rate)) => {
|
||||
format!("CD Quality • {}-bit/{}kHz", bits, rate)
|
||||
}
|
||||
_ => String::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Show a track change notification with artwork
|
||||
#[tauri::command]
|
||||
pub fn show_track_notification(
|
||||
title: String,
|
||||
artist: String,
|
||||
album: String,
|
||||
_artwork_url: Option<String>,
|
||||
artwork_url: Option<String>,
|
||||
bit_depth: Option<u32>,
|
||||
sample_rate: Option<u32>,
|
||||
) -> Result<(), String> {
|
||||
log::info!("Command: show_track_notification - {} by {}", title, artist);
|
||||
|
||||
let body = if album.is_empty() {
|
||||
artist.clone()
|
||||
} else {
|
||||
format!("{} • {}", artist, album)
|
||||
};
|
||||
// Build body with artist, album, and quality
|
||||
let mut body_parts = Vec::new();
|
||||
|
||||
Notification::new()
|
||||
if !artist.is_empty() {
|
||||
body_parts.push(artist.clone());
|
||||
}
|
||||
if !album.is_empty() {
|
||||
body_parts.push(album.clone());
|
||||
}
|
||||
|
||||
let quality = format_quality(bit_depth, sample_rate);
|
||||
if !quality.is_empty() {
|
||||
body_parts.push(quality);
|
||||
}
|
||||
|
||||
let body = body_parts.join(" • ");
|
||||
|
||||
let mut notification = Notification::new();
|
||||
notification
|
||||
.summary(&title)
|
||||
.body(&body)
|
||||
.appname("QBZ")
|
||||
.timeout(3000) // 3 seconds
|
||||
.timeout(4000); // 4 seconds
|
||||
|
||||
// Try to add artwork
|
||||
if let Some(url) = artwork_url {
|
||||
match cache_artwork(&url) {
|
||||
Ok(path) => {
|
||||
if let Some(path_str) = path.to_str() {
|
||||
notification.image_path(path_str);
|
||||
log::debug!("Notification artwork set: {}", path_str);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Could not cache artwork for notification: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notification
|
||||
.show()
|
||||
.map_err(|e| format!("Failed to show notification: {}", e))?;
|
||||
|
||||
|
||||
Reference in new issue
Block a user