4.8 KiB
4.8 KiB
BrowseMetadata Fix for Radio Paradise - PMO Music
Date: 2025-11-27 Issue: gupnp-av-cp failed to get metadata for live streams and history containers
Problem
UPnP clients (like gupnp-av-cp) were unable to get metadata for:
- Live stream items (e.g.,
radio-paradise:channel:mellow:live) - History containers (e.g.,
radio-paradise:channel:mellow:history)
Error:
Failed to get metadata for 'radio-paradise:channel:mellow:live'
Failed to get metadata for 'radio-paradise:channel:mellow:history'
Root Cause
The UPnP ContentDirectory service has two browse modes:
- BrowseMetadata: Get metadata for a specific object (item or container)
- BrowseDirectChildren: Get the children of a container
The ContentHandler::browse_metadata() was calling source.browse() for all objects, but:
- The
MusicSource::browse()trait method is designed to return children, not object metadata - For leaf items (LiveStream, HistoryTrack),
RadioParadiseSource::browse()was rejecting them as "cannot be browsed" - The trait doesn't provide a way to distinguish between BrowseMetadata and BrowseDirectChildren requests
Solution
1. Modified ContentHandler (pmomediaserver/src/content_handler.rs)
browse_metadata()now triesget_item()first for leaf items before falling back tobrowse()- This allows proper metadata retrieval for items (LiveStream, HistoryTrack)
2. Modified RadioParadiseSource (pmoparadise/src/source.rs)
For LiveStream items:
browse()now returnsBrowseResult::Items([live_item])with the item's metadata- This supports both BrowseMetadata (via ContentHandler) and direct browse calls
For HistoryTrack items:
browse()now returnsBrowseResult::Items([track])usingget_item()internally- Properly retrieves track metadata from the history playlist
For History containers:
browse()now returnsBrowseResult::Mixed { containers: [history_container], items: [tracks] }- Provides both container metadata and its children in one result
3. Added Container Filtering (pmomediaserver/src/content_handler.rs)
browse_result_to_didl()now filters out containers that match the browsedobject_id- Prevents containers from appearing as children of themselves
- For History: BrowseDirectChildren returns only tracks, not the container
Files Modified
-
✅ pmomediaserver/src/content_handler.rs
- Lines 120-135: Try get_item() first in browse_metadata()
- Lines 290-310: Added object_id parameter and container filtering in browse_result_to_didl()
- Lines 212, 286: Updated callers to pass object_id
-
- Lines 345-369: Modified History browse to return Mixed (container + items)
- Lines 371-377: Modified LiveStream browse to return item metadata
- Lines 379-383: Modified HistoryTrack browse to return track metadata
Validation
Live Stream Metadata ✅
curl -X POST -H "SOAPAction: ..." BrowseMetadata radio-paradise:channel:mellow:live
Returns:
<item id="radio-paradise:channel:mellow:live" parentID="radio-paradise:channel:mellow">
<dc:title>Unknown Title</dc:title>
<upnp:class>object.item.audioItem.audioBroadcast</upnp:class>
<res protocolInfo="http-get:*:audio/flac:*">http://.../radioparadise/stream/mellow/flac</res>
</item>
History Container Metadata ✅
curl -X POST -H "SOAPAction: ..." BrowseMetadata radio-paradise:channel:mellow:history
Returns:
<container id="radio-paradise:channel:mellow:history" parentID="radio-paradise:channel:mellow">
<dc:title>Mellow Mix - History</dc:title>
<upnp:class>object.container.playlistContainer</upnp:class>
</container>
History Children ✅
curl -X POST -H "SOAPAction: ..." BrowseDirectChildren radio-paradise:channel:mellow:history
Returns only track items (not the container itself)
Design Notes
This solution works around a fundamental limitation in the MusicSource trait:
- The
browse()method doesn't receive thebrowse_flagparameter - It can't distinguish between BrowseMetadata and BrowseDirectChildren
- We use
get_item()for items andbrowse()for containers - Container filtering ensures correct BrowseDirectChildren behavior
Testing Checklist
- BrowseMetadata works for LiveStream items
- BrowseMetadata works for History containers
- BrowseDirectChildren works for History (returns only tracks)
- Container filtering prevents self-reference
- Test with BubbleUPnP (user to verify)
- Test with gupnp-av-cp (user to verify)
References
- Original issue report: UPNP_FIX_SUMMARY.md
- UPnP AV Architecture: https://openconnectivity.org/developer/specifications/upnp-resources/upnp/