Fix Sonos album artwork performance (#116391)
This commit is contained in:
parent
5c263b039e
commit
1ad2e4951d
7 changed files with 344 additions and 9 deletions
|
@ -53,14 +53,16 @@ def get_thumbnail_url_full(
|
||||||
media_content_type: str,
|
media_content_type: str,
|
||||||
media_content_id: str,
|
media_content_id: str,
|
||||||
media_image_id: str | None = None,
|
media_image_id: str | None = None,
|
||||||
|
item: MusicServiceItem | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Get thumbnail URL."""
|
"""Get thumbnail URL."""
|
||||||
if is_internal:
|
if is_internal:
|
||||||
item = get_media(
|
if not item:
|
||||||
media.library,
|
item = get_media(
|
||||||
media_content_id,
|
media.library,
|
||||||
media_content_type,
|
media_content_id,
|
||||||
)
|
media_content_type,
|
||||||
|
)
|
||||||
return urllib.parse.unquote(getattr(item, "album_art_uri", ""))
|
return urllib.parse.unquote(getattr(item, "album_art_uri", ""))
|
||||||
|
|
||||||
return urllib.parse.unquote(
|
return urllib.parse.unquote(
|
||||||
|
@ -255,7 +257,7 @@ def item_payload(item: DidlObject, get_thumbnail_url=None) -> BrowseMedia:
|
||||||
content_id = get_content_id(item)
|
content_id = get_content_id(item)
|
||||||
thumbnail = None
|
thumbnail = None
|
||||||
if getattr(item, "album_art_uri", None):
|
if getattr(item, "album_art_uri", None):
|
||||||
thumbnail = get_thumbnail_url(media_class, content_id)
|
thumbnail = get_thumbnail_url(media_class, content_id, item=item)
|
||||||
|
|
||||||
return BrowseMedia(
|
return BrowseMedia(
|
||||||
title=item.title,
|
title=item.title,
|
||||||
|
|
|
@ -316,12 +316,35 @@ def sonos_favorites_fixture() -> SearchResult:
|
||||||
class MockMusicServiceItem:
|
class MockMusicServiceItem:
|
||||||
"""Mocks a Soco MusicServiceItem."""
|
"""Mocks a Soco MusicServiceItem."""
|
||||||
|
|
||||||
def __init__(self, title: str, item_id: str, parent_id: str, item_class: str):
|
def __init__(
|
||||||
|
self,
|
||||||
|
title: str,
|
||||||
|
item_id: str,
|
||||||
|
parent_id: str,
|
||||||
|
item_class: str,
|
||||||
|
album_art_uri: None | str = None,
|
||||||
|
):
|
||||||
"""Initialize the mock item."""
|
"""Initialize the mock item."""
|
||||||
self.title = title
|
self.title = title
|
||||||
self.item_id = item_id
|
self.item_id = item_id
|
||||||
self.item_class = item_class
|
self.item_class = item_class
|
||||||
self.parent_id = parent_id
|
self.parent_id = parent_id
|
||||||
|
self.album_art_uri: None | str = album_art_uri
|
||||||
|
|
||||||
|
|
||||||
|
def list_from_json_fixture(file_name: str) -> list[MockMusicServiceItem]:
|
||||||
|
"""Create a list of music service items from a json fixture file."""
|
||||||
|
item_list = load_json_value_fixture(file_name, "sonos")
|
||||||
|
return [
|
||||||
|
MockMusicServiceItem(
|
||||||
|
item.get("title"),
|
||||||
|
item.get("item_id"),
|
||||||
|
item.get("parent_id"),
|
||||||
|
item.get("item_class"),
|
||||||
|
item.get("album_art_uri"),
|
||||||
|
)
|
||||||
|
for item in item_list
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def mock_browse_by_idstring(
|
def mock_browse_by_idstring(
|
||||||
|
@ -398,6 +421,10 @@ def mock_browse_by_idstring(
|
||||||
"object.container.album.musicAlbum",
|
"object.container.album.musicAlbum",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
if search_type == "tracks":
|
||||||
|
return list_from_json_fixture("music_library_tracks.json")
|
||||||
|
if search_type == "albums" and idstring == "A:ALBUM":
|
||||||
|
return list_from_json_fixture("music_library_albums.json")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@ -416,13 +443,23 @@ def mock_get_music_library_information(
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="music_library_browse_categories")
|
||||||
|
def music_library_browse_categories() -> list[MockMusicServiceItem]:
|
||||||
|
"""Create fixture for top-level music library categories."""
|
||||||
|
return list_from_json_fixture("music_library_categories.json")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="music_library")
|
@pytest.fixture(name="music_library")
|
||||||
def music_library_fixture(sonos_favorites: SearchResult) -> Mock:
|
def music_library_fixture(
|
||||||
|
sonos_favorites: SearchResult,
|
||||||
|
music_library_browse_categories: list[MockMusicServiceItem],
|
||||||
|
) -> Mock:
|
||||||
"""Create music_library fixture."""
|
"""Create music_library fixture."""
|
||||||
music_library = MagicMock()
|
music_library = MagicMock()
|
||||||
music_library.get_sonos_favorites.return_value = sonos_favorites
|
music_library.get_sonos_favorites.return_value = sonos_favorites
|
||||||
music_library.browse_by_idstring = mock_browse_by_idstring
|
music_library.browse_by_idstring = Mock(side_effect=mock_browse_by_idstring)
|
||||||
music_library.get_music_library_information = mock_get_music_library_information
|
music_library.get_music_library_information = mock_get_music_library_information
|
||||||
|
music_library.browse = Mock(return_value=music_library_browse_categories)
|
||||||
return music_library
|
return music_library
|
||||||
|
|
||||||
|
|
||||||
|
|
23
tests/components/sonos/fixtures/music_library_albums.json
Normal file
23
tests/components/sonos/fixtures/music_library_albums.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "A Hard Day's Night",
|
||||||
|
"item_id": "A:ALBUM/A%20Hard%20Day's%20Night",
|
||||||
|
"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%2fThe%2520Beatles%2fA%2520Hard%2520Day's%2520Night%2f01%2520A%2520Hard%2520Day's%2520Night%25201.m4a&v=53"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Abbey Road",
|
||||||
|
"item_id": "A:ALBUM/Abbey%20Road",
|
||||||
|
"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%2fThe%2520Beatles%2fAbbeyA%2520Road%2f01%2520Come%2520Together.m4a&v=53"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Between Good And Evil",
|
||||||
|
"item_id": "A:ALBUM/Between%20Good%20And%20Evil",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,44 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "Contributing Artists",
|
||||||
|
"item_id": "A:ARTIST",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Artists",
|
||||||
|
"item_id": "A:ALBUMARTIST",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Albums",
|
||||||
|
"item_id": "A:ALBUM",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Genres",
|
||||||
|
"item_id": "A:GENRE",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Composers",
|
||||||
|
"item_id": "A:COMPOSER",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Tracks",
|
||||||
|
"item_id": "A:TRACKS",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container.playlistContainer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Playlists",
|
||||||
|
"item_id": "A:PLAYLISTS",
|
||||||
|
"parent_id": "A:",
|
||||||
|
"item_class": "object.container"
|
||||||
|
}
|
||||||
|
]
|
14
tests/components/sonos/fixtures/music_library_tracks.json
Normal file
14
tests/components/sonos/fixtures/music_library_tracks.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "A Hard Day's Night",
|
||||||
|
"item_id": "S://192.168.42.100/music/iTunes/The%20Beatles/A%20Hard%20Day%2fs%20Night/A%20Hard%20Day%2fs%20Night.mp3",
|
||||||
|
"parent_id": "A:ALBUMARTIST/Beatles/A%20Hard%20Day's%20Night",
|
||||||
|
"item_class": "object.container.album.musicTrack"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "I Should Have Known Better",
|
||||||
|
"item_id": "S://192.168.42.100/music/iTunes/The%20Beatles/A%20Hard%20Day%2fs%I%20Should%20Have%20Known%20Better.mp3",
|
||||||
|
"parent_id": "A:ALBUMARTIST/Beatles/A%20Hard%20Day's%20Night",
|
||||||
|
"item_class": "object.container.album.musicTrack"
|
||||||
|
}
|
||||||
|
]
|
133
tests/components/sonos/snapshots/test_media_browser.ambr
Normal file
133
tests/components/sonos/snapshots/test_media_browser.ambr
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# serializer version: 1
|
||||||
|
# name: test_browse_media_library
|
||||||
|
list([
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'contributing_artist',
|
||||||
|
'media_content_id': 'A:ARTIST',
|
||||||
|
'media_content_type': 'contributing_artist',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Contributing Artists',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'artist',
|
||||||
|
'media_content_id': 'A:ALBUMARTIST',
|
||||||
|
'media_content_type': 'artist',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Artists',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'album',
|
||||||
|
'media_content_id': 'A:ALBUM',
|
||||||
|
'media_content_type': 'album',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Albums',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'genre',
|
||||||
|
'media_content_id': 'A:GENRE',
|
||||||
|
'media_content_type': 'genre',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Genres',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'composer',
|
||||||
|
'media_content_id': 'A:COMPOSER',
|
||||||
|
'media_content_type': 'composer',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Composers',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': True,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'track',
|
||||||
|
'media_content_id': 'A:TRACKS',
|
||||||
|
'media_content_type': 'track',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Tracks',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'playlist',
|
||||||
|
'media_content_id': 'A:PLAYLISTS',
|
||||||
|
'media_content_type': 'playlist',
|
||||||
|
'thumbnail': None,
|
||||||
|
'title': 'Playlists',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
# ---
|
||||||
|
# name: test_browse_media_library_albums
|
||||||
|
list([
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': True,
|
||||||
|
'children_media_class': None,
|
||||||
|
'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",
|
||||||
|
'title': "A Hard Day's Night",
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': True,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'album',
|
||||||
|
'media_content_id': 'A:ALBUM/Abbey%20Road',
|
||||||
|
'media_content_type': 'album',
|
||||||
|
'thumbnail': 'http://192.168.42.2:1400/getaa?u=x-file-cifs://192.168.42.100/music/The%20Beatles/AbbeyA%20Road/01%20Come%20Together.m4a&v=53',
|
||||||
|
'title': 'Abbey Road',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': True,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'album',
|
||||||
|
'media_content_id': 'A:ALBUM/Between%20Good%20And%20Evil',
|
||||||
|
'media_content_type': 'album',
|
||||||
|
'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',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
# ---
|
||||||
|
# name: test_browse_media_root
|
||||||
|
list([
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'directory',
|
||||||
|
'media_content_id': '',
|
||||||
|
'media_content_type': 'favorites',
|
||||||
|
'thumbnail': 'https://brands.home-assistant.io/_/sonos/logo.png',
|
||||||
|
'title': 'Favorites',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'can_expand': True,
|
||||||
|
'can_play': False,
|
||||||
|
'children_media_class': None,
|
||||||
|
'media_class': 'directory',
|
||||||
|
'media_content_id': '',
|
||||||
|
'media_content_type': 'library',
|
||||||
|
'thumbnail': 'https://brands.home-assistant.io/_/sonos/logo.png',
|
||||||
|
'title': 'Music Library',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
# ---
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.media_player.browse_media import BrowseMedia
|
from homeassistant.components.media_player.browse_media import BrowseMedia
|
||||||
from homeassistant.components.media_player.const import MediaClass, MediaType
|
from homeassistant.components.media_player.const import MediaClass, MediaType
|
||||||
from homeassistant.components.sonos.media_browser import (
|
from homeassistant.components.sonos.media_browser import (
|
||||||
|
@ -12,6 +14,8 @@ from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .conftest import SoCoMockFactory
|
from .conftest import SoCoMockFactory
|
||||||
|
|
||||||
|
from tests.typing import WebSocketGenerator
|
||||||
|
|
||||||
|
|
||||||
class MockMusicServiceItem:
|
class MockMusicServiceItem:
|
||||||
"""Mocks a Soco MusicServiceItem."""
|
"""Mocks a Soco MusicServiceItem."""
|
||||||
|
@ -95,3 +99,81 @@ async def test_build_item_response(
|
||||||
browse_item.children[1].media_content_id
|
browse_item.children[1].media_content_id
|
||||||
== "x-file-cifs://192.168.42.10/music/The%20Beatles/Abbey%20Road/03%20Something.mp3"
|
== "x-file-cifs://192.168.42.10/music/The%20Beatles/Abbey%20Road/03%20Something.mp3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_browse_media_root(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
soco_factory: SoCoMockFactory,
|
||||||
|
async_autosetup_sonos,
|
||||||
|
soco,
|
||||||
|
discover,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test the async_browse_media method."""
|
||||||
|
|
||||||
|
client = await hass_ws_client()
|
||||||
|
await client.send_json(
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "media_player/browse_media",
|
||||||
|
"entity_id": "media_player.zone_a",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
response = await client.receive_json()
|
||||||
|
assert response["success"]
|
||||||
|
assert response["result"]["children"] == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
async def test_browse_media_library(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
soco_factory: SoCoMockFactory,
|
||||||
|
async_autosetup_sonos,
|
||||||
|
soco,
|
||||||
|
discover,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test the async_browse_media method."""
|
||||||
|
|
||||||
|
client = await hass_ws_client()
|
||||||
|
await client.send_json(
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "media_player/browse_media",
|
||||||
|
"entity_id": "media_player.zone_a",
|
||||||
|
"media_content_id": "",
|
||||||
|
"media_content_type": "library",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
response = await client.receive_json()
|
||||||
|
assert response["success"]
|
||||||
|
assert response["result"]["children"] == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
async def test_browse_media_library_albums(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
soco_factory: SoCoMockFactory,
|
||||||
|
async_autosetup_sonos,
|
||||||
|
soco,
|
||||||
|
discover,
|
||||||
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test the async_browse_media method."""
|
||||||
|
soco_mock = soco_factory.mock_list.get("192.168.42.2")
|
||||||
|
|
||||||
|
client = await hass_ws_client()
|
||||||
|
await client.send_json(
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "media_player/browse_media",
|
||||||
|
"entity_id": "media_player.zone_a",
|
||||||
|
"media_content_id": "A:ALBUM",
|
||||||
|
"media_content_type": "album",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
response = await client.receive_json()
|
||||||
|
assert response["success"]
|
||||||
|
assert response["result"]["children"] == snapshot
|
||||||
|
assert soco_mock.music_library.browse_by_idstring.call_count == 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue