Handle more cases of instance steering (#2762)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 7 Dec 2025 03:03:46 +0000 (04:03 +0100)
committerGitHub <noreply@github.com>
Sun, 7 Dec 2025 03:03:46 +0000 (04:03 +0100)
music_assistant/controllers/media/base.py
music_assistant/controllers/media/playlists.py
music_assistant/controllers/media/podcasts.py
music_assistant/controllers/music.py

index c912fe366b114279a82acb24c98a61e5ae1a0487..e19902e12827ba34696895018cb7b91c31ddeca7 100644 (file)
@@ -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)
index f4fa8e26ed25b538a1a4660187440c8430dfdd9a..c6141c85334479c2519701bb91d8a43e1ff4e724 100644 (file)
@@ -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
index d1326796734d25af8400042345a5cd4a56787b49..7e95202746efa75d650bcc9c4e19ada50946c83f 100644 (file)
@@ -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(
index b691f49479452287fac2128e0e25e3a6488fd3f6..7a47c79185ca7d2bc4d7da376ec5bf73ff9104bc 100644 (file)
@@ -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: