From 7507cc8b423641ed9e07123ec5263c237a98a37a Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 21 Aug 2024 12:19:58 +0200 Subject: [PATCH] Fix (radio) browse listings (#1587) --- .../server/models/music_provider.py | 67 ++++++++++-- .../server/providers/radiobrowser/__init__.py | 100 ++++++++---------- 2 files changed, 105 insertions(+), 62 deletions(-) diff --git a/music_assistant/server/models/music_provider.py b/music_assistant/server/models/music_provider.py index e77b7aa7..051ee33d 100644 --- a/music_assistant/server/models/music_provider.py +++ b/music_assistant/server/models/music_provider.py @@ -4,7 +4,7 @@ from __future__ import annotations import asyncio from collections.abc import Sequence -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from music_assistant.common.models.enums import CacheCategory, MediaType, ProviderFeature from music_assistant.common.models.errors import MediaNotFoundError, MusicAssistantError @@ -300,15 +300,70 @@ class MusicProvider(Provider): subpath = path.split("://", 1)[1] # this reference implementation can be overridden with a provider specific approach if subpath == "artists": - return await self.mass.music.artists.library_items(provider=self.instance_id) + library_items = await self.mass.cache.get( + "artist", + default=[], + category=CacheCategory.LIBRARY_ITEMS, + base_key=self.instance_id, + ) + library_items = cast(list[int], library_items) + query = "artists.item_id in :ids" + query_params = {"ids": library_items} + return await self.mass.music.artists.library_items( + provider=self.instance_id, extra_query=query, extra_query_params=query_params + ) if subpath == "albums": - return await self.mass.music.albums.library_items(provider=self.instance_id) + library_items = await self.mass.cache.get( + "album", + default=[], + category=CacheCategory.LIBRARY_ITEMS, + base_key=self.instance_id, + ) + library_items = cast(list[int], library_items) + query = "albums.item_id in :ids" + query_params = {"ids": library_items} + return await self.mass.music.albums.library_items( + extra_query=query, extra_query_params=query_params + ) if subpath == "tracks": - return await self.mass.music.tracks.library_items(provider=self.instance_id) + library_items = await self.mass.cache.get( + "track", + default=[], + category=CacheCategory.LIBRARY_ITEMS, + base_key=self.instance_id, + ) + library_items = cast(list[int], library_items) + query = "tracks.item_id in :ids" + query_params = {"ids": library_items} + return await self.mass.music.tracks.library_items( + extra_query=query, extra_query_params=query_params + ) if subpath == "radios": - return await self.mass.music.radio.library_items(provider=self.instance_id) + library_items = await self.mass.cache.get( + "radio", + default=[], + category=CacheCategory.LIBRARY_ITEMS, + base_key=self.instance_id, + ) + library_items = cast(list[int], library_items) + query = "radios.item_id in :ids" + query_params = {"ids": library_items} + return await self.mass.music.radio.library_items( + extra_query=query, extra_query_params=query_params + ) if subpath == "playlists": - return await self.mass.music.playlists.library_items(provider=self.instance_id) + library_items = await self.mass.cache.get( + "playlist", + default=[], + category=CacheCategory.LIBRARY_ITEMS, + base_key=self.instance_id, + ) + library_items = cast(list[int], library_items) + query = "playlists.item_id in :ids" + query_params = {"ids": library_items} + return await self.mass.music.playlists.library_items( + extra_query=query, extra_query_params=query_params + ) if subpath: # unknown path msg = "Invalid subpath" diff --git a/music_assistant/server/providers/radiobrowser/__init__.py b/music_assistant/server/providers/radiobrowser/__init__.py index 6ecc0141..dde24e19 100644 --- a/music_assistant/server/providers/radiobrowser/__init__.py +++ b/music_assistant/server/providers/radiobrowser/__init__.py @@ -139,8 +139,9 @@ class RadioBrowserProvider(MusicProvider): :param path: The path to browse, (e.g. provid://artists). """ - subpath = path.split("://", 1)[1] - subsubpath = "" if "/" not in subpath else subpath.split("/")[-1] + part_parts = path.split("://")[1].split("/") + subpath = part_parts[0] if part_parts else "" + subsubpath = part_parts[1] if len(part_parts) > 1 else "" if not subpath: # return main listing @@ -171,50 +172,18 @@ class RadioBrowserProvider(MusicProvider): if subpath == "popular": return await self.get_by_popularity() + if subpath == "tag" and subsubpath: + return await self.get_by_tag(subsubpath) + if subpath == "tag": - tags = await self.radios.tags( - hide_broken=True, - order=Order.STATION_COUNT, - reverse=True, - ) - tags.sort(key=lambda tag: tag.name) - return [ - BrowseFolder( - item_id=tag.name.lower(), - provider=self.domain, - path=path + "/" + tag.name.lower(), - name=tag.name, - ) - for tag in tags - ] + return await self.get_tag_folders(path) - if subpath == "country": - items: list[BrowseFolder | Radio] = [] - for country in await self.radios.countries(order=Order.NAME, hide_broken=True): - folder = BrowseFolder( - item_id=country.code.lower(), - provider=self.domain, - path=path + "/" + country.code.lower(), - name=country.name, - ) - folder.metadata.images = UniqueList( - [ - MediaItemImage( - type=ImageType.THUMB, - path=country.favicon, - provider=self.lookup_key, - remotely_accessible=True, - ) - ] - ) - items.append(folder) - return items + if subpath == "country" and subsubpath: + return await self.get_by_country(subsubpath) - if subsubpath in await self.get_tag_names(): - return await self.get_by_tag(subsubpath) + if subpath == "country": + return await self.get_country_folders(path) - if subsubpath in await self.get_country_codes(): - return await self.get_by_country(subsubpath) return [] async def get_library_radios(self) -> AsyncGenerator[Radio, None]: @@ -254,28 +223,47 @@ class RadioBrowserProvider(MusicProvider): return True @use_cache(3600 * 24) - async def get_tag_names(self) -> Sequence[str]: - """Get a list of tag names.""" + async def get_tag_folders(self, base_path: str) -> list[BrowseFolder]: + """Get a list of tag names as BrowseFolder.""" tags = await self.radios.tags( hide_broken=True, - limit=10000, order=Order.STATION_COUNT, reverse=True, ) tags.sort(key=lambda tag: tag.name) - tag_names = [] - for tag in tags: - tag_names.append(tag.name.lower()) - return tag_names + return [ + BrowseFolder( + item_id=tag.name.lower(), + provider=self.domain, + path=base_path + "/" + tag.name.lower(), + name=tag.name, + ) + for tag in tags + ] @use_cache(3600 * 24) - async def get_country_codes(self) -> Sequence[str]: - """Get a list of country names.""" - countries = await self.radios.countries(order=Order.NAME, hide_broken=True) - country_codes = [] - for country in countries: - country_codes.append(country.code.lower()) - return country_codes + async def get_country_folders(self, base_path: str) -> list[BrowseFolder]: + """Get a list of country names as BrowseFolder.""" + items: list[BrowseFolder] = [] + for country in await self.radios.countries(order=Order.NAME, hide_broken=True): + folder = BrowseFolder( + item_id=country.code.lower(), + provider=self.domain, + path=base_path + "/" + country.code.lower(), + name=country.name, + ) + folder.metadata.images = UniqueList( + [ + MediaItemImage( + type=ImageType.THUMB, + path=country.favicon, + provider=self.lookup_key, + remotely_accessible=True, + ) + ] + ) + items.append(folder) + return items @use_cache(3600) async def get_by_popularity(self) -> Sequence[Radio]: -- 2.34.1