Chore: some (sonos) tweaks to didl metadata helper
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 30 Mar 2025 22:35:10 +0000 (00:35 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 30 Mar 2025 22:35:10 +0000 (00:35 +0200)
music_assistant/helpers/didl_lite.py

index 5b44a254c276162d51084a2518274e96fb2524cf..2e0738e2adb6127f48a58ba48784b48abd603e70 100644 (file)
@@ -4,6 +4,7 @@ from __future__ import annotations
 
 import datetime
 from typing import TYPE_CHECKING
+from xml.sax.saxutils import escape as xmlescape
 
 from music_assistant_models.enums import MediaType
 
@@ -25,38 +26,37 @@ def create_didl_metadata(media: PlayerMedia) -> str:
         return (
             '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">'
             f'<item id="flowmode" parentID="0" restricted="1">'
-            f"<dc:title>{escape_string(title)}</dc:title>"
-            f"<upnp:albumArtURI>{escape_string(image_url)}</upnp:albumArtURI>"
+            f"<dc:title>{xmlescape(title)}</dc:title>"
+            f"<upnp:albumArtURI>{xmlescape(image_url)}</upnp:albumArtURI>"
             f"<dc:queueItemId>{media.uri}</dc:queueItemId>"
             "<upnp:class>object.item.audioItem.audioBroadcast</upnp:class>"
             f"<upnp:mimeType>audio/{ext}</upnp:mimeType>"
-            f'<res protocolInfo="http-get:*:audio/{ext}:DLNA.ORG_PN={ext.upper()};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000">{escape_string(media.uri)}</res>'
+            f'<res protocolInfo="http-get:*:audio/{ext}:DLNA.ORG_PN={ext.upper()};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000">{xmlescape(media.uri)}</res>'
             "</item>"
             "</DIDL-Lite>"
         )
     duration_str = str(datetime.timedelta(seconds=media.duration or 0)) + ".000"
+
     return (
         '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/">'
-        f'<item id="{media.queue_item_id or media.uri}" restricted="true">'
-        f"<dc:title>{escape_string(media.title or media.uri)}</dc:title>"
-        f"<dc:creator>{escape_string(media.artist or '')}</dc:creator>"
-        f"<upnp:album>{escape_string(media.album or '')}</upnp:album>"
-        f"<upnp:artist>{escape_string(media.artist or '')}</upnp:artist>"
+        f'<item id="{media.queue_item_id or xmlescape(media.uri)}" restricted="true" parentID="{media.queue_id or ""}">'
+        f"<dc:title>{xmlescape(media.title or media.uri)}</dc:title>"
+        f"<dc:creator>{xmlescape(media.artist or '')}</dc:creator>"
+        f"<upnp:album>{xmlescape(media.album or '')}</upnp:album>"
+        f"<upnp:artist>{xmlescape(media.artist or '')}</upnp:artist>"
         f"<upnp:duration>{int(media.duration or 0)}</upnp:duration>"
-        f"<dc:queueItemId>{media.uri}</dc:queueItemId>"
+        f"<dc:queueItemId>{xmlescape(media.queue_item_id)}</dc:queueItemId>"
         f"<dc:description>Music Assistant</dc:description>"
-        f"<upnp:albumArtURI>{escape_string(image_url)}</upnp:albumArtURI>"
+        f"<upnp:albumArtURI>{xmlescape(image_url)}</upnp:albumArtURI>"
         "<upnp:class>object.item.audioItem.musicTrack</upnp:class>"
         f"<upnp:mimeType>audio/{ext}</upnp:mimeType>"
-        f'<res duration="{duration_str}" protocolInfo="http-get:*:audio/{ext}:DLNA.ORG_PN={ext.upper()};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000">{escape_string(media.uri)}</res>'
+        f'<res duration="{duration_str}" protocolInfo="http-get:*:audio/{ext}:DLNA.ORG_PN={ext.upper()};DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=0d500000000000000000000000000000">{xmlescape(media.uri)}</res>'
+        '<desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">RINCON_AssociatedZPUDN</desc>'
         "</item>"
         "</DIDL-Lite>"
     )
 
 
-def escape_string(data: str) -> str:
-    """Create DIDL-safe string."""
-    data = data.replace("&", "&amp;")
-    # data = data.replace("?", "&#63;")
-    data = data.replace(">", "&gt;")
-    return data.replace("<", "&lt;")
+def create_didl_metadata_str(media: PlayerMedia) -> str:
+    """Create (xml-escaped) DIDL metadata string from url and PlayerMedia."""
+    return xmlescape(create_didl_metadata(media))