From: Marcel van der Veldt Date: Thu, 23 Jun 2022 13:41:57 +0000 (+0200) Subject: fix global search caching X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=531929a998dd8d36c4822b4a9d90baa24d5a6fb3;p=music-assistant-server.git fix global search caching --- diff --git a/music_assistant/controllers/music/__init__.py b/music_assistant/controllers/music/__init__.py index 8f6a4d88..786a348d 100755 --- a/music_assistant/controllers/music/__init__.py +++ b/music_assistant/controllers/music/__init__.py @@ -20,7 +20,7 @@ from music_assistant.helpers.uri import parse_uri from music_assistant.models.config import MusicProviderConfig from music_assistant.models.enums import MediaType, ProviderType from music_assistant.models.errors import MusicAssistantError, SetupFailedError -from music_assistant.models.media_items import MediaItem, MediaItemType +from music_assistant.models.media_items import MediaItem, MediaItemType, media_from_dict from music_assistant.models.music_provider import MusicProvider from music_assistant.music_providers.filesystem import FileSystemProvider from music_assistant.music_providers.qobuz import QobuzProvider @@ -132,7 +132,7 @@ class MusicController: :param limit: number of items to return in the search (per type). """ # include results from all music providers - provider_ids = ["database"] + [item.id for item in self.providers] + provider_ids = [item.id for item in self.providers] # TODO: sort by name and filter out duplicates ? return await asyncio.gather( *[ @@ -159,19 +159,32 @@ class MusicController: :param media_types: A list of media_types to include. All types if None. :param limit: number of items to return in the search (per type). """ - if provider == ProviderType.DATABASE or provider_id == "database": - # get results from database - return ( - await self.artists.search(search_query, provider, provider_id, limit) - + await self.albums.search(search_query, provider, provider_id, limit) - + await self.tracks.search(search_query, provider, provider_id, limit) - + await self.playlists.search( - search_query, provider, provider_id, limit - ) - + await self.radio.search(search_query, provider, provider_id, limit) + assert provider or provider_id, "Provider needs to be supplied" + prov = self.get_provider(provider_id or provider) + await provider.search(search_query, media_types, limit) + + # create safe search string + search_query = search_query.replace("/", " ").replace("'", "") + + # prefer cache items (if any) + cache_key = f"{prov.type.value}.search.{search_query}.{limit}" + cache_key += "".join(media_types) + + if cache := await self.mass.cache.get(cache_key): + return [media_from_dict(x) for x in cache] + # no items in cache - get listing from provider + items = await prov.search( + search_query, + media_types, + limit, + ) + # store (serializable items) in cache + self.mass.create_task( + self.mass.cache.set( + cache_key, [x.to_dict() for x in items], expiration=86400 * 7 ) - provider = self.get_provider(provider_id or provider) - return await provider.search(search_query, media_types, limit) + ) + return items async def get_item_by_uri( self, uri: str, force_refresh: bool = False, lazy: bool = True diff --git a/music_assistant/models/media_controller.py b/music_assistant/models/media_controller.py index 0e436b73..fc7f5e04 100644 --- a/music_assistant/models/media_controller.py +++ b/music_assistant/models/media_controller.py @@ -144,9 +144,8 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): limit: int = 25, ) -> List[ItemCls]: """Search database or provider with given query.""" - search_query = search_query.replace("/", " ").replace( - "'", "" - ) # safe search string + # create safe search string + search_query = search_query.replace("/", " ").replace("'", "") if provider == ProviderType.DATABASE or provider_id == "database": return [ self.item_cls.from_db_row(db_row) @@ -160,7 +159,9 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): return {} # prefer cache items (if any) - cache_key = f"{prov.type.value}.search.{self.media_type.value}" + cache_key = ( + f"{prov.type.value}.search.{self.media_type.value}.{search_query}.{limit}" + ) if cache := await self.mass.cache.get(cache_key): return [self.media_type.from_dict(x) for x in cache] # no items in cache - get listing from provider @@ -171,7 +172,9 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): ) # store (serializable items) in cache self.mass.create_task( - self.mass.cache.set(cache_key, [x.to_dict() for x in items]) + self.mass.cache.set( + cache_key, [x.to_dict() for x in items], expiration=86400 * 7 + ) ) return items diff --git a/music_assistant/models/media_items.py b/music_assistant/models/media_items.py index 0a075dfd..7b89b001 100755 --- a/music_assistant/models/media_items.py +++ b/music_assistant/models/media_items.py @@ -344,6 +344,21 @@ class Radio(MediaItem): MediaItemType = Union[Artist, Album, Track, Radio, Playlist] +def media_from_dict(media_item: dict) -> MediaItemType: + """Return MediaItem from dict.""" + if media_item["media_type"] == "artist": + return Artist.from_dict(media_item) + if media_item["media_type"] == "album": + return Album.from_dict(media_item) + if media_item["media_type"] == "track": + return Track.from_dict(media_item) + if media_item["media_type"] == "playlist": + return Playlist.from_dict(media_item) + if media_item["media_type"] == "radio": + return Radio.from_dict(media_item) + return MediaItem.from_dict(media_item) + + @dataclass class StreamDetails(DataClassDictMixin): """Model for streamdetails."""