From ebbb63cd080dd9510a43925f611cdde3f672c809 Mon Sep 17 00:00:00 2001 From: Pete Sage <76050312+PeteRager@users.noreply.github.com> Date: Wed, 19 Jun 2024 17:38:49 -0400 Subject: [PATCH] Fix Sonos album images with special characters not displaying in media browser UI (#118249) * initial commit * initial commit * simplify tests * rename symbol * original_uri -> original_url * change symbol name --------- Co-authored-by: Dave T <17680170+davet2001@users.noreply.github.com> --- .../components/sonos/media_browser.py | 28 ++++++++++++++++++- .../sonos/fixtures/music_library_albums.json | 7 +++++ .../sonos/snapshots/test_media_browser.ambr | 12 +++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sonos/media_browser.py b/homeassistant/components/sonos/media_browser.py index 3416896e879..995d6cea08c 100644 --- a/homeassistant/components/sonos/media_browser.py +++ b/homeassistant/components/sonos/media_browser.py @@ -46,6 +46,32 @@ _LOGGER = logging.getLogger(__name__) type GetBrowseImageUrlType = Callable[[str, str, str | None], str] +def fix_image_url(url: str) -> str: + """Update the image url to fully encode characters to allow image display in media_browser UI. + + Images whose file path contains characters such as ',()+ are not loaded without escaping them. + """ + + # Before parsing encode the plus sign; otherwise it'll be interpreted as a space. + original_url: str = urllib.parse.unquote(url).replace("+", "%2B") + parsed_url = urllib.parse.urlparse(original_url) + query_params = urllib.parse.parse_qsl(parsed_url.query) + new_url = urllib.parse.urlunsplit( + ( + parsed_url.scheme, + parsed_url.netloc, + parsed_url.path, + urllib.parse.urlencode( + query_params, quote_via=urllib.parse.quote, safe="/:" + ), + "", + ) + ) + if original_url != new_url: + _LOGGER.debug("fix_sonos_image_url original: %s new: %s", original_url, new_url) + return new_url + + def get_thumbnail_url_full( media: SonosMedia, is_internal: bool, @@ -63,7 +89,7 @@ def get_thumbnail_url_full( media_content_id, media_content_type, ) - return urllib.parse.unquote(getattr(item, "album_art_uri", "")) + return fix_image_url(getattr(item, "album_art_uri", "")) return urllib.parse.unquote( get_browse_image_url( diff --git a/tests/components/sonos/fixtures/music_library_albums.json b/tests/components/sonos/fixtures/music_library_albums.json index 4941abe8ba7..24ee386e338 100644 --- a/tests/components/sonos/fixtures/music_library_albums.json +++ b/tests/components/sonos/fixtures/music_library_albums.json @@ -19,5 +19,12 @@ "parent_id": "A:ALBUM", "item_class": "object.container.album.musicAlbum", "album_art_uri": "http://192.168.42.2:1400/getaa?u=x-file-cifs%3a%2f%2f192.168.42.100%2fmusic%2fSantana%2fA%2520Between%2520Good%2520And%2520Evil%2f02%2520A%2520Persuasion.m4a&v=53" + }, + { + "title": "Special Characters,'()+", + "item_id": "A:ALBUM/Special%20Characters,'()+", + "parent_id": "A:ALBUM", + "item_class": "object.container.album.musicAlbum", + "album_art_uri": "http://192.168.42.2:1400/getaa?u=x-file-cifs%3a%2f%2f192.168.42.100%2fmusic%2fSpecial%2fA%2520Special%2520Characters,()+%2f01%2520A%2520TheFirstTrack.m4a&v=53" } ] diff --git a/tests/components/sonos/snapshots/test_media_browser.ambr b/tests/components/sonos/snapshots/test_media_browser.ambr index b4388b148e5..ae8e813ae5d 100644 --- a/tests/components/sonos/snapshots/test_media_browser.ambr +++ b/tests/components/sonos/snapshots/test_media_browser.ambr @@ -82,7 +82,7 @@ 'media_class': 'album', 'media_content_id': "A:ALBUM/A%20Hard%20Day's%20Night", 'media_content_type': 'album', - 'thumbnail': "http://192.168.42.2:1400/getaa?u=x-file-cifs://192.168.42.100/music/The%20Beatles/A%20Hard%20Day's%20Night/01%20A%20Hard%20Day's%20Night%201.m4a&v=53", + 'thumbnail': 'http://192.168.42.2:1400/getaa?u=x-file-cifs://192.168.42.100/music/The%20Beatles/A%20Hard%20Day%27s%20Night/01%20A%20Hard%20Day%27s%20Night%201.m4a&v=53', 'title': "A Hard Day's Night", }), dict({ @@ -105,6 +105,16 @@ 'thumbnail': 'http://192.168.42.2:1400/getaa?u=x-file-cifs://192.168.42.100/music/Santana/A%20Between%20Good%20And%20Evil/02%20A%20Persuasion.m4a&v=53', 'title': 'Between Good And Evil', }), + dict({ + 'can_expand': True, + 'can_play': True, + 'children_media_class': None, + 'media_class': 'album', + 'media_content_id': "A:ALBUM/Special%20Characters,'()+", + 'media_content_type': 'album', + 'thumbnail': 'http://192.168.42.2:1400/getaa?u=x-file-cifs://192.168.42.100/music/Special/A%20Special%20Characters%2C%28%29%2B/01%20A%20TheFirstTrack.m4a&v=53', + 'title': "Special Characters,'()+", + }), ]) # --- # name: test_browse_media_root