From: Marcel van der Veldt Date: Wed, 19 Jun 2024 17:21:04 +0000 (+0200) Subject: Improve library performance (#1389) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=2397b783d3ba73762729afc7345332c1c1a2c244;p=music-assistant-server.git Improve library performance (#1389) --- diff --git a/music_assistant/constants.py b/music_assistant/constants.py index 83feaebf..fbb224c3 100644 --- a/music_assistant/constants.py +++ b/music_assistant/constants.py @@ -5,7 +5,7 @@ from typing import Final API_SCHEMA_VERSION: Final[int] = 24 MIN_SCHEMA_VERSION: Final[int] = 24 -DB_SCHEMA_VERSION: Final[int] = 1 +DB_SCHEMA_VERSION: Final[int] = 2 MASS_LOGGER_NAME: Final[str] = "music_assistant" diff --git a/music_assistant/server/controllers/media/albums.py b/music_assistant/server/controllers/media/albums.py index 2bf851f5..7ba29a0a 100644 --- a/music_assistant/server/controllers/media/albums.py +++ b/music_assistant/server/controllers/media/albums.py @@ -30,6 +30,7 @@ from music_assistant.constants import ( DB_TABLE_ALBUM_TRACKS, DB_TABLE_ALBUMS, DB_TABLE_ARTISTS, + DB_TABLE_PROVIDER_MAPPINGS, ) from music_assistant.server.controllers.media.base import MediaControllerBase from music_assistant.server.helpers.compare import ( @@ -53,11 +54,11 @@ class AlbumsController(MediaControllerBase[Album]): """Initialize class.""" super().__init__(*args, **kwargs) self.base_query = f""" - SELECT DISTINCT - {self.db_table}.* - FROM {self.db_table} + SELECT DISTINCT {self.db_table}.* FROM {self.db_table} LEFT JOIN {DB_TABLE_ALBUM_ARTISTS} on {DB_TABLE_ALBUM_ARTISTS}.album_id = {self.db_table}.item_id LEFT JOIN {DB_TABLE_ARTISTS} on {DB_TABLE_ARTISTS}.item_id = {DB_TABLE_ALBUM_ARTISTS}.artist_id + LEFT JOIN {DB_TABLE_PROVIDER_MAPPINGS} ON + {DB_TABLE_PROVIDER_MAPPINGS}.item_id = {self.db_table}.item_id AND media_type = '{self.media_type}' """ # noqa: E501 # register (extra) api handlers api_base = self.api_base diff --git a/music_assistant/server/controllers/media/artists.py b/music_assistant/server/controllers/media/artists.py index e6c1dec8..310b9a11 100644 --- a/music_assistant/server/controllers/media/artists.py +++ b/music_assistant/server/controllers/media/artists.py @@ -25,10 +25,8 @@ from music_assistant.common.models.media_items import ( ) from music_assistant.constants import ( DB_TABLE_ALBUM_ARTISTS, - DB_TABLE_ALBUMS, DB_TABLE_ARTISTS, DB_TABLE_TRACK_ARTISTS, - DB_TABLE_TRACKS, VARIOUS_ARTISTS_ID_MBID, VARIOUS_ARTISTS_NAME, ) @@ -59,15 +57,17 @@ class ArtistsController(MediaControllerBase[Artist]): self, favorite_only: bool = False, album_artists_only: bool = False ) -> int: """Return the total number of items in the library.""" - sql_query = self.base_query + sql_query = f"SELECT item_id FROM {self.db_table}" + query_parts: list[str] = [] if favorite_only: - sql_query += f" WHERE {self.db_table}.favorite = 1" + query_parts.append("favorite = 1") if album_artists_only: - sql_query += " WHERE " if "WHERE" not in sql_query else " AND " - sql_query += ( - f"artists.item_id in (select {DB_TABLE_ALBUM_ARTISTS}.artist_id " - f"from {DB_TABLE_ALBUM_ARTISTS})" + query_parts.append( + f"item_id in (select {DB_TABLE_ALBUM_ARTISTS}.artist_id " + f"FROM {DB_TABLE_ALBUM_ARTISTS})" ) + if query_parts: + sql_query += f" WHERE {' AND '.join(query_parts)}" return await self.mass.music.database.get_count_from_query(sql_query) async def library_items( @@ -223,14 +223,10 @@ class ArtistsController(MediaControllerBase[Artist]): item_id, provider_instance_id_or_domain, ): - subquery = ( - "SELECT item_id FROM provider_mappings WHERE " - "media_type = 'track' AND (provider_domain = :prov_id " - "OR provider_instance = :prov_id)" - ) query = ( - f"WHERE {DB_TABLE_TRACKS}.item_id IN ({subquery}) " - f"AND {DB_TABLE_TRACK_ARTISTS}.artist_id = :artist_id" + f"WHERE {DB_TABLE_TRACK_ARTISTS}.artist_id = :artist_id " + "AND (provider_domain = :prov_id " + "OR provider_instance = :prov_id)" ) query_params = { "artist_id": db_artist.item_id, @@ -281,14 +277,10 @@ class ArtistsController(MediaControllerBase[Artist]): item_id, provider_instance_id_or_domain, ): - subquery = ( - "SELECT item_id FROM provider_mappings WHERE " - "media_type = 'album' AND (provider_domain = :prov_id " - "OR provider_instance = :prov_id)" - ) query = ( - f"WHERE {DB_TABLE_ALBUMS}.item_id IN ({subquery}) " - f"AND {DB_TABLE_ALBUM_ARTISTS}.artist_id = :artist_id" + f"WHERE {DB_TABLE_ALBUM_ARTISTS}.artist_id = :artist_id " + "AND (provider_domain = :prov_id " + "OR provider_instance = :prov_id)" ) query_params = { "prov_id": provider_instance_id_or_domain, diff --git a/music_assistant/server/controllers/media/base.py b/music_assistant/server/controllers/media/base.py index 5cfcc14e..1713f5c7 100644 --- a/music_assistant/server/controllers/media/base.py +++ b/music_assistant/server/controllers/media/base.py @@ -22,7 +22,6 @@ from music_assistant.common.models.media_items import ( media_from_dict, ) from music_assistant.constants import ( - DB_TABLE_ALBUMS, DB_TABLE_ARTISTS, DB_TABLE_PLAYLOG, DB_TABLE_PROVIDER_MAPPINGS, @@ -43,8 +42,8 @@ JSON_KEYS = ("artists", "album", "metadata", "provider_mappings", "external_ids" SORT_KEYS = { "name": "name COLLATE NOCASE ASC", "name_desc": "name COLLATE NOCASE DESC", - "sort_name": "sort_name ASC", - "sort_name_desc": "sort_name DESC", + "sort_name": "sort_name COLLATE NOCASE ASC", + "sort_name_desc": "sort_name COLLATE NOCASE DESC", "timestamp_added": "timestamp_added ASC", "timestamp_added_desc": "timestamp_added DESC", "timestamp_modified": "timestamp_modified ASC", @@ -53,16 +52,13 @@ SORT_KEYS = { "last_played_desc": "last_played DESC", "play_count": "play_count ASC", "play_count_desc": "play_count DESC", - "artist": "artists.name COLLATE NOCASE ASC", - "album": "albums.name COLLATE NOCASE ASC", - "sort_artist": "artists.sort_name ASC", - "sort_album": "albums.sort_name ASC", "year": "year ASC", "year_desc": "year DESC", "position": "position ASC", "position_desc": "position DESC", "random": "RANDOM()", - "random_play_count": "RANDOM(), play_count", + "random_play_count": "random(), play_count ASC", + "random_fast": "play_count ASC", # this one is handled with a special query } @@ -76,7 +72,12 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): def __init__(self, mass: MusicAssistant) -> None: """Initialize class.""" self.mass = mass - self.base_query = f"SELECT * FROM {self.db_table}" + self.base_query = ( + f"SELECT DISTINCT {self.db_table}.* FROM {self.db_table} " + f"LEFT JOIN {DB_TABLE_PROVIDER_MAPPINGS} ON " + f"{DB_TABLE_PROVIDER_MAPPINGS}.item_id = {self.db_table}.item_id " + f"AND media_type = '{self.media_type}'" + ) self.logger = logging.getLogger(f"{MASS_LOGGER_NAME}.music.{self.media_type.value}") # register (base) api handlers self.api_base = api_base = f"{self.media_type}s" @@ -193,10 +194,10 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): async def library_count(self, favorite_only: bool = False) -> int: """Return the total number of items in the library.""" - sql_query = self.base_query if favorite_only: - sql_query += f" WHERE {self.db_table}.favorite = 1" - return await self.mass.music.database.get_count_from_query(sql_query) + sql_query = f"SELECT item_id FROM {self.db_table} WHERE favorite = 1" + return await self.mass.music.database.get_count_from_query(sql_query) + return await self.mass.music.database.get_count(self.db_table) async def library_items( self, @@ -209,6 +210,12 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): extra_query_params: dict[str, Any] | None = None, ) -> list[ItemCls]: """Get in-database items.""" + # create special performant random query + if order_by == "random_fast" and not extra_query: + extra_query = ( + f"{self.db_table}.rowid > (ABS(RANDOM()) % " + f"(SELECT max({self.db_table}.rowid) FROM {self.db_table}))" + ) return await self._get_library_items_by_query( favorite=favorite, search=search, @@ -478,25 +485,21 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): assert provider_instance_id_or_domain != "library" assert provider_domain != "library" assert provider_instance != "library" - subquery = f"WHERE provider_mappings.media_type = '{self.media_type.value}' " if provider_instance: query_params = {"prov_id": provider_instance} - subquery += "AND provider_mappings.provider_instance = :prov_id" + query = "provider_mappings.provider_instance = :prov_id" elif provider_domain: query_params = {"prov_id": provider_domain} - subquery += "AND provider_mappings.provider_domain = :prov_id" + query = "provider_mappings.provider_domain = :prov_id" else: query_params = {"prov_id": provider_instance_id_or_domain} - subquery += ( - "AND (provider_mappings.provider_instance = :prov_id " - "OR provider_mappings.provider_domain = :prov_id) " + query = ( + "(provider_mappings.provider_instance = :prov_id " + "OR provider_mappings.provider_domain = :prov_id)" ) if provider_item_id: - subquery += " AND provider_mappings.provider_item_id = :item_id" + query += " AND provider_mappings.provider_item_id = :item_id" query_params["item_id"] = provider_item_id - query = ( - f"WHERE {self.db_table}.item_id in (SELECT item_id FROM provider_mappings {subquery})" - ) return await self._get_library_items_by_query( limit=limit, offset=offset, extra_query=query, extra_query_params=query_params ) @@ -751,32 +754,30 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): sql_query = self.base_query query_params = extra_query_params or {} query_parts: list[str] = [] - # handle extra/custom query - if extra_query: - # prevent duplicate where statement - if extra_query.lower().startswith("where "): - extra_query = extra_query[5:] - query_parts.append(extra_query) # handle basic search on name if search: - query_params["search"] = f"%{search}%" - if self.media_type == MediaType.ALBUM: + # handle combined artist + title search + if self.media_type in (MediaType.ALBUM, MediaType.TRACK) and " - " in search: + artist_str, title_str = search.split(" - ", 1) query_parts.append( - f"({self.db_table}.name LIKE :search " - f"OR {DB_TABLE_ARTISTS}.name LIKE :search)" - ) - elif self.media_type == MediaType.TRACK: - query_parts.append( - f"({self.db_table}.name LIKE :search " - f"OR {DB_TABLE_ARTISTS}.name LIKE :search " - f"OR {DB_TABLE_ALBUMS}.name LIKE :search)" + f"({self.db_table}.name LIKE :search_title " + f"AND {DB_TABLE_ARTISTS}.name LIKE :search_artist)" ) + query_params["search_title"] = f"%{title_str}%" + query_params["search_artist"] = f"%{artist_str}%" else: + query_params["search"] = f"%{search}%" query_parts.append(f"{self.db_table}.name LIKE :search") # handle favorite filter if favorite is not None: query_parts.append(f"{self.db_table}.favorite = :favorite") query_params["favorite"] = favorite + # handle extra/custom query + if extra_query: + # prevent duplicate where statement + if extra_query.lower().startswith("where "): + extra_query = extra_query[5:] + query_parts.append(extra_query) # concetenate all where queries if query_parts: sql_query += " WHERE " + " AND ".join(query_parts) @@ -784,9 +785,6 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): if order_by: if sort_key := SORT_KEYS.get(order_by): sql_query += f" ORDER BY {sort_key}" - else: - self.logger.warning("%s is not a valid sort option!", order_by) - # return dbresult parsed to media item model return [ self.item_cls.from_dict(self._parse_db_row(db_row)) diff --git a/music_assistant/server/controllers/media/tracks.py b/music_assistant/server/controllers/media/tracks.py index 2850dc8d..33f91a43 100644 --- a/music_assistant/server/controllers/media/tracks.py +++ b/music_assistant/server/controllers/media/tracks.py @@ -19,6 +19,7 @@ from music_assistant.constants import ( DB_TABLE_ALBUM_TRACKS, DB_TABLE_ALBUMS, DB_TABLE_ARTISTS, + DB_TABLE_PROVIDER_MAPPINGS, DB_TABLE_TRACK_ARTISTS, DB_TABLE_TRACKS, ) @@ -60,6 +61,8 @@ class TracksController(MediaControllerBase[Track]): LEFT JOIN {DB_TABLE_ALBUMS} on {DB_TABLE_ALBUMS}.item_id = {DB_TABLE_ALBUM_TRACKS}.album_id LEFT JOIN {DB_TABLE_TRACK_ARTISTS} on {DB_TABLE_TRACK_ARTISTS}.track_id = {self.db_table}.item_id LEFT JOIN {DB_TABLE_ARTISTS} on {DB_TABLE_ARTISTS}.item_id = {DB_TABLE_TRACK_ARTISTS}.artist_id + LEFT JOIN {DB_TABLE_PROVIDER_MAPPINGS} ON + {DB_TABLE_PROVIDER_MAPPINGS}.item_id = {self.db_table}.item_id AND media_type = '{self.media_type}' """ # noqa: E501 # register (extra) api handlers api_base = self.api_base diff --git a/music_assistant/server/controllers/music.py b/music_assistant/server/controllers/music.py index 07db9324..295ef525 100644 --- a/music_assistant/server/controllers/music.py +++ b/music_assistant/server/controllers/music.py @@ -175,7 +175,7 @@ class MusicController(CoreController): self, search_query: str, media_types: list[MediaType] = MediaType.ALL, - limit: int = 50, + limit: int = 25, ) -> SearchResults: """Perform global search for media items on all providers. @@ -356,7 +356,7 @@ class MusicController(CoreController): ) -> list[MediaItemType]: """Return a list of the last played items.""" if media_types is None: - media_types = [MediaType.TRACK, MediaType.RADIO] + media_types = MediaType.ALL media_types_str = "(" + ",".join(f'"{x}"' for x in media_types) + ")" query = ( f"SELECT * FROM {DB_TABLE_PLAYLOG} WHERE media_type " @@ -370,7 +370,13 @@ class MusicController(CoreController): with suppress(MediaNotFoundError, ProviderUnavailableError): media_type = MediaType(db_row["media_type"]) ctrl = self.get_controller(media_type) - item = await ctrl.get_provider_item(db_row["item_id"], db_row["provider"]) + item = await ctrl.get( + db_row["item_id"], + db_row["provider"], + add_to_library=False, + lazy=True, + force_refresh=False, + ) result.append(item) return result @@ -590,16 +596,17 @@ class MusicController(CoreController): """Mark item as played in playlog.""" timestamp = utc_timestamp() + if provider_instance_id_or_domain == "builtin": + # we deliberately skip builtin provider items as those are often + # one-off items like TTS or some sound effect etc. + return + if provider_instance_id_or_domain == "library": prov_key = "library" elif prov := self.mass.get_provider(provider_instance_id_or_domain): prov_key = prov.lookup_key else: prov_key = provider_instance_id_or_domain - # do not try to store dynamic urls (e.g. with auth token etc.), - # stick with plain uri/urls only - if "http" in item_id and "?" in item_id: - return # update generic playlog table await self.database.insert( @@ -892,6 +899,20 @@ class MusicController(CoreController): async def __migrate_database(self, prev_version: int) -> None: """Perform a database migration.""" + self.logger.info( + "Migrating database from version %s to %s", prev_version, DB_SCHEMA_VERSION + ) + if prev_version == 1: + # migrate from version 1 to 2 + await self.database.execute( + f"DELETE FROM {DB_TABLE_PLAYLOG} WHERE provider = 'builtin'" + ) + await self.database.commit() + return + + # all other versions: reset the database + # we only migrate from prtev version to current we do not try to handle + # more complex migrations self.logger.warning( "Database schema too old - Resetting library/database - " "a full rescan will be performed, this can take a while!" @@ -1100,19 +1121,40 @@ class MusicController(CoreController): await self.database.execute( f"CREATE INDEX IF NOT EXISTS {db_table}_name_idx on {db_table}(name);" ) + # index on name (without case sensitivity) + await self.database.execute( + f"CREATE INDEX IF NOT EXISTS {db_table}_name_nocase_idx " + f"ON {db_table}(name COLLATE NOCASE);" + ) # index on sort_name await self.database.execute( f"CREATE INDEX IF NOT EXISTS {db_table}_sort_name_idx on {db_table}(sort_name);" ) + # index on sort_name (without case sensitivity) + await self.database.execute( + f"CREATE INDEX IF NOT EXISTS {db_table}_sort_name_nocase_idx " + f"ON {db_table}(sort_name COLLATE NOCASE);" + ) # index on external_ids await self.database.execute( - f"CREATE INDEX IF NOT EXISTS {db_table}_external_ids_idx on {db_table}(external_ids);" # noqa: E501 + f"CREATE INDEX IF NOT EXISTS {db_table}_external_ids_idx " + f"ON {db_table}(external_ids);" ) # index on timestamp_added await self.database.execute( f"CREATE INDEX IF NOT EXISTS {db_table}_timestamp_added_idx " f"on {db_table}(timestamp_added);" ) + # index on play_count + await self.database.execute( + f"CREATE INDEX IF NOT EXISTS {db_table}_play_count_idx " + f"on {db_table}(play_count);" + ) + # index on last_played + await self.database.execute( + f"CREATE INDEX IF NOT EXISTS {db_table}_last_played_idx " + f"on {db_table}(last_played);" + ) # indexes on provider_mappings table await self.database.execute( @@ -1127,6 +1169,16 @@ class MusicController(CoreController): f"CREATE UNIQUE INDEX IF NOT EXISTS {DB_TABLE_PROVIDER_MAPPINGS}_provider_instance_idx " f"on {DB_TABLE_PROVIDER_MAPPINGS}(media_type,provider_instance,provider_item_id);" ) + await self.database.execute( + "CREATE INDEX IF NOT EXISTS " + f"{DB_TABLE_PROVIDER_MAPPINGS}_media_type_provider_instance_idx " + f"on {DB_TABLE_PROVIDER_MAPPINGS}(media_type,provider_instance);" + ) + await self.database.execute( + "CREATE INDEX IF NOT EXISTS " + f"{DB_TABLE_PROVIDER_MAPPINGS}_media_type_provider_domain_idx " + f"on {DB_TABLE_PROVIDER_MAPPINGS}(media_type,provider_domain);" + ) # indexes on track_artists table await self.database.execute( diff --git a/music_assistant/server/helpers/database.py b/music_assistant/server/helpers/database.py index bccf02a1..12375061 100644 --- a/music_assistant/server/helpers/database.py +++ b/music_assistant/server/helpers/database.py @@ -23,7 +23,7 @@ ENABLE_DEBUG = os.environ.get("PYTHONDEVMODE") == "1" @asynccontextmanager -async def debug_query(sql_query: str): +async def debug_query(sql_query: str, query_params: dict | None = None): """Time the processing time of an sql query.""" if not ENABLE_DEBUG: yield @@ -37,7 +37,10 @@ async def debug_query(sql_query: str): finally: process_time = time.time() - time_start if process_time > 0.5: - LOGGER.warning("SQL Query took %s seconds! (\n%s", process_time, sql_query) + # log slow queries + for key, value in (query_params or {}).items(): + sql_query = sql_query.replace(f":{key}", repr(value)) + LOGGER.warning("SQL Query took %s seconds! (\n%s\n", process_time, sql_query) def query_params(query: str, params: dict[str, Any] | None) -> tuple[str, dict[str, Any]]: @@ -117,7 +120,7 @@ class DatabaseConnection: if limit: query += f" LIMIT {limit} OFFSET {offset}" _query, _params = query_params(query, params) - async with debug_query(_query): + async with debug_query(_query, _params): return await self._db.execute_fetchall(_query, _params) async def get_count_from_query( @@ -150,14 +153,14 @@ class DatabaseConnection: """Search table by column.""" sql_query = f"SELECT * FROM {table} WHERE {table}.{column} LIKE :search" params = {"search": f"%{search}%"} - async with debug_query(sql_query): + async with debug_query(sql_query, params): return await self._db.execute_fetchall(sql_query, params) async def get_row(self, table: str, match: dict[str, Any]) -> Mapping | None: """Get single row for given table where column matches keys/values.""" sql_query = f"SELECT * FROM {table} WHERE " sql_query += " AND ".join(f"{table}.{x} = :{x}" for x in match) - async with debug_query(sql_query), self._db.execute(sql_query, match) as cursor: + async with debug_query(sql_query, match), self._db.execute(sql_query, match) as cursor: return await cursor.fetchone() async def insert( diff --git a/music_assistant/server/providers/builtin/__init__.py b/music_assistant/server/providers/builtin/__init__.py index cd6e9b08..0da09eaf 100644 --- a/music_assistant/server/providers/builtin/__init__.py +++ b/music_assistant/server/providers/builtin/__init__.py @@ -514,23 +514,21 @@ class BuiltinProvider(MusicProvider): result: list[Track] = [] if builtin_playlist_id == ALL_FAVORITE_TRACKS: res = await self.mass.music.tracks.library_items( - favorite=True, limit=2500, order_by="random_play_count" + favorite=True, limit=250000, order_by="random" ) for idx, item in enumerate(res, 1): item.position = idx result.append(item) return result if builtin_playlist_id == RANDOM_TRACKS: - res = await self.mass.music.tracks.library_items( - limit=500, order_by="random_play_count" - ) + res = await self.mass.music.tracks.library_items(limit=500, order_by="random_fast") for idx, item in enumerate(res, 1): item.position = idx result.append(item) return result if builtin_playlist_id == RANDOM_ALBUM: for random_album in await self.mass.music.albums.library_items( - limit=1, order_by="random" + limit=1, order_by="random_fast" ): # use the function specified in the queue controller as that # already handles unwrapping an album by user preference @@ -543,7 +541,7 @@ class BuiltinProvider(MusicProvider): return result if builtin_playlist_id == RANDOM_ARTIST: for random_artist in await self.mass.music.artists.library_items( - limit=1, order_by="random" + limit=1, order_by="random_fast" ): # use the function specified in the queue controller as that # already handles unwrapping an artist by user preference diff --git a/music_assistant/server/providers/filesystem_local/base.py b/music_assistant/server/providers/filesystem_local/base.py index cfbed2db..db2ba4d0 100644 --- a/music_assistant/server/providers/filesystem_local/base.py +++ b/music_assistant/server/providers/filesystem_local/base.py @@ -174,7 +174,8 @@ class FileSystemProviderBase(MusicProvider): ---------- - path: path of the directory (relative or absolute) to list contents of. Empty string for provider's root. - - recursive: If True will recursively keep unwrapping subdirectories (scandir equivalent). + - recursive: If True will recursively keep unwrapping + subdirectories (scandir equivalent). Returns: ------- @@ -227,62 +228,28 @@ class FileSystemProviderBase(MusicProvider): """Perform search on this file based musicprovider.""" result = SearchResults() # searching the filesystem is slow and unreliable, - # instead we make some (slow) freaking queries to the db ;-) + # so instead we just query the db... + query = "provider_mappings.provider_instance = :provider_instance " params = { - "name": f"%{search_query}%", "provider_instance": self.instance_id, } - subquery = "WHERE " - # ruff: noqa: E501 if media_types is None or MediaType.TRACK in media_types: - subquery = ( - "WHERE provider_mappings.media_type = 'track' " - "AND provider_mappings.provider_instance = :provider_instance" - ) - query = ( - "WHERE tracks.name LIKE :name AND tracks.item_id in " - f"(SELECT item_id FROM provider_mappings {subquery})" - ) result.tracks = await self.mass.music.tracks._get_library_items_by_query( - extra_query=query, extra_query_params=params + search=search_query, extra_query=query, extra_query_params=params, limit=limit ) if media_types is None or MediaType.ALBUM in media_types: - subquery = ( - "WHERE provider_mappings.media_type = 'album' " - "AND provider_mappings.provider_instance = :provider_instance" - ) - query = ( - "WHERE albums.name LIKE :name AND albums.item_id in " - f"(SELECT item_id FROM provider_mappings {subquery})" - ) result.albums = await self.mass.music.albums._get_library_items_by_query( - extra_query=query, extra_query_params=params + search=search_query, extra_query=query, extra_query_params=params, limit=limit ) if media_types is None or MediaType.ARTIST in media_types: - subquery = ( - "WHERE provider_mappings.media_type = 'artist' " - "AND provider_mappings.provider_instance = :provider_instance" - ) - query = ( - "WHERE artists.name LIKE :name AND artists.item_id in " - f"(SELECT item_id FROM provider_mappings {subquery})" - ) result.artists = await self.mass.music.artists._get_library_items_by_query( - extra_query=query, extra_query_params=params + search=search_query, extra_query=query, extra_query_params=params, limit=limit ) if media_types is None or MediaType.PLAYLIST in media_types: - subquery = ( - "WHERE provider_mappings.media_type = 'playlist' " - "AND provider_mappings.provider_instance = :provider_instance" - ) - query = ( - "WHERE playlists.name LIKE :name AND playlists.item_id in " - f"(SELECT item_id FROM provider_mappings {subquery})" - ) result.playlists = await self.mass.music.playlists._get_library_items_by_query( - extra_query=query, extra_query_params=params + search=search_query, extra_query=query, extra_query_params=params, limit=limit ) return result @@ -419,8 +386,7 @@ class FileSystemProviderBase(MusicProvider): f"WHERE item_id not in " f"( select artist_id from {DB_TABLE_TRACK_ARTISTS} " f"UNION SELECT artist_id from {DB_TABLE_ALBUM_ARTISTS} )" - f"AND item_id in ( SELECT item_id from {DB_TABLE_PROVIDER_MAPPINGS} " - f"WHERE provider_instance = '{self.instance_id}' and media_type = 'artist' )" + f"AND provider_instance = '{self.instance_id}'" ) for db_row in await self.mass.music.database.get_rows_from_query( query,