From: Marcel van der Veldt Date: Sat, 20 Dec 2025 16:34:53 +0000 (+0100) Subject: Fix the 'in_library' filtering (right this time) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=bc56e22197ec7a8009c7479565adf64a18622032;p=music-assistant-server.git Fix the 'in_library' filtering (right this time) Prevent that library listings include child/parent mappings that got included from streaming providers --- diff --git a/music_assistant/controllers/media/albums.py b/music_assistant/controllers/media/albums.py index a7a4f785..36842784 100644 --- a/music_assistant/controllers/media/albums.py +++ b/music_assistant/controllers/media/albums.py @@ -113,7 +113,9 @@ class AlbumsController(MediaControllerBase[Album]): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, album_types: list[AlbumType] | None = None, + **kwargs: Any, ) -> list[Album]: """Get in-database albums. @@ -126,6 +128,8 @@ class AlbumsController(MediaControllerBase[Album]): :param extra_query: Additional SQL query string. :param extra_query_params: Additional query parameters. :param album_types: Filter by album types. + :param library_items_only: If True, only return items that are + marked as 'in_library' on any provider mapping. """ extra_query_params = extra_query_params or {} extra_query_parts: list[str] = [extra_query] if extra_query else [] @@ -170,6 +174,7 @@ class AlbumsController(MediaControllerBase[Album]): extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, extra_join_parts=extra_join_parts, + in_library_only=library_items_only, ) # Calculate how many more items we need to reach the original limit @@ -197,6 +202,7 @@ class AlbumsController(MediaControllerBase[Album]): extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, extra_join_parts=extra_join_parts, + in_library_only=library_items_only, ): # prevent duplicates (when artist is also in the title) if album.uri not in existing_uris: diff --git a/music_assistant/controllers/media/artists.py b/music_assistant/controllers/media/artists.py index d9acdd9e..7ae766dd 100644 --- a/music_assistant/controllers/media/artists.py +++ b/music_assistant/controllers/media/artists.py @@ -73,7 +73,9 @@ class ArtistsController(MediaControllerBase[Artist]): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, album_artists_only: bool = False, + **kwargs: Any, ) -> list[Artist]: """Get in-database (album) artists. @@ -86,6 +88,8 @@ class ArtistsController(MediaControllerBase[Artist]): :param extra_query: Additional SQL query string. :param extra_query_params: Additional query parameters. :param album_artists_only: Only return artists that have albums. + :param library_items_only: If True, only return items that are + marked as 'in_library' on any provider mapping. """ extra_query_params = extra_query_params or {} extra_query_parts: list[str] = [extra_query] if extra_query else [] @@ -103,6 +107,7 @@ class ArtistsController(MediaControllerBase[Artist]): provider_filter=self._ensure_provider_filter(provider), extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) async def tracks( diff --git a/music_assistant/controllers/media/audiobooks.py b/music_assistant/controllers/media/audiobooks.py index 54d37657..c35fcb69 100644 --- a/music_assistant/controllers/media/audiobooks.py +++ b/music_assistant/controllers/media/audiobooks.py @@ -69,6 +69,8 @@ class AudiobooksController(MediaControllerBase[Audiobook]): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, + **kwargs: Any, ) -> list[Audiobook]: """Get in-database audiobooks. @@ -80,6 +82,8 @@ class AudiobooksController(MediaControllerBase[Audiobook]): :param provider: Filter by provider instance ID (single string or list). :param extra_query: Additional SQL query string. :param extra_query_params: Additional query parameters. + :param library_items_only: If True, only return items that are + marked as 'in_library' on any provider mapping. """ extra_query_params = extra_query_params or {} extra_query_parts: list[str] = [extra_query] if extra_query else [] @@ -92,6 +96,7 @@ class AudiobooksController(MediaControllerBase[Audiobook]): provider_filter=self._ensure_provider_filter(provider), extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) if search and len(result) < 25 and not offset: # append author items to result @@ -107,6 +112,7 @@ class AudiobooksController(MediaControllerBase[Audiobook]): provider_filter=self._ensure_provider_filter(provider), extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) return result diff --git a/music_assistant/controllers/media/base.py b/music_assistant/controllers/media/base.py index c5abb11b..34c03560 100644 --- a/music_assistant/controllers/media/base.py +++ b/music_assistant/controllers/media/base.py @@ -129,9 +129,6 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): # update existing item await self._update_library_item(library_id, item, overwrite=overwrite_existing) else: - for provider_mapping in item.provider_mappings: - if item.item_id == provider_mapping.item_id: - provider_mapping.in_library = True # actually add a new item in the library db self.mass.music.match_provider_instances(item) async with self._db_add_lock: @@ -246,8 +243,23 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, + **kwargs: Any, ) -> list[ItemCls]: - """Get in-database items.""" + """ + Get in-database albums. + + :param favorite: Filter by favorite status. + :param search: Filter by search query. + :param limit: Maximum number of items to return. + :param offset: Number of items to skip. + :param order_by: Order by field (e.g. 'sort_name', 'timestamp_added'). + :param provider: Filter by provider instance ID (single string or list). + :param extra_query: Additional SQL query string. + :param extra_query_params: Additional query parameters. + :param library_items_only: If True, only return items that are + marked as 'in_library' on any provider mapping. + """ return await self._get_library_items_by_query( favorite=favorite, search=search, @@ -257,6 +269,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): provider_filter=self._ensure_provider_filter(provider), extra_query_parts=[extra_query] if extra_query else None, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) async def iter_library_items( @@ -267,6 +280,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, ) -> AsyncGenerator[ItemCls, None]: """Iterate all in-database items.""" limit: int = 500 @@ -285,6 +299,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): provider_filter=provider_filter, extra_query_parts=[extra_query] if extra_query else None, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) for item in next_items: yield item @@ -361,7 +376,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): db_id = int(item_id) # ensure integer extra_query = f"WHERE {self.db_table}.item_id = {item_id}" for db_item in await self._get_library_items_by_query( - extra_query_parts=[extra_query], + extra_query_parts=[extra_query], in_library_only=False ): return db_item msg = f"{self.media_type.value} not found in library: {db_id}" @@ -471,6 +486,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): offset=offset, extra_query_parts=[query], extra_query_params=query_params, + in_library_only=False, ) @final @@ -762,6 +778,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): extra_query_parts: list[str] | None = None, extra_query_params: dict[str, Any] | None = None, extra_join_parts: list[str] | None = None, + in_library_only: bool = False, ) -> list[ItemCls]: """Fetch MediaItem records from database by building the query.""" query_params = extra_query_params or {} @@ -777,6 +794,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): favorite=favorite, search=search, provider_filter=provider_filter, + in_library_only=in_library_only, limit=limit, ) else: @@ -788,6 +806,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): favorite=favorite, search=search, provider_filter=provider_filter, + in_library_only=in_library_only, ) # build and execute final query sql_query = self._build_final_query(query_parts, join_parts, order_by) @@ -822,6 +841,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): favorite: bool | None, search: str | None, provider_filter: list[str] | None, + in_library_only: bool, limit: int, ) -> None: """Build a fast random subquery with all filters applied.""" @@ -836,6 +856,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): favorite=favorite, search=search, provider_filter=provider_filter, + in_library_only=in_library_only, ) # Build the subquery @@ -864,6 +885,7 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): favorite: bool | None, search: str | None, provider_filter: list[str] | None, + in_library_only: bool, ) -> None: """Apply search, favorite, and provider filters.""" # handle search @@ -881,9 +903,15 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): join_parts.append( f"JOIN provider_mappings ON provider_mappings.item_id = {self.db_table}.item_id " f"AND provider_mappings.media_type = '{self.media_type.value}' " - f"AND provider_mappings.in_library = 1 " + f"AND provider_mappings.in_library = {in_library_only} " f"AND ({' OR '.join(provider_conditions)})" ) + elif in_library_only: + join_parts.append( + f"JOIN provider_mappings ON provider_mappings.item_id = {self.db_table}.item_id " + f"AND provider_mappings.media_type = '{self.media_type.value}' " + f"AND provider_mappings.in_library = {in_library_only} " + ) @final def _build_final_query( diff --git a/music_assistant/controllers/media/podcasts.py b/music_assistant/controllers/media/podcasts.py index 7530b958..c19bd506 100644 --- a/music_assistant/controllers/media/podcasts.py +++ b/music_assistant/controllers/media/podcasts.py @@ -52,6 +52,8 @@ class PodcastsController(MediaControllerBase[Podcast]): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, + **kwargs: Any, ) -> list[Podcast]: """Get in-database podcasts. @@ -63,6 +65,8 @@ class PodcastsController(MediaControllerBase[Podcast]): :param provider: Filter by provider instance ID (single string or list). :param extra_query: Additional SQL query string. :param extra_query_params: Additional query parameters. + :param library_items_only: If True, only return items that are + marked as 'in_library' on any provider mapping. """ extra_query_params = extra_query_params or {} extra_query_parts: list[str] = [extra_query] if extra_query else [] @@ -75,6 +79,7 @@ class PodcastsController(MediaControllerBase[Podcast]): provider_filter=self._ensure_provider_filter(provider), extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) if search and len(result) < 25 and not offset: # append publisher items to result @@ -90,6 +95,7 @@ class PodcastsController(MediaControllerBase[Podcast]): provider_filter=self._ensure_provider_filter(provider), extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, + in_library_only=library_items_only, ) return result diff --git a/music_assistant/controllers/media/tracks.py b/music_assistant/controllers/media/tracks.py index 7a9e555f..d34f0f0b 100644 --- a/music_assistant/controllers/media/tracks.py +++ b/music_assistant/controllers/media/tracks.py @@ -167,6 +167,8 @@ class TracksController(MediaControllerBase[Track]): provider: str | list[str] | None = None, extra_query: str | None = None, extra_query_params: dict[str, Any] | None = None, + library_items_only: bool = True, + **kwargs: Any, ) -> list[Track]: """Get in-database tracks. @@ -178,6 +180,8 @@ class TracksController(MediaControllerBase[Track]): :param provider: Filter by provider instance ID (single string or list). :param extra_query: Additional SQL query string. :param extra_query_params: Additional query parameters. + :param library_items_only: If True, only return items that are + marked as 'in_library' on any provider mapping. """ extra_query_params = extra_query_params or {} extra_query_parts: list[str] = [extra_query] if extra_query else [] @@ -207,6 +211,7 @@ class TracksController(MediaControllerBase[Track]): extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, extra_join_parts=extra_join_parts, + in_library_only=library_items_only, ) if search and len(result) < 25 and not offset: # append artist items to result @@ -227,6 +232,7 @@ class TracksController(MediaControllerBase[Track]): extra_query_parts=extra_query_parts, extra_query_params=extra_query_params, extra_join_parts=extra_join_parts, + in_library_only=library_items_only, ): # prevent duplicates (when artist is also in the title) if _track.uri not in existing_uris: @@ -414,7 +420,9 @@ class TracksController(MediaControllerBase[Track]): f"WHERE {DB_TABLE_ALBUM_TRACKS}.track_id = {item_id}" ) query = f"{DB_TABLE_ALBUMS}.item_id in ({subquery})" - return await self.mass.music.albums._get_library_items_by_query(extra_query_parts=[query]) + return await self.mass.music.albums._get_library_items_by_query( + extra_query_parts=[query], in_library_only=True + ) async def match_provider( self, diff --git a/music_assistant/controllers/music.py b/music_assistant/controllers/music.py index 5b853401..855ffa91 100644 --- a/music_assistant/controllers/music.py +++ b/music_assistant/controllers/music.py @@ -97,7 +97,7 @@ CONF_RESET_DB = "reset_db" DEFAULT_SYNC_INTERVAL = 12 * 60 # default sync interval in minutes CONF_SYNC_INTERVAL = "sync_interval" CONF_DELETED_PROVIDERS = "deleted_providers" -DB_SCHEMA_VERSION: Final[int] = 25 +DB_SCHEMA_VERSION: Final[int] = 26 CACHE_CATEGORY_LAST_SYNC: Final[int] = 9 CACHE_CATEGORY_SEARCH_RESULTS: Final[int] = 10 @@ -2170,14 +2170,18 @@ class MusicController(CoreController): if "duplicate column" not in str(err): raise - if prev_version <= 25: - # set in_library=True for local(file)-based providers - # these providers always represent the user's actual library + if prev_version <= 26: + # force in_library=True for provider mappings from non-streaming providers + # streaming providers will be automatically added to library when synced await self._database.execute( f"UPDATE {DB_TABLE_PROVIDER_MAPPINGS} SET in_library = 1 " - "WHERE provider_domain IN " - "('filesystem_local', 'filesystem_smb', 'plex', " - "'jellyfin', 'opensubsonic', 'builtin');" + "WHERE provider_domain NOT IN " + "('spotify', 'deezer', 'tidal', 'qobuz', 'apple_music', 'ytmusic');" + ) + await self._database.execute( + f"UPDATE {DB_TABLE_PROVIDER_MAPPINGS} SET in_library = 1 " + "WHERE media_type IN " + "('radio', 'playlist');" ) # save changes @@ -2589,7 +2593,9 @@ class MusicController(CoreController): self.audiobooks, self.podcasts, ): - async for db_item in ctrl.iter_library_items(provider=list(multi_instance_providers)): + async for db_item in ctrl.iter_library_items( + provider=list(multi_instance_providers), library_items_only=False + ): if self.match_provider_instances(db_item): await ctrl.update_item_in_library(db_item.item_id, db_item) # prevent overwhelming the event loop diff --git a/music_assistant/models/music_provider.py b/music_assistant/models/music_provider.py index 57b51a82..27d52836 100644 --- a/music_assistant/models/music_provider.py +++ b/music_assistant/models/music_provider.py @@ -712,44 +712,23 @@ class MusicProvider(Provider): try: library_item = await controller.get_library_item(db_id) except MediaNotFoundError: - # edge case: the item is already removed + # edge case: the item is (already) removed from MA library as well continue # check if we have other provider-mappings (marked as in-library) - remaining_providers = { + remaining_providers_in_library = { x.provider_instance for x in library_item.provider_mappings if x.provider_instance != self.instance_id and x.in_library } - if remaining_providers: - # if we have other remaining providers, update the provider mappings - for prov_map in library_item.provider_mappings: - if prov_map.provider_instance == self.instance_id: - prov_map.in_library = False - await controller.set_provider_mappings( - db_id, library_item.provider_mappings - ) - else: - # this item is removed from the provider's library - # and we have no other providers attached to it - # it is safe to remove it from the MA library too - try: - await controller.remove_item_from_library( - db_id, recursive=media_type == MediaType.ALBUM - ) - except MusicAssistantError as err: - # this is probably because the item still has dependents - self.logger.warning( - "Error removing item %s from library: %s", db_id, str(err) - ) - # just un-favorite the item if we can't remove it - if library_item.favorite: - await controller.set_favorite(db_id, False) - for prov_map in library_item.provider_mappings: - if prov_map.provider_instance == self.instance_id: - prov_map.in_library = False - await controller.set_provider_mappings( - db_id, library_item.provider_mappings - ) + if not remaining_providers_in_library and library_item.favorite: + # unmark as favorite since no providers have it in library anymore + await controller.set_favorite(db_id, False) + # unmark this provider mapping as in_library = False + # we keep it in the library database so we can keep the metadata for future use + for prov_map in library_item.provider_mappings: + if prov_map.provider_instance == self.instance_id: + prov_map.in_library = False + await controller.set_provider_mappings(db_id, library_item.provider_mappings) await asyncio.sleep(0) # yield to eventloop # store current list of id's in cache so we can track changes await self.mass.cache.set( @@ -771,14 +750,14 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.artists.add_item_to_library(prov_item) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.artists.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.artists.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.artists.set_favorite(library_item.item_id, True) cur_db_ids.add(int(library_item.item_id)) await asyncio.sleep(0) # yield to eventloop except MusicAssistantError as err: @@ -806,14 +785,14 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.albums.add_item_to_library(prov_item) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.albums.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.albums.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.albums.set_favorite(library_item.item_id, True) cur_db_ids.add(int(library_item.item_id)) await asyncio.sleep(0) # yield to eventloop # optionally add album tracks to library @@ -866,14 +845,14 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.audiobooks.add_item_to_library(prov_item) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.audiobooks.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.audiobooks.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.audiobooks.set_favorite(library_item.item_id, True) # check if resume_position_ms or fully_played changed if ( @@ -915,14 +894,15 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.playlists.add_item_to_library(prov_item) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.playlists.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.playlists.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.playlists.set_favorite(library_item.item_id, True) + cur_db_ids.add(int(library_item.item_id)) await asyncio.sleep(0) # yield to eventloop # optionally sync playlist tracks @@ -986,19 +966,15 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.tracks.add_item_to_library(prov_item) - elif library_item.available != prov_item.available: - # existing library item but availability changed - library_item = await self.mass.music.tracks.update_item_in_library( - library_item.item_id, prov_item - ) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.tracks.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.tracks.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.tracks.set_favorite(library_item.item_id, True) + cur_db_ids.add(int(library_item.item_id)) await asyncio.sleep(0) # yield to eventloop except MusicAssistantError as err: @@ -1021,19 +997,14 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.podcasts.add_item_to_library(prov_item) - elif library_item.available != prov_item.available: - # existing library item but availability changed - library_item = await self.mass.music.podcasts.update_item_in_library( - library_item.item_id, prov_item - ) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.podcasts.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.podcasts.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.podcasts.set_favorite(library_item.item_id, True) cur_db_ids.add(int(library_item.item_id)) await asyncio.sleep(0) # yield to eventloop @@ -1063,14 +1034,14 @@ class MusicProvider(Provider): if not library_item: # add item to the library library_item = await self.mass.music.radio.add_item_to_library(prov_item) - elif not library_item.favorite and prov_item.favorite: - # existing library item not favorite but should be - await self.mass.music.radio.set_favorite(library_item.item_id, True) elif not self._check_provider_mappings(library_item, prov_item, True): # existing library item but provider mapping doesn't match library_item = await self.mass.music.radio.update_item_in_library( library_item.item_id, prov_item ) + if not library_item.favorite and prov_item.favorite: + # existing library item not favorite but should be + await self.mass.music.radio.set_favorite(library_item.item_id, True) cur_db_ids.add(int(library_item.item_id)) await asyncio.sleep(0) # yield to eventloop diff --git a/music_assistant/providers/filesystem_local/__init__.py b/music_assistant/providers/filesystem_local/__init__.py index 4d236e65..3538a83f 100644 --- a/music_assistant/providers/filesystem_local/__init__.py +++ b/music_assistant/providers/filesystem_local/__init__.py @@ -1044,7 +1044,9 @@ class LocalFileSystemProvider(MusicProvider): artist_path = foldermatch else: # check if we have an existing item to retrieve the artist path - async for item in self.mass.music.artists.iter_library_items(search=name): + async for item in self.mass.music.artists.iter_library_items( + search=name, provider=self.instance_id + ): if not compare_strings(name, item.name): continue for prov_mapping in item.provider_mappings: