From: Fabian Munkes <105975993+fmunkes@users.noreply.github.com>
Date: Wed, 2 Jul 2025 10:10:02 +0000 (+0200)
Subject: fix non-ascii characters in didl_lite metadata (#2256)
X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=3aa5d529abd9bb73c1d93d62d7d3a5528a83fc12;p=music-assistant-server.git
fix non-ascii characters in didl_lite metadata (#2256)
---
diff --git a/music_assistant/helpers/upnp.py b/music_assistant/helpers/upnp.py
index 47041a8c..de289156 100644
--- a/music_assistant/helpers/upnp.py
+++ b/music_assistant/helpers/upnp.py
@@ -111,6 +111,21 @@ def get_xml_soap_set_next_url(player_media: PlayerMedia) -> tuple[str, str]:
# DIDL-LITE
def create_didl_metadata(media: PlayerMedia) -> str:
"""Create DIDL metadata string from url and PlayerMedia."""
+
+ def escape_metadata(data: str) -> str:
+ """Escape didl metadata."""
+ data = xmlescape(data)
+ # Escape non-ascii to decimal code.
+ result = ""
+ for char in data:
+ unicode_code = ord(char)
+ if unicode_code < 128:
+ # ascii
+ result += char
+ else:
+ result += f"{unicode_code};"
+ return result
+
ext = media.uri.split(".")[-1].split("?")[0]
image_url = media.image_url or MASS_LOGO_ONLINE
if media.media_type in (MediaType.FLOW_STREAM, MediaType.RADIO) or not media.duration:
@@ -119,12 +134,12 @@ def create_didl_metadata(media: PlayerMedia) -> str:
return (
''
f'- '
- f"{xmlescape(title)}"
- f"{xmlescape(image_url)}"
- f"{media.uri}"
+ f"{escape_metadata(title)}"
+ f"{escape_metadata(image_url)}"
+ f"{escape_metadata(media.uri)}"
"object.item.audioItem.audioBroadcast"
f"audio/{ext}"
- f'{xmlescape(media.uri)}'
+ f'{escape_metadata(media.uri)}'
"
"
""
)
@@ -135,17 +150,17 @@ def create_didl_metadata(media: PlayerMedia) -> str:
return (
''
f'- '
- f"{xmlescape(media.title or media.uri)}"
- f"{xmlescape(media.artist or '')}"
- f"{xmlescape(media.album or '')}"
- f"{xmlescape(media.artist or '')}"
+ f"{escape_metadata(media.title or media.uri)}"
+ f"{escape_metadata(media.artist or '')}"
+ f"{escape_metadata(media.album or '')}"
+ f"{escape_metadata(media.artist or '')}"
f"{int(media.duration or 0)}"
- f"{xmlescape(media.queue_item_id)}"
+ f"{escape_metadata(media.queue_item_id)}"
f"Music Assistant"
- f"{xmlescape(image_url)}"
+ f"{escape_metadata(image_url)}"
"object.item.audioItem.musicTrack"
f"audio/{ext}"
- f'{xmlescape(media.uri)}'
+ f'{escape_metadata(media.uri)}'
'RINCON_AssociatedZPUDN'
"
"
""