diff --git a/android/app/src/main/java/com/fabledsword/minstrel/player/output/upnp/AVTransportClient.kt b/android/app/src/main/java/com/fabledsword/minstrel/player/output/upnp/AVTransportClient.kt index 61e2a2ad..11dbbe58 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/player/output/upnp/AVTransportClient.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/player/output/upnp/AVTransportClient.kt @@ -204,22 +204,29 @@ class AVTransportClient( } private fun buildDidlLite(uri: String, mime: String, title: String): String { - // Sonos firmware accepts boolean-style restricted ("true"/"false") only - // and rejects "1"/"0" silently; if it can't parse our DIDL it regenerates - // its own with generic class object.item, no title, and the URL query - // string as the displayed name (logcat 2026-06-04 confirmed). Match the - // SoCo convention which the firmware definitely accepts. + // Sonos requires (a) the rinconnetworks namespace declared on + // even if we don't use Rincon elements directly, and + // (b) a element identifying the URI as an + // external (non-Sonos-library) source. Without those, Sonos + // discards our metadata content and regenerates its own with + // class=object.item and the URL query string as the title + // (logcat 2026-06-04 confirmed). Match SoCo's pattern. val safeTitle = title.ifBlank { "Minstrel" } return buildString { append("") + append("xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" ") + append("xmlns:r=\"urn:schemas-rinconnetworks-com:metadata-1-0/\">") append("") append("").append(xmlEscape(safeTitle)).append("") append("object.item.audioItem.musicTrack") append("").append(xmlEscape(uri)).append("") + append("") + append("RINCON_AssociatedZPUDN") + append("") append("") append("") }