Recreate PR134142 after rebase conflicts

This commit is contained in:
peteS-UK 2025-01-07 16:17:18 +00:00
parent d1f0e0a70f
commit d431e3ffee
4 changed files with 92 additions and 6 deletions

View File

@ -18,6 +18,8 @@ from homeassistant.components.media_player import (
from homeassistant.core import HomeAssistant
from homeassistant.helpers.network import is_internal_request
from .const import UNPLAYABLE_TYPES
LIBRARY = [
"Favorites",
"Artists",
@ -26,6 +28,8 @@ LIBRARY = [
"Playlists",
"Genres",
"New Music",
"Apps",
"Radios",
]
MEDIA_TYPE_TO_SQUEEZEBOX = {
@ -41,6 +45,9 @@ MEDIA_TYPE_TO_SQUEEZEBOX = {
MediaType.TRACK: "title",
MediaType.PLAYLIST: "playlist",
MediaType.GENRE: "genre",
MediaType.APPS: "apps",
"Apps": "apps",
"Radios": "radios",
}
SQUEEZEBOX_ID_BY_TYPE = {
@ -50,10 +57,14 @@ SQUEEZEBOX_ID_BY_TYPE = {
MediaType.PLAYLIST: "playlist_id",
MediaType.GENRE: "genre_id",
"Favorites": "item_id",
MediaType.APPS: "item_id",
}
CONTENT_TYPE_MEDIA_CLASS: dict[str | MediaType, dict[str, MediaClass | None]] = {
"Favorites": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
"Apps": {"item": MediaClass.DIRECTORY, "children": MediaClass.APP},
"Radios": {"item": MediaClass.DIRECTORY, "children": MediaClass.APP},
"App": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
"Artists": {"item": MediaClass.DIRECTORY, "children": MediaClass.ARTIST},
"Albums": {"item": MediaClass.DIRECTORY, "children": MediaClass.ALBUM},
"Tracks": {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
@ -65,6 +76,8 @@ CONTENT_TYPE_MEDIA_CLASS: dict[str | MediaType, dict[str, MediaClass | None]] =
MediaType.TRACK: {"item": MediaClass.TRACK, "children": None},
MediaType.GENRE: {"item": MediaClass.GENRE, "children": MediaClass.ARTIST},
MediaType.PLAYLIST: {"item": MediaClass.PLAYLIST, "children": MediaClass.TRACK},
MediaType.APP: {"item": MediaClass.DIRECTORY, "children": MediaClass.TRACK},
MediaType.APPS: {"item": MediaClass.DIRECTORY, "children": MediaClass.APP},
}
CONTENT_TYPE_TO_CHILD_TYPE = {
@ -78,9 +91,16 @@ CONTENT_TYPE_TO_CHILD_TYPE = {
"Playlists": MediaType.PLAYLIST,
"Genres": MediaType.GENRE,
"Favorites": None, # can only be determined after inspecting the item
"Apps": MediaClass.APP,
"Radios": MediaClass.APP,
"App": None, # can only be determined after inspecting the item
"New Music": MediaType.ALBUM,
MediaType.APPS: MediaType.APP,
MediaType.APP: MediaType.TRACK,
}
KNOWN_APPS: set[str | None] = set()
async def build_item_response(
entity: MediaPlayerEntity,
@ -118,7 +138,7 @@ async def build_item_response(
children = []
list_playable = []
for item in result["items"]:
item_id = str(item["id"])
item_id = str(item.get("id", ""))
item_thumbnail: str | None = None
if item_type:
child_item_type: MediaType | str = item_type
@ -144,6 +164,46 @@ async def build_item_response(
can_expand = item["hasitems"]
can_play = item["isaudio"] and item.get("url")
if search_type in ["Apps", "Radios"]:
# item["cmd"] contains the name of the command to use with the cli for the app
# add the command to the dictionaries
if item["title"] == "Search" or item.get("type") in UNPLAYABLE_TYPES:
# Skip searches in apps as they'd need UI or if the link isn't to audio
continue
_cmd = "app-" + item["cmd"]
MEDIA_TYPE_TO_SQUEEZEBOX.update({_cmd: _cmd})
SQUEEZEBOX_ID_BY_TYPE.update({_cmd: "item_id"})
CONTENT_TYPE_MEDIA_CLASS.update(
{
_cmd: {
"item": MediaClass.DIRECTORY,
"children": MediaClass.TRACK,
}
}
)
CONTENT_TYPE_TO_CHILD_TYPE.update({_cmd: MediaType.TRACK})
if _cmd not in KNOWN_APPS:
KNOWN_APPS.add(_cmd)
child_item_type = _cmd
child_media_class = CONTENT_TYPE_MEDIA_CLASS[_cmd]
can_expand = True
can_play = False
if search_type in KNOWN_APPS:
if (
item.get("title") in ["Search", None]
or item.get("type") in UNPLAYABLE_TYPES
):
# Skip searches in apps as they'd need UI
continue
child_item_type = search_type
child_media_class = CONTENT_TYPE_MEDIA_CLASS[search_type]
can_play = item["isaudio"] and item.get("url")
can_expand = item["hasitems"]
if artwork_track_id := item.get("artwork_track_id"):
if internal_request:
item_thumbnail = player.generate_image_url_from_track_id(
@ -153,6 +213,8 @@ async def build_item_response(
item_thumbnail = entity.get_browse_image_url(
item_type, item_id, artwork_track_id
)
elif search_type in ["Apps", "Radios"]:
item_thumbnail = player.generate_image_url(item["icon"])
else:
item_thumbnail = item.get("image_url") # will not be proxied by HA
@ -176,6 +238,7 @@ async def build_item_response(
assert media_class["item"] is not None
if not search_id:
search_id = search_type
return BrowseMedia(
title=result.get("title"),
media_class=media_class["item"],
@ -215,7 +278,7 @@ async def library_payload(hass: HomeAssistant, player: Player) -> BrowseMedia:
media_class=media_class["children"],
media_content_id=item,
media_content_type=item,
can_play=item != "Favorites",
can_play=item not in ["Favorites", "Apps", "Radios"],
can_expand=True,
)
)
@ -251,8 +314,13 @@ async def generate_playlist(
raise BrowseError(f"Media type not supported: {media_type}")
browse_id = (SQUEEZEBOX_ID_BY_TYPE[media_type], media_id)
if media_type[:4] == "app-":
category = media_type
else:
category = "titles"
result = await player.async_browse(
"titles", limit=browse_limit, browse_id=browse_id
category, limit=browse_limit, browse_id=browse_id
)
if result and "items" in result:
items: list = result["items"]

View File

@ -27,7 +27,12 @@ STATUS_QUERY_LIBRARYNAME = "libraryname"
STATUS_QUERY_MAC = "mac"
STATUS_QUERY_UUID = "uuid"
STATUS_QUERY_VERSION = "version"
SQUEEZEBOX_SOURCE_STRINGS = ("source:", "wavin:", "spotify:")
SQUEEZEBOX_SOURCE_STRINGS = (
"source:",
"wavin:",
"spotify:",
"loop:",
)
SIGNAL_PLAYER_DISCOVERED = "squeezebox_player_discovered"
SIGNAL_PLAYER_REDISCOVERED = "squeezebox_player_rediscovered"
DISCOVERY_INTERVAL = 60
@ -36,5 +41,4 @@ CONF_BROWSE_LIMIT = "browse_limit"
CONF_VOLUME_STEP = "volume_step"
DEFAULT_BROWSE_LIMIT = 1000
DEFAULT_VOLUME_STEP = 5
ATTR_ANNOUNCE_VOLUME = "announce_volume"
ATTR_ANNOUNCE_TIMEOUT = "announce_timeout"
UNPLAYABLE_TYPES = ("text", "actions")

View File

@ -142,6 +142,9 @@ async def mock_async_browse(
"title": "title",
"playlists": "playlist",
"playlist": "title",
"apps": "app",
"radios": "app",
"app-fakecommand": "track",
}
fake_items = [
{
@ -152,6 +155,8 @@ async def mock_async_browse(
"item_type": child_types[media_type],
"artwork_track_id": "b35bb9e9",
"url": "file:///var/lib/squeezeboxserver/music/track_1.mp3",
"cmd": "fakecommand",
"icon": "plugins/Qobuz/html/images/qobuz.png",
},
{
"title": "Fake Item 2",
@ -161,6 +166,8 @@ async def mock_async_browse(
"item_type": child_types[media_type],
"image_url": "http://lms.internal:9000/html/images/favorites.png",
"url": "file:///var/lib/squeezeboxserver/music/track_2.mp3",
"cmd": "fakecommand",
"icon": "plugins/Qobuz/html/images/qobuz.png",
},
{
"title": "Fake Item 3",
@ -169,6 +176,8 @@ async def mock_async_browse(
"isaudio": True,
"album_id": FAKE_VALID_ITEM_ID if media_type == "favorites" else None,
"url": "file:///var/lib/squeezeboxserver/music/track_3.mp3",
"cmd": "fakecommand",
"icon": "plugins/Qobuz/html/images/qobuz.png",
},
]
@ -232,6 +241,9 @@ def mock_pysqueezebox_player(uuid: str) -> MagicMock:
mock_player.async_play_announcement = AsyncMock(
side_effect=mock_async_play_announcement
)
mock_player.generate_image_url = MagicMock(
return_value="http://lms.internal:9000/html/images/favorites.png"
)
mock_player.name = TEST_PLAYER_NAME
mock_player.player_id = uuid
mock_player.mode = "stop"

View File

@ -79,6 +79,8 @@ async def test_async_browse_media_with_subitems(
"Playlists",
"Genres",
"New Music",
"Apps",
"Radios",
):
with patch(
"homeassistant.components.squeezebox.browse_media.is_internal_request",