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:
Jozef Kruszynski 2024-11-27 02:52:08 +01:00 committed by GitHub
parent f04c50c59e
commit 40a4ff1c84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 2096 additions and 3 deletions

View 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,
)

View File

@ -43,6 +43,7 @@ from homeassistant.util.dt import utc_from_timestamp
from . import MusicAssistantConfigEntry
from .const import ATTR_ACTIVE_QUEUE, ATTR_MASS_PLAYER_TYPE, DOMAIN
from .entity import MusicAssistantEntity
from .media_browser import async_browse_media
if TYPE_CHECKING:
from music_assistant_client import MusicAssistantClient
@ -440,10 +441,11 @@ class MusicAssistantPlayer(MusicAssistantEntity, MediaPlayerEntity):
media_content_id: str | None = None,
) -> BrowseMedia:
"""Implement the websocket media browsing helper."""
return await media_source.async_browse_media(
return await async_browse_media(
self.hass,
self.mass,
media_content_id,
content_filter=lambda item: item.media_content_type.startswith("audio/"),
media_content_type,
)
def _update_media_image_url(

View File

@ -3,9 +3,10 @@
from __future__ import annotations
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.media_items import Album, Artist, Playlist, Radio, Track
from music_assistant_models.player import Player
from music_assistant_models.player_queue import PlayerQueue
from syrupy import SnapshotAssertion
@ -42,6 +43,25 @@ async def setup_integration_from_fixtures(
data={"url": MOCK_URL},
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)
assert await hass.config_entries.async_setup(config_entry.entry_id)
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(
hass: HomeAssistant,
client: MagicMock,

View File

@ -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
}
]
}

View 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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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
}
]
}

View File

@ -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
}
]
}

View File

@ -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"
}
]
}

View File

@ -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
}
]
}

View 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
}
]
}

View 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")