mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Adds media_browser functionality to the music assistant integration (#131577)
* Add test fixtures for all library loading * Add media browser * Add tests for media_browser
This commit is contained in:
parent
f04c50c59e
commit
40a4ff1c84
351
homeassistant/components/music_assistant/media_browser.py
Normal file
351
homeassistant/components/music_assistant/media_browser.py
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
"""Media Source Implementation."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
|
from music_assistant_models.media_items import MediaItemType
|
||||||
|
|
||||||
|
from homeassistant.components import media_source
|
||||||
|
from homeassistant.components.media_player import (
|
||||||
|
BrowseError,
|
||||||
|
BrowseMedia,
|
||||||
|
MediaClass,
|
||||||
|
MediaType,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .const import DEFAULT_NAME, DOMAIN
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from music_assistant_client import MusicAssistantClient
|
||||||
|
|
||||||
|
MEDIA_TYPE_RADIO = "radio"
|
||||||
|
|
||||||
|
PLAYABLE_MEDIA_TYPES = [
|
||||||
|
MediaType.PLAYLIST,
|
||||||
|
MediaType.ALBUM,
|
||||||
|
MediaType.ARTIST,
|
||||||
|
MEDIA_TYPE_RADIO,
|
||||||
|
MediaType.TRACK,
|
||||||
|
]
|
||||||
|
|
||||||
|
LIBRARY_ARTISTS = "artists"
|
||||||
|
LIBRARY_ALBUMS = "albums"
|
||||||
|
LIBRARY_TRACKS = "tracks"
|
||||||
|
LIBRARY_PLAYLISTS = "playlists"
|
||||||
|
LIBRARY_RADIO = "radio"
|
||||||
|
|
||||||
|
|
||||||
|
LIBRARY_TITLE_MAP = {
|
||||||
|
LIBRARY_ARTISTS: "Artists",
|
||||||
|
LIBRARY_ALBUMS: "Albums",
|
||||||
|
LIBRARY_TRACKS: "Tracks",
|
||||||
|
LIBRARY_PLAYLISTS: "Playlists",
|
||||||
|
LIBRARY_RADIO: "Radio stations",
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBRARY_MEDIA_CLASS_MAP = {
|
||||||
|
LIBRARY_ARTISTS: MediaClass.ARTIST,
|
||||||
|
LIBRARY_ALBUMS: MediaClass.ALBUM,
|
||||||
|
LIBRARY_TRACKS: MediaClass.TRACK,
|
||||||
|
LIBRARY_PLAYLISTS: MediaClass.PLAYLIST,
|
||||||
|
LIBRARY_RADIO: MediaClass.MUSIC, # radio is not accepted by HA
|
||||||
|
}
|
||||||
|
|
||||||
|
MEDIA_CONTENT_TYPE_FLAC = "audio/flac"
|
||||||
|
THUMB_SIZE = 200
|
||||||
|
|
||||||
|
|
||||||
|
def media_source_filter(item: BrowseMedia) -> bool:
|
||||||
|
"""Filter media sources."""
|
||||||
|
return item.media_content_type.startswith("audio/")
|
||||||
|
|
||||||
|
|
||||||
|
async def async_browse_media(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mass: MusicAssistantClient,
|
||||||
|
media_content_id: str | None,
|
||||||
|
media_content_type: str | None,
|
||||||
|
) -> BrowseMedia:
|
||||||
|
"""Browse media."""
|
||||||
|
if media_content_id is None:
|
||||||
|
return await build_main_listing(hass)
|
||||||
|
|
||||||
|
assert media_content_type is not None
|
||||||
|
|
||||||
|
if media_source.is_media_source_id(media_content_id):
|
||||||
|
return await media_source.async_browse_media(
|
||||||
|
hass, media_content_id, content_filter=media_source_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
if media_content_id == LIBRARY_ARTISTS:
|
||||||
|
return await build_artists_listing(mass)
|
||||||
|
if media_content_id == LIBRARY_ALBUMS:
|
||||||
|
return await build_albums_listing(mass)
|
||||||
|
if media_content_id == LIBRARY_TRACKS:
|
||||||
|
return await build_tracks_listing(mass)
|
||||||
|
if media_content_id == LIBRARY_PLAYLISTS:
|
||||||
|
return await build_playlists_listing(mass)
|
||||||
|
if media_content_id == LIBRARY_RADIO:
|
||||||
|
return await build_radio_listing(mass)
|
||||||
|
if "artist" in media_content_id:
|
||||||
|
return await build_artist_items_listing(mass, media_content_id)
|
||||||
|
if "album" in media_content_id:
|
||||||
|
return await build_album_items_listing(mass, media_content_id)
|
||||||
|
if "playlist" in media_content_id:
|
||||||
|
return await build_playlist_items_listing(mass, media_content_id)
|
||||||
|
|
||||||
|
raise BrowseError(f"Media not found: {media_content_type} / {media_content_id}")
|
||||||
|
|
||||||
|
|
||||||
|
async def build_main_listing(hass: HomeAssistant) -> BrowseMedia:
|
||||||
|
"""Build main browse listing."""
|
||||||
|
children: list[BrowseMedia] = []
|
||||||
|
for library, media_class in LIBRARY_MEDIA_CLASS_MAP.items():
|
||||||
|
child_source = BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id=library,
|
||||||
|
media_content_type=DOMAIN,
|
||||||
|
title=LIBRARY_TITLE_MAP[library],
|
||||||
|
children_media_class=media_class,
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
)
|
||||||
|
children.append(child_source)
|
||||||
|
|
||||||
|
try:
|
||||||
|
item = await media_source.async_browse_media(
|
||||||
|
hass, None, content_filter=media_source_filter
|
||||||
|
)
|
||||||
|
# If domain is None, it's overview of available sources
|
||||||
|
if item.domain is None and item.children is not None:
|
||||||
|
children.extend(item.children)
|
||||||
|
else:
|
||||||
|
children.append(item)
|
||||||
|
except media_source.BrowseError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id="",
|
||||||
|
media_content_type=DOMAIN,
|
||||||
|
title=DEFAULT_NAME,
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
children=children,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_playlists_listing(mass: MusicAssistantClient) -> BrowseMedia:
|
||||||
|
"""Build Playlists browse listing."""
|
||||||
|
media_class = LIBRARY_MEDIA_CLASS_MAP[LIBRARY_PLAYLISTS]
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id=LIBRARY_PLAYLISTS,
|
||||||
|
media_content_type=MediaType.PLAYLIST,
|
||||||
|
title=LIBRARY_TITLE_MAP[LIBRARY_PLAYLISTS],
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=media_class,
|
||||||
|
children=sorted(
|
||||||
|
[
|
||||||
|
build_item(mass, item, can_expand=True)
|
||||||
|
# we only grab the first page here because the
|
||||||
|
# HA media browser does not support paging
|
||||||
|
for item in await mass.music.get_library_playlists(limit=500)
|
||||||
|
if item.available
|
||||||
|
],
|
||||||
|
key=lambda x: x.title,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_playlist_items_listing(
|
||||||
|
mass: MusicAssistantClient, identifier: str
|
||||||
|
) -> BrowseMedia:
|
||||||
|
"""Build Playlist items browse listing."""
|
||||||
|
playlist = await mass.music.get_item_by_uri(identifier)
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.PLAYLIST,
|
||||||
|
media_content_id=playlist.uri,
|
||||||
|
media_content_type=MediaType.PLAYLIST,
|
||||||
|
title=playlist.name,
|
||||||
|
can_play=True,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=MediaClass.TRACK,
|
||||||
|
children=[
|
||||||
|
build_item(mass, item, can_expand=False)
|
||||||
|
# we only grab the first page here because the
|
||||||
|
# HA media browser does not support paging
|
||||||
|
for item in await mass.music.get_playlist_tracks(
|
||||||
|
playlist.item_id, playlist.provider
|
||||||
|
)
|
||||||
|
if item.available
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_artists_listing(mass: MusicAssistantClient) -> BrowseMedia:
|
||||||
|
"""Build Albums browse listing."""
|
||||||
|
media_class = LIBRARY_MEDIA_CLASS_MAP[LIBRARY_ARTISTS]
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id=LIBRARY_ARTISTS,
|
||||||
|
media_content_type=MediaType.ARTIST,
|
||||||
|
title=LIBRARY_TITLE_MAP[LIBRARY_ARTISTS],
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=media_class,
|
||||||
|
children=sorted(
|
||||||
|
[
|
||||||
|
build_item(mass, artist, can_expand=True)
|
||||||
|
# we only grab the first page here because the
|
||||||
|
# HA media browser does not support paging
|
||||||
|
for artist in await mass.music.get_library_artists(limit=500)
|
||||||
|
if artist.available
|
||||||
|
],
|
||||||
|
key=lambda x: x.title,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_artist_items_listing(
|
||||||
|
mass: MusicAssistantClient, identifier: str
|
||||||
|
) -> BrowseMedia:
|
||||||
|
"""Build Artist items browse listing."""
|
||||||
|
artist = await mass.music.get_item_by_uri(identifier)
|
||||||
|
albums = await mass.music.get_artist_albums(artist.item_id, artist.provider)
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaType.ARTIST,
|
||||||
|
media_content_id=artist.uri,
|
||||||
|
media_content_type=MediaType.ARTIST,
|
||||||
|
title=artist.name,
|
||||||
|
can_play=True,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=MediaClass.ALBUM,
|
||||||
|
children=[
|
||||||
|
build_item(mass, album, can_expand=True)
|
||||||
|
for album in albums
|
||||||
|
if album.available
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_albums_listing(mass: MusicAssistantClient) -> BrowseMedia:
|
||||||
|
"""Build Albums browse listing."""
|
||||||
|
media_class = LIBRARY_MEDIA_CLASS_MAP[LIBRARY_ALBUMS]
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id=LIBRARY_ALBUMS,
|
||||||
|
media_content_type=MediaType.ALBUM,
|
||||||
|
title=LIBRARY_TITLE_MAP[LIBRARY_ALBUMS],
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=media_class,
|
||||||
|
children=sorted(
|
||||||
|
[
|
||||||
|
build_item(mass, album, can_expand=True)
|
||||||
|
# we only grab the first page here because the
|
||||||
|
# HA media browser does not support paging
|
||||||
|
for album in await mass.music.get_library_albums(limit=500)
|
||||||
|
if album.available
|
||||||
|
],
|
||||||
|
key=lambda x: x.title,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_album_items_listing(
|
||||||
|
mass: MusicAssistantClient, identifier: str
|
||||||
|
) -> BrowseMedia:
|
||||||
|
"""Build Album items browse listing."""
|
||||||
|
album = await mass.music.get_item_by_uri(identifier)
|
||||||
|
tracks = await mass.music.get_album_tracks(album.item_id, album.provider)
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaType.ALBUM,
|
||||||
|
media_content_id=album.uri,
|
||||||
|
media_content_type=MediaType.ALBUM,
|
||||||
|
title=album.name,
|
||||||
|
can_play=True,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=MediaClass.TRACK,
|
||||||
|
children=[
|
||||||
|
build_item(mass, track, False) for track in tracks if track.available
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_tracks_listing(mass: MusicAssistantClient) -> BrowseMedia:
|
||||||
|
"""Build Tracks browse listing."""
|
||||||
|
media_class = LIBRARY_MEDIA_CLASS_MAP[LIBRARY_TRACKS]
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id=LIBRARY_TRACKS,
|
||||||
|
media_content_type=MediaType.TRACK,
|
||||||
|
title=LIBRARY_TITLE_MAP[LIBRARY_TRACKS],
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=media_class,
|
||||||
|
children=sorted(
|
||||||
|
[
|
||||||
|
build_item(mass, track, can_expand=False)
|
||||||
|
# we only grab the first page here because the
|
||||||
|
# HA media browser does not support paging
|
||||||
|
for track in await mass.music.get_library_tracks(limit=500)
|
||||||
|
if track.available
|
||||||
|
],
|
||||||
|
key=lambda x: x.title,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def build_radio_listing(mass: MusicAssistantClient) -> BrowseMedia:
|
||||||
|
"""Build Radio browse listing."""
|
||||||
|
media_class = LIBRARY_MEDIA_CLASS_MAP[LIBRARY_RADIO]
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=MediaClass.DIRECTORY,
|
||||||
|
media_content_id=LIBRARY_RADIO,
|
||||||
|
media_content_type=DOMAIN,
|
||||||
|
title=LIBRARY_TITLE_MAP[LIBRARY_RADIO],
|
||||||
|
can_play=False,
|
||||||
|
can_expand=True,
|
||||||
|
children_media_class=media_class,
|
||||||
|
children=[
|
||||||
|
build_item(mass, track, can_expand=False, media_class=media_class)
|
||||||
|
# we only grab the first page here because the
|
||||||
|
# HA media browser does not support paging
|
||||||
|
for track in await mass.music.get_library_radios(limit=500)
|
||||||
|
if track.available
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_item(
|
||||||
|
mass: MusicAssistantClient,
|
||||||
|
item: MediaItemType,
|
||||||
|
can_expand: bool = True,
|
||||||
|
media_class: Any = None,
|
||||||
|
) -> BrowseMedia:
|
||||||
|
"""Return BrowseMedia for MediaItem."""
|
||||||
|
if artists := getattr(item, "artists", None):
|
||||||
|
title = f"{artists[0].name} - {item.name}"
|
||||||
|
else:
|
||||||
|
title = item.name
|
||||||
|
img_url = mass.get_media_item_image_url(item)
|
||||||
|
|
||||||
|
return BrowseMedia(
|
||||||
|
media_class=media_class or item.media_type.value,
|
||||||
|
media_content_id=item.uri,
|
||||||
|
media_content_type=MediaType.MUSIC,
|
||||||
|
title=title,
|
||||||
|
can_play=True,
|
||||||
|
can_expand=can_expand,
|
||||||
|
thumbnail=img_url,
|
||||||
|
)
|
@ -43,6 +43,7 @@ from homeassistant.util.dt import utc_from_timestamp
|
|||||||
from . import MusicAssistantConfigEntry
|
from . import MusicAssistantConfigEntry
|
||||||
from .const import ATTR_ACTIVE_QUEUE, ATTR_MASS_PLAYER_TYPE, DOMAIN
|
from .const import ATTR_ACTIVE_QUEUE, ATTR_MASS_PLAYER_TYPE, DOMAIN
|
||||||
from .entity import MusicAssistantEntity
|
from .entity import MusicAssistantEntity
|
||||||
|
from .media_browser import async_browse_media
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from music_assistant_client import MusicAssistantClient
|
from music_assistant_client import MusicAssistantClient
|
||||||
@ -440,10 +441,11 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
|
|||||||
media_content_id: str | None = None,
|
media_content_id: str | None = None,
|
||||||
) -> BrowseMedia:
|
) -> BrowseMedia:
|
||||||
"""Implement the websocket media browsing helper."""
|
"""Implement the websocket media browsing helper."""
|
||||||
return await media_source.async_browse_media(
|
return await async_browse_media(
|
||||||
self.hass,
|
self.hass,
|
||||||
|
self.mass,
|
||||||
media_content_id,
|
media_content_id,
|
||||||
content_filter=lambda item: item.media_content_type.startswith("audio/"),
|
media_content_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_media_image_url(
|
def _update_media_image_url(
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
|
||||||
from music_assistant_models.enums import EventType
|
from music_assistant_models.enums import EventType
|
||||||
|
from music_assistant_models.media_items import Album, Artist, Playlist, Radio, Track
|
||||||
from music_assistant_models.player import Player
|
from music_assistant_models.player import Player
|
||||||
from music_assistant_models.player_queue import PlayerQueue
|
from music_assistant_models.player_queue import PlayerQueue
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
@ -42,6 +43,25 @@ async def setup_integration_from_fixtures(
|
|||||||
data={"url": MOCK_URL},
|
data={"url": MOCK_URL},
|
||||||
unique_id=music_assistant_client.server_info.server_id,
|
unique_id=music_assistant_client.server_info.server_id,
|
||||||
)
|
)
|
||||||
|
music = music_assistant_client.music
|
||||||
|
library_artists = create_library_artists_from_fixture()
|
||||||
|
music.get_library_artists = AsyncMock(return_value=library_artists)
|
||||||
|
library_artist_albums = create_library_artist_albums_from_fixture()
|
||||||
|
music.get_artist_albums = AsyncMock(return_value=library_artist_albums)
|
||||||
|
library_albums = create_library_albums_from_fixture()
|
||||||
|
music.get_library_albums = AsyncMock(return_value=library_albums)
|
||||||
|
library_album_tracks = create_library_album_tracks_from_fixture()
|
||||||
|
music.get_album_tracks = AsyncMock(return_value=library_album_tracks)
|
||||||
|
library_tracks = create_library_tracks_from_fixture()
|
||||||
|
music.get_library_tracks = AsyncMock(return_value=library_tracks)
|
||||||
|
library_playlists = create_library_playlists_from_fixture()
|
||||||
|
music.get_library_playlists = AsyncMock(return_value=library_playlists)
|
||||||
|
library_playlist_tracks = create_library_playlist_tracks_from_fixture()
|
||||||
|
music.get_playlist_tracks = AsyncMock(return_value=library_playlist_tracks)
|
||||||
|
library_radios = create_library_radios_from_fixture()
|
||||||
|
music.get_library_radios = AsyncMock(return_value=library_radios)
|
||||||
|
music.get_item_by_uri = AsyncMock()
|
||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
@ -61,6 +81,54 @@ def create_player_queues_from_fixture() -> list[Player]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_albums_from_fixture() -> list[Album]:
|
||||||
|
"""Create MA Albums from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_albums")
|
||||||
|
return [Album.from_dict(album_data) for album_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_album_tracks_from_fixture() -> list[Track]:
|
||||||
|
"""Create MA Tracks from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_album_tracks")
|
||||||
|
return [Track.from_dict(track_data) for track_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_tracks_from_fixture() -> list[Track]:
|
||||||
|
"""Create MA Tracks from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_tracks")
|
||||||
|
return [Track.from_dict(track_data) for track_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_artists_from_fixture() -> list[Artist]:
|
||||||
|
"""Create MA Artists from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_artists")
|
||||||
|
return [Artist.from_dict(artist_data) for artist_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_artist_albums_from_fixture() -> list[Album]:
|
||||||
|
"""Create MA Albums from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_artist_albums")
|
||||||
|
return [Album.from_dict(album_data) for album_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_playlists_from_fixture() -> list[Playlist]:
|
||||||
|
"""Create MA Playlists from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_playlists")
|
||||||
|
return [Playlist.from_dict(playlist_data) for playlist_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_playlist_tracks_from_fixture() -> list[Track]:
|
||||||
|
"""Create MA Tracks from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_playlist_tracks")
|
||||||
|
return [Track.from_dict(track_data) for track_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
|
def create_library_radios_from_fixture() -> list[Radio]:
|
||||||
|
"""Create MA Radios from fixture."""
|
||||||
|
fixture_data = load_and_parse_fixture("library_radios")
|
||||||
|
return [Radio.from_dict(radio_data) for radio_data in fixture_data]
|
||||||
|
|
||||||
|
|
||||||
async def trigger_subscription_callback(
|
async def trigger_subscription_callback(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
client: MagicMock,
|
client: MagicMock,
|
||||||
|
@ -0,0 +1,364 @@
|
|||||||
|
{
|
||||||
|
"library_album_tracks": [
|
||||||
|
{
|
||||||
|
"item_id": "247",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Le Mirage",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "mirage, le",
|
||||||
|
"uri": "library://track/247",
|
||||||
|
"external_ids": [["isrc", "FR10S1794640"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "70953631",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--63Pkq9Aw",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/70953631",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "Dana Murray",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 35,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 352,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 195,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Dana Jean Phoenix",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "dana jean phoenix",
|
||||||
|
"uri": "library://artist/195",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 95,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Synthwave (The 80S Revival)",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "synthwave (the 80s revival)",
|
||||||
|
"uri": "library://album/95",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "362",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Rabbit in the Headlights",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "rabbit in the headlights",
|
||||||
|
"uri": "library://track/362",
|
||||||
|
"external_ids": [["isrc", "GBLFP1645070"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "70953636",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/70953636",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "Michael Oakley",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 34,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 253,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 90,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Michael Oakley",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "michael oakley",
|
||||||
|
"uri": "library://artist/90",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 95,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Synthwave (The 80S Revival)",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "synthwave (the 80s revival)",
|
||||||
|
"uri": "library://album/95",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "1",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "1988 Girls",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "1988 girls",
|
||||||
|
"uri": "library://track/1",
|
||||||
|
"external_ids": [["isrc", "DEBL60768604"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "70953637",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--56X5qDS7",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/70953637",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "Kiez Beats",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 14,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 258,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 110,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Futurecop!",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "futurecop!",
|
||||||
|
"uri": "library://artist/110",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 95,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Synthwave (The 80S Revival)",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "synthwave (the 80s revival)",
|
||||||
|
"uri": "library://album/95",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "495",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Timmy Goes to Space",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "timmy goes to space",
|
||||||
|
"uri": "library://track/495",
|
||||||
|
"external_ids": [["isrc", "NO2D81710001"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "70953643",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/70953643",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "Jens Kristian Espevik",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 4,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 212,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 453,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Mr. Maen",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "mr. maen",
|
||||||
|
"uri": "library://artist/453",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 95,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Synthwave (The 80S Revival)",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "synthwave (the 80s revival)",
|
||||||
|
"uri": "library://album/95",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 13
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
148
tests/components/music_assistant/fixtures/library_albums.json
Normal file
148
tests/components/music_assistant/fixtures/library_albums.json
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
{
|
||||||
|
"library_albums": [
|
||||||
|
{
|
||||||
|
"item_id": "396",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Synth Punk EP",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "synth punk ep",
|
||||||
|
"uri": "library://album/396",
|
||||||
|
"external_ids": [["barcode", "872133626743"]],
|
||||||
|
"media_type": "album",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "48563817",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/album/48563817",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/99c8bc2f/ed43/4fb2/adfb/e7e3157089d2/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "586446 Records DK",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 7,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": null,
|
||||||
|
"year": 2015,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 289,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "A Space Love Adventure",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "space love adventure, a",
|
||||||
|
"uri": "library://artist/289",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album_type": "ep"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "95",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Synthwave (The 80S Revival)",
|
||||||
|
"version": "The 80S Revival",
|
||||||
|
"sort_name": "synthwave (the 80s revival)",
|
||||||
|
"uri": "library://album/95",
|
||||||
|
"external_ids": [["barcode", "3614974086112"]],
|
||||||
|
"media_type": "album",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "70953630",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--56X5qDS7",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/album/70953630",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/b7b1897c/57ed/4a31/83d7/9ab3df83183a/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "Kiez Beats",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 43,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": null,
|
||||||
|
"year": 2017,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 96,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Various Artists",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "various artists",
|
||||||
|
"uri": "library://artist/96",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album_type": "compilation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"library_artist_albums": [
|
||||||
|
{
|
||||||
|
"item_id": "115",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "A Sea of Stars",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "sea of stars, a",
|
||||||
|
"uri": "library://album/115",
|
||||||
|
"external_ids": [["barcode", "859741010126"]],
|
||||||
|
"media_type": "album",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "157401232",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--56X5qDS7",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/album/157401232",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/f55c749b/6642/40e3/a291/ff01fd2915cf/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "2021 NRW Records, under exclusive license to NewRetroWave, LLC",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 0,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": null,
|
||||||
|
"year": 2021,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 127,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "W O L F C L U B",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "w o l f c l u b",
|
||||||
|
"uri": "library://artist/127",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": 128,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Dora Pereli",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "dora pereli",
|
||||||
|
"uri": "library://artist/128",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album_type": "single"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"library_artists": [
|
||||||
|
{
|
||||||
|
"item_id": "127",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "W O L F C L U B",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "w o l f c l u b",
|
||||||
|
"uri": "library://artist/127",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "8741977",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--56X5qDS7",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "?",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "?",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/artist/8741977",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": null,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/1e01cdb6/f15d/4d8b/8440/a047976c1cac/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": null,
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": null,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,262 @@
|
|||||||
|
{
|
||||||
|
"library_playlist_tracks": [
|
||||||
|
{
|
||||||
|
"item_id": "77616130",
|
||||||
|
"provider": "tidal--Ah76MuMg",
|
||||||
|
"name": "Won't Get Fooled Again",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "won't get fooled again",
|
||||||
|
"uri": "tidal--Ah76MuMg://track/77616130",
|
||||||
|
"external_ids": [["isrc", "GBUM71405419"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "77616130",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": true,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 24,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/77616130",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/3496a8ad/ea69/4d7e/bbda/045417ab59e1/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 1971 Polydor Ltd. (UK)",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 30,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": 0,
|
||||||
|
"duration": 516,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": "24915",
|
||||||
|
"provider": "tidal--Ah76MuMg",
|
||||||
|
"name": "The Who",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "who, the",
|
||||||
|
"uri": "tidal--Ah76MuMg://artist/24915",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "24915",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": true,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "?",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "?",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/artist/24915",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": null,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/0f782232/18c8/40b7/bb13/91c6039e40e6/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": null,
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": null,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": "77616121",
|
||||||
|
"provider": "tidal--Ah76MuMg",
|
||||||
|
"name": "Who's Next",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "who's next",
|
||||||
|
"uri": "tidal--Ah76MuMg://album/77616121",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "153795",
|
||||||
|
"provider": "tidal--Ah76MuMg",
|
||||||
|
"name": "We're An American Band",
|
||||||
|
"version": "Remastered 2002",
|
||||||
|
"sort_name": "we're an american band",
|
||||||
|
"uri": "tidal--Ah76MuMg://track/153795",
|
||||||
|
"external_ids": [["isrc", "USCA20200334"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "153795",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": true,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/153795",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/a6d86e02/84c1/41f7/84f5/41be8571fc40/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 2002 Capitol Records, LLC",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 48,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": 1,
|
||||||
|
"duration": 207,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": "9380",
|
||||||
|
"provider": "tidal--Ah76MuMg",
|
||||||
|
"name": "Grand Funk Railroad",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "grand funk railroad",
|
||||||
|
"uri": "tidal--Ah76MuMg://artist/9380",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "9380",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": true,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "?",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "?",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/artist/9380",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": null,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/6535bf95/a06d/4d23/8262/604fa41d8126/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": null,
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": null,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": false,
|
||||||
|
"position": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": "153794",
|
||||||
|
"provider": "tidal--Ah76MuMg",
|
||||||
|
"name": "We're An American Band (Expanded Edition / Remastered 2002)",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "we're an american band (expanded edition / remastered 2002)",
|
||||||
|
"uri": "tidal--Ah76MuMg://album/153794",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"library_playlists": [
|
||||||
|
{
|
||||||
|
"item_id": "40",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "1970s Rock Hits",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "1970s rock hits",
|
||||||
|
"uri": "library://playlist/40",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "playlist",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "30da0578-0ca0-4716-b66e-5f02bcd96702",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "?",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "?",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/browse/playlist/30da0578-0ca0-4716-b66e-5f02bcd96702",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": null,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/95913801/41c1/4cc9/bf94/a0fba657bba5/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": null,
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": null,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"owner": "TIDAL",
|
||||||
|
"is_editable": 0,
|
||||||
|
"cache_checksum": "2023-10-09 07: 09: 23.446000+00: 00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"library_radios": [
|
||||||
|
{
|
||||||
|
"item_id": "1",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "fm4 | ORF | HQ",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "fm4 | orf | hq",
|
||||||
|
"uri": "library://radio/1",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "radio",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "1e13ed4e-daa9-4728-8550-e08d89c1c8e7",
|
||||||
|
"provider_domain": "radiobrowser",
|
||||||
|
"provider_instance": "radiobrowser--FRc3pD3t",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "?",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "?",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": null,
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": null,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://tubestatic.orf.at/mojo/1_3/storyserver//tube/fm4/images/touch-icon-iphone-retina.png",
|
||||||
|
"provider": "radiobrowser",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": null,
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"type": "website",
|
||||||
|
"url": "https://fm4.orf.at/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 166,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 172800
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
556
tests/components/music_assistant/fixtures/library_tracks.json
Normal file
556
tests/components/music_assistant/fixtures/library_tracks.json
Normal file
@ -0,0 +1,556 @@
|
|||||||
|
{
|
||||||
|
"library_tracks": [
|
||||||
|
{
|
||||||
|
"item_id": "456",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Tennessee Whiskey",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "tennessee whiskey",
|
||||||
|
"uri": "library://track/456",
|
||||||
|
"external_ids": [["isrc", "USUM71418088"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "44832786",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/44832786",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/4894ff62/9de2/4ed8/a7b9/69e217bbbdda/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 2015 Mercury Records, a Division of UMG Recordings, Inc.",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 33,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 293,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 433,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Chris Stapleton",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "chris stapleton",
|
||||||
|
"uri": "library://artist/433",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 463,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Traveller",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "traveller",
|
||||||
|
"uri": "library://album/463",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/4894ff62/9de2/4ed8/a7b9/69e217bbbdda/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "467",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Thelma + Louise",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "thelma + louise",
|
||||||
|
"uri": "library://track/467",
|
||||||
|
"external_ids": [["isrc", "GBUM72104380"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "194027388",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 24,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/194027388",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/04fc7c3c/b814/4855/874c/a2e456205b65/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 2021 Virgin Records Limited",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 20,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 137,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 81,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Bastille",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "bastille",
|
||||||
|
"uri": "library://artist/81",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 471,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Thelma + Louise",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "thelma + louise",
|
||||||
|
"uri": "library://album/471",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/04fc7c3c/b814/4855/874c/a2e456205b65/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "485",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "They Don't Care About Us",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "they don't care about us",
|
||||||
|
"uri": "library://track/485",
|
||||||
|
"external_ids": [["isrc", "USSM19500629"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "5279069",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 24,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/5279069",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/a2fa5815/851d/4d2d/b6a7/17a365c838f9/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "(P) 1995 MJJ Productions Inc.",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 27,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 284,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 30,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Michael Jackson",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "michael jackson",
|
||||||
|
"uri": "library://artist/30",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 486,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "HIStory - PAST, PRESENT AND FUTURE - BOOK I",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "history - past, present and future - book i",
|
||||||
|
"uri": "library://album/486",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/a2fa5815/851d/4d2d/b6a7/17a365c838f9/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 2,
|
||||||
|
"track_number": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "486",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "They Don't Give A F**** About Us",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "they don't give a f**** about us",
|
||||||
|
"uri": "library://track/486",
|
||||||
|
"external_ids": [["isrc", "USIR10211795"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "44066854",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/44066854",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": true,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/6b7b2b58/5dc2/4d0c/8979/7b30bb779d6f/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 2002 Amaru Entertainment, Inc., Under exclusive license to Interscope Records",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 34,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 306,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 159,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "2Pac",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "2pac",
|
||||||
|
"uri": "library://artist/159",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": 451,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "The Outlawz",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "outlawz, the",
|
||||||
|
"uri": "library://artist/451",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 487,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Better Dayz",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "better dayz",
|
||||||
|
"uri": "library://album/487",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/6b7b2b58/5dc2/4d0c/8979/7b30bb779d6f/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 2,
|
||||||
|
"track_number": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "487",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Things We Lost In The Fire",
|
||||||
|
"version": "TORN Remix",
|
||||||
|
"sort_name": "things we lost in the fire",
|
||||||
|
"uri": "library://track/487",
|
||||||
|
"external_ids": [["isrc", "GBUM71304903"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "22627902",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 16,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/22627902",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/de277fd3/cc29/4d63/a60f/13b501c5f3d0/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 2013 Virgin Records Limited",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 10,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 323,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 81,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Bastille",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "bastille",
|
||||||
|
"uri": "library://artist/81",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 488,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Things We Lost In The Fire",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "things we lost in the fire",
|
||||||
|
"uri": "library://album/488",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/de277fd3/cc29/4d63/a60f/13b501c5f3d0/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item_id": "488",
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Those Nights",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "those nights",
|
||||||
|
"uri": "library://track/488",
|
||||||
|
"external_ids": [["isrc", "GBUM71803866"]],
|
||||||
|
"media_type": "track",
|
||||||
|
"provider_mappings": [
|
||||||
|
{
|
||||||
|
"item_id": "110750762",
|
||||||
|
"provider_domain": "tidal",
|
||||||
|
"provider_instance": "tidal--Ah76MuMg",
|
||||||
|
"available": 1,
|
||||||
|
"audio_format": {
|
||||||
|
"content_type": "flac",
|
||||||
|
"sample_rate": 44100,
|
||||||
|
"bit_depth": 24,
|
||||||
|
"channels": 2,
|
||||||
|
"output_format_str": "flac",
|
||||||
|
"bit_rate": 0
|
||||||
|
},
|
||||||
|
"url": "https://tidal.com/track/110750762",
|
||||||
|
"details": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"review": null,
|
||||||
|
"explicit": false,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/713805f3/c08c/4c0f/8199/d63e6badac0d/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genres": null,
|
||||||
|
"mood": null,
|
||||||
|
"style": null,
|
||||||
|
"copyright": "℗ 2019 Virgin Records Limited",
|
||||||
|
"lyrics": null,
|
||||||
|
"label": null,
|
||||||
|
"links": null,
|
||||||
|
"performers": null,
|
||||||
|
"preview": null,
|
||||||
|
"popularity": 21,
|
||||||
|
"release_date": null,
|
||||||
|
"languages": null,
|
||||||
|
"last_refresh": null
|
||||||
|
},
|
||||||
|
"favorite": true,
|
||||||
|
"position": null,
|
||||||
|
"duration": 270,
|
||||||
|
"artists": [
|
||||||
|
{
|
||||||
|
"item_id": 81,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Bastille",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "bastille",
|
||||||
|
"uri": "library://artist/81",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "artist",
|
||||||
|
"available": true,
|
||||||
|
"image": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"album": {
|
||||||
|
"item_id": 489,
|
||||||
|
"provider": "library",
|
||||||
|
"name": "Doom Days",
|
||||||
|
"version": "",
|
||||||
|
"sort_name": "doom days",
|
||||||
|
"uri": "library://album/489",
|
||||||
|
"external_ids": [],
|
||||||
|
"media_type": "album",
|
||||||
|
"available": true,
|
||||||
|
"image": {
|
||||||
|
"type": "thumb",
|
||||||
|
"path": "https://resources.tidal.com/images/713805f3/c08c/4c0f/8199/d63e6badac0d/750x750.jpg",
|
||||||
|
"provider": "tidal",
|
||||||
|
"remotely_accessible": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disc_number": 1,
|
||||||
|
"track_number": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
65
tests/components/music_assistant/test_media_browser.py
Normal file
65
tests/components/music_assistant/test_media_browser.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""Test Music Assistant media browser implementation."""
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.media_player import BrowseError, BrowseMedia, MediaType
|
||||||
|
from homeassistant.components.music_assistant.const import DOMAIN
|
||||||
|
from homeassistant.components.music_assistant.media_browser import (
|
||||||
|
LIBRARY_ALBUMS,
|
||||||
|
LIBRARY_ARTISTS,
|
||||||
|
LIBRARY_PLAYLISTS,
|
||||||
|
LIBRARY_RADIO,
|
||||||
|
LIBRARY_TRACKS,
|
||||||
|
async_browse_media,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .common import setup_integration_from_fixtures
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("media_content_id", "media_content_type", "expected"),
|
||||||
|
[
|
||||||
|
(LIBRARY_PLAYLISTS, MediaType.PLAYLIST, "library://playlist/40"),
|
||||||
|
(LIBRARY_ARTISTS, MediaType.ARTIST, "library://artist/127"),
|
||||||
|
(LIBRARY_ALBUMS, MediaType.ALBUM, "library://album/396"),
|
||||||
|
(LIBRARY_TRACKS, MediaType.TRACK, "library://track/486"),
|
||||||
|
(LIBRARY_RADIO, DOMAIN, "library://radio/1"),
|
||||||
|
("artist", MediaType.ARTIST, "library://album/115"),
|
||||||
|
("album", MediaType.ALBUM, "library://track/247"),
|
||||||
|
("playlist", DOMAIN, "tidal--Ah76MuMg://track/77616130"),
|
||||||
|
(None, None, "artists"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_browse_media_root(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
music_assistant_client: MagicMock,
|
||||||
|
media_content_id: str,
|
||||||
|
media_content_type: str,
|
||||||
|
expected: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test the async_browse_media method."""
|
||||||
|
await setup_integration_from_fixtures(hass, music_assistant_client)
|
||||||
|
entity_id = "media_player.test_player_1"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
browse_item: BrowseMedia = await async_browse_media(
|
||||||
|
hass, music_assistant_client, media_content_id, media_content_type
|
||||||
|
)
|
||||||
|
assert browse_item.children[0].media_content_id == expected
|
||||||
|
|
||||||
|
|
||||||
|
async def test_browse_media_not_found(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
music_assistant_client: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the async_browse_media method when media is not found."""
|
||||||
|
await setup_integration_from_fixtures(hass, music_assistant_client)
|
||||||
|
entity_id = "media_player.test_player_1"
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state
|
||||||
|
|
||||||
|
with pytest.raises(BrowseError, match="Media not found: unknown / unknown"):
|
||||||
|
await async_browse_media(hass, music_assistant_client, "unknown", "unknown")
|
Loading…
x
Reference in New Issue
Block a user