From 5d57feffc5a0c20586441eafe811840ac683f128 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Sun, 7 Dec 2025 04:03:46 +0100 Subject: [PATCH] Handle more cases of instance steering (#2762) --- music_assistant/controllers/media/base.py | 13 +++++++++++++ music_assistant/controllers/media/playlists.py | 5 +---- music_assistant/controllers/media/podcasts.py | 5 +---- music_assistant/controllers/music.py | 18 ++++++++++++------ 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/music_assistant/controllers/media/base.py b/music_assistant/controllers/media/base.py index c912fe36..e19902e1 100644 --- a/music_assistant/controllers/media/base.py +++ b/music_assistant/controllers/media/base.py @@ -933,3 +933,16 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta): # No user filter - use the provided filter as is final_provider_filter = [provider] if isinstance(provider, str) else provider return final_provider_filter + + def _select_provider_id(self, library_item: ItemCls) -> tuple[str, str]: + """Select the correct provider id to use for fetching the item.""" + user = get_current_user() + user_provider_filter = user.provider_filter if user and user.provider_filter else None + # prefer user provider filter if available + for mapping in library_item.provider_mappings: + if user_provider_filter and mapping.provider_instance not in user_provider_filter: + continue + return (mapping.provider_instance, mapping.item_id) + # fallback to first mapping + mapping = next(iter(library_item.provider_mappings)) + return (mapping.provider_instance, mapping.item_id) diff --git a/music_assistant/controllers/media/playlists.py b/music_assistant/controllers/media/playlists.py index f4fa8e26..c6141c85 100644 --- a/music_assistant/controllers/media/playlists.py +++ b/music_assistant/controllers/media/playlists.py @@ -84,10 +84,7 @@ class PlaylistController(MediaControllerBase[Playlist]): """Return playlist tracks for the given provider playlist id.""" if provider_instance_id_or_domain == "library": library_item = await self.get_library_item(item_id) - # a playlist can only have one provider so simply pick the first one - prov_map = next(x for x in library_item.provider_mappings) - item_id = prov_map.item_id - provider_instance_id_or_domain = prov_map.provider_instance + provider_instance_id_or_domain, item_id = self._select_provider_id(library_item) # playlist tracks are not stored in the db, # we always fetched them (cached) from the provider page = 0 diff --git a/music_assistant/controllers/media/podcasts.py b/music_assistant/controllers/media/podcasts.py index d1326796..7e952027 100644 --- a/music_assistant/controllers/media/podcasts.py +++ b/music_assistant/controllers/media/podcasts.py @@ -104,10 +104,7 @@ class PodcastsController(MediaControllerBase[Podcast]): library_podcast = await self.get_library_item(item_id) if not library_podcast: raise MediaNotFoundError(f"Podcast {item_id} not found in library") - for provider_mapping in library_podcast.provider_mappings: - item_id = provider_mapping.item_id - provider_instance_id_or_domain = provider_mapping.provider_instance - break + provider_instance_id_or_domain, item_id = self._select_provider_id(library_podcast) # podcast episodes are not stored in the db/library # so we always need to fetch them from the provider async for episode in self._get_provider_podcast_episodes( diff --git a/music_assistant/controllers/music.py b/music_assistant/controllers/music.py index b691f494..7a47c791 100644 --- a/music_assistant/controllers/music.py +++ b/music_assistant/controllers/music.py @@ -1285,15 +1285,21 @@ class MusicController(CoreController): """ Return all unique MusicProvider (instance or domain) ids. - This will return instance_ids for non-streaming providers - and domain names for streaming providers to avoid duplicates. + This will return a set of provider instance ids but will only return + a single instance_id per streaming provider domain. """ + processed_domains: set[str] = set() + # Get user provider filter if set + user = get_current_user() + user_provider_filter = user.provider_filter if user and user.provider_filter else None result: set[str] = set() for provider in self.providers: - if provider.is_streaming_provider: - result.add(provider.domain) - else: - result.add(provider.instance_id) + if provider.is_streaming_provider and provider.domain in processed_domains: + continue + if user_provider_filter and provider.instance_id not in user_provider_filter: + continue + result.add(provider.instance_id) + processed_domains.add(provider.domain) return result async def cleanup_provider(self, provider_instance: str) -> None: -- 2.34.1