Add cover URL support in ICY metadata via StreamUrl field
Enhances ICY metadata streaming to include cover artwork URLs, enabling
media players to display album art while streaming.
Changes:
- Add cover_pk field to MetadataSnapshot (cache primary key)
- Extract cover_pk in update_metadata() alongside cover_url
- Format ICY metadata with StreamUrl field pointing to cover image:
* If cover_pk exists: /covers/image/{pk}/256 (local cache, 256px)
* Fallback to cover_url if no local cache (external URL)
- Use relative URLs for compatibility with same-origin streaming
ICY format example:
StreamTitle='AC/DC - Highway to Hell';StreamUrl='/covers/image/abc123/256';
This works seamlessly with pmocovers which serves images at:
GET /covers/image/{pk} - Original WebP
GET /covers/image/{pk}/256 - 256px variant (used in ICY)
Relative URLs are resolved correctly by VLC and other ICY-compatible players
when streaming from the same server that serves covers.
This commit is contained in:
@@ -101,9 +101,12 @@ pub struct MetadataSnapshot {
|
||||
/// Track duration
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub duration: Option<Duration>,
|
||||
/// Cover image URL
|
||||
/// Cover image URL (external/original)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cover_url: Option<String>,
|
||||
/// Cover primary key in local cache (for constructing server URL)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cover_pk: Option<String>,
|
||||
/// Track number
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub track_number: Option<u32>,
|
||||
@@ -287,10 +290,27 @@ impl IcyClientStream {
|
||||
///
|
||||
/// ICY format: StreamTitle='Artist - Title';StreamUrl='url';
|
||||
/// Padded to multiple of 16 bytes, prefixed with length byte.
|
||||
///
|
||||
/// If cover_pk is available, constructs a URL for the cover image:
|
||||
/// - If pmoserver is initialized: http://server/covers/image/{pk}/256
|
||||
/// - Otherwise: relative URL /covers/image/{pk}/256
|
||||
fn format_icy_metadata(meta: &MetadataSnapshot) -> Bytes {
|
||||
let title = meta.title.as_deref().unwrap_or("Unknown");
|
||||
let artist = meta.artist.as_deref().unwrap_or("Unknown Artist");
|
||||
let metadata_str = format!("StreamTitle='{} - {}';", artist, title);
|
||||
|
||||
// Build ICY metadata string with cover URL if available
|
||||
let mut metadata_str = format!("StreamTitle='{} - {}';", artist, title);
|
||||
|
||||
// Add cover URL if we have a cover_pk
|
||||
if let Some(pk) = &meta.cover_pk {
|
||||
// Use relative URL /covers/image/{pk}/256
|
||||
// This works when streaming from the same server that serves covers
|
||||
// VLC and other players will resolve relative URLs correctly
|
||||
metadata_str.push_str(&format!("StreamUrl='/covers/image/{}/256';", pk));
|
||||
} else if let Some(url) = &meta.cover_url {
|
||||
// Fallback to external cover URL if no local pk
|
||||
metadata_str.push_str(&format!("StreamUrl='{}';", url));
|
||||
}
|
||||
|
||||
// ICY metadata is padded to multiple of 16 bytes
|
||||
let metadata_bytes = metadata_str.as_bytes();
|
||||
@@ -494,6 +514,7 @@ impl StreamingFlacSinkLogic {
|
||||
snapshot.album = metadata.get_album().await.ok();
|
||||
snapshot.duration = metadata.get_duration().await.ok();
|
||||
snapshot.cover_url = metadata.get_cover_url().await.ok();
|
||||
snapshot.cover_pk = metadata.get_cover_pk().await.ok();
|
||||
snapshot.album_artist = metadata.get_album_artist().await.ok();
|
||||
snapshot.year = metadata.get_year().await.ok();
|
||||
|
||||
@@ -509,11 +530,12 @@ impl StreamingFlacSinkLogic {
|
||||
snapshot.version += 1;
|
||||
|
||||
debug!(
|
||||
"Metadata updated: v{} @ {:.2}s - {} - {}",
|
||||
"Metadata updated: v{} @ {:.2}s - {} - {} (cover_pk: {:?})",
|
||||
snapshot.version,
|
||||
timestamp_sec,
|
||||
snapshot.artist.as_deref().unwrap_or("?"),
|
||||
snapshot.title.as_deref().unwrap_or("?")
|
||||
snapshot.title.as_deref().unwrap_or("?"),
|
||||
snapshot.cover_pk
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user