You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Many LinkPlay devices don't expose album art through the HTTP API. On these
devices getMetaInfo returns unknown command and getPlayerStatus(Ex) has
no art field, so player.album_art is always empty and downstream consumers
(e.g. the Home Assistant integration) show no cover.
The art is available through the device's UPnP AVTransport service. This adds a
fallback: when album_art is empty after the normal HTTP update, it queries AVTransport.GetMediaInfo and reads albumArtURI from the returned DIDL-Lite
metadata, storing it in the existing metainfo structure so album_art keeps
working unchanged.
Details
New linkplay/upnp.py with a small LinkPlayUPnP helper built on async_upnp_client (already an install dependency). The UpnpDevice is
created once and reused.
The trigger is manufacturer-agnostic: it fires whenever album art is missing,
so it also helps WiiM devices that occasionally omit it, rather than
hard-coding a WiiM / non-WiiM split.
The DIDL is parsed with defusedxml (added to install_requires) to avoid
XXE and entity-expansion attacks on the device response.
Non-URL placeholders (devices report un_known when nothing is playing or on
followers) are validated away to None.
Adds host and session accessors on LinkPlayApiEndpoint for the UPnP
client.
Testing
New tests/linkplay/test_upnp.py covers DIDL parsing (a real Spotify albumArtURI, the un_known placeholder, and malformed input) and the GetMediaInfo paths.
New bridge tests assert album art is fetched via UPnP when missing and that
UPnP is skipped when the HTTP API already provides it.
ruff, mypy and the full test suite pass.
Verified against real hardware: Arylic Up2Stream Pro V4 and Audio Pro C10, both
playing Spotify, where the Spotify cover URL is exposed via albumArtURI.
Scope
Relates to #68. This intentionally does not close it: using UPnP as a full
replacement for devices that don't support getPlayerStatusEx at all (transport
state, position, volume via RenderingControl) is a larger change. The
properties dict would be empty and the numeric and enum accessors (int(""), LoopMode("")) would raise, so it needs seeded defaults and additional UPnP
actions. That is left as a follow-up. This builds on the exploration in the
draft #102 by @silamon.
Many LinkPlay devices (e.g. Arylic and Audio Pro models) do not expose
album art through the HTTP API; the art is only available through the
device's UPnP AVTransport service. When getPlayerStatusEx does not provide
album art, fall back to UPnP GetMediaInfo and read the albumArtURI, keeping
the value in the existing metainfo structure so album_art works unchanged.
The fallback is manufacturer-agnostic: it triggers whenever album art is
missing, which also helps WiiM devices that occasionally omit it. Non-URL
placeholders (e.g. "un_known") are discarded.
Relates to Velleman#68. UPnP as a full replacement for devices without
getPlayerStatusEx (including transport state and volume) is left as a
follow-up. Builds on the exploration in the draft Velleman#102.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Many LinkPlay devices don't expose album art through the HTTP API. On these
devices
getMetaInforeturnsunknown commandandgetPlayerStatus(Ex)hasno art field, so
player.album_artis always empty and downstream consumers(e.g. the Home Assistant integration) show no cover.
The art is available through the device's UPnP AVTransport service. This adds a
fallback: when
album_artis empty after the normal HTTP update, it queriesAVTransport.GetMediaInfoand readsalbumArtURIfrom the returned DIDL-Litemetadata, storing it in the existing
metainfostructure soalbum_artkeepsworking unchanged.
Details
linkplay/upnp.pywith a smallLinkPlayUPnPhelper built onasync_upnp_client(already an install dependency). TheUpnpDeviceiscreated once and reused.
so it also helps WiiM devices that occasionally omit it, rather than
hard-coding a WiiM / non-WiiM split.
defusedxml(added toinstall_requires) to avoidXXE and entity-expansion attacks on the device response.
un_knownwhen nothing is playing or onfollowers) are validated away to
None.hostandsessionaccessors onLinkPlayApiEndpointfor the UPnPclient.
Testing
tests/linkplay/test_upnp.pycovers DIDL parsing (a real SpotifyalbumArtURI, theun_knownplaceholder, and malformed input) and theGetMediaInfopaths.UPnP is skipped when the HTTP API already provides it.
ruff,mypyand the full test suite pass.Verified against real hardware: Arylic Up2Stream Pro V4 and Audio Pro C10, both
playing Spotify, where the Spotify cover URL is exposed via
albumArtURI.Scope
Relates to #68. This intentionally does not close it: using UPnP as a full
replacement for devices that don't support
getPlayerStatusExat all (transportstate, position, volume via
RenderingControl) is a larger change. Theproperties dict would be empty and the numeric and enum accessors (
int(""),LoopMode("")) would raise, so it needs seeded defaults and additional UPnPactions. That is left as a follow-up. This builds on the exploration in the
draft #102 by @silamon.