Phase out lookup key (#2751)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 5 Dec 2025 15:10:20 +0000 (16:10 +0100)
committerGitHub <noreply@github.com>
Fri, 5 Dec 2025 15:10:20 +0000 (16:10 +0100)
77 files changed:
music_assistant/controllers/config.py
music_assistant/controllers/media/audiobooks.py
music_assistant/controllers/media/base.py
music_assistant/controllers/media/playlists.py
music_assistant/controllers/media/podcasts.py
music_assistant/controllers/metadata.py
music_assistant/controllers/music.py
music_assistant/controllers/players/player_controller.py
music_assistant/controllers/players/sync_groups.py
music_assistant/helpers/audio.py
music_assistant/helpers/podcast_parsers.py
music_assistant/mass.py
music_assistant/models/music_provider.py
music_assistant/models/player.py
music_assistant/models/player_provider.py
music_assistant/models/provider.py
music_assistant/providers/_demo_plugin_provider/__init__.py
music_assistant/providers/airplay/player.py
music_assistant/providers/airplay/provider.py
music_assistant/providers/apple_music/__init__.py
music_assistant/providers/ard_audiothek/__init__.py
music_assistant/providers/audiobookshelf/__init__.py
music_assistant/providers/audiobookshelf/parsers.py
music_assistant/providers/bluesound/player.py
music_assistant/providers/builtin/__init__.py
music_assistant/providers/deezer/__init__.py
music_assistant/providers/gpodder/__init__.py
music_assistant/providers/ibroadcast/__init__.py
music_assistant/providers/itunes_podcasts/__init__.py
music_assistant/providers/jellyfin/__init__.py
music_assistant/providers/musiccast/player.py
music_assistant/providers/musiccast/provider.py
music_assistant/providers/nicovideo/converters/album.py
music_assistant/providers/nicovideo/converters/artist.py
music_assistant/providers/nicovideo/converters/playlist.py
music_assistant/providers/nicovideo/converters/track.py
music_assistant/providers/nicovideo/provider_mixins/explorer.py
music_assistant/providers/nugs/__init__.py
music_assistant/providers/plex/__init__.py
music_assistant/providers/podcast_index/helpers.py
music_assistant/providers/podcast_index/provider.py
music_assistant/providers/podcastfeed/__init__.py
music_assistant/providers/qobuz/__init__.py
music_assistant/providers/radiobrowser/__init__.py
music_assistant/providers/radioparadise/parsers.py
music_assistant/providers/radioparadise/provider.py
music_assistant/providers/sendspin/player.py
music_assistant/providers/siriusxm/__init__.py
music_assistant/providers/snapcast/player.py
music_assistant/providers/sonos/player.py
music_assistant/providers/sonos_s1/player.py
music_assistant/providers/sonos_s1/provider.py
music_assistant/providers/soundcloud/__init__.py
music_assistant/providers/spotify/parsers.py
music_assistant/providers/spotify/provider.py
music_assistant/providers/squeezelite/player.py
music_assistant/providers/test/__init__.py
music_assistant/providers/theaudiodb/__init__.py
music_assistant/providers/tidal/parsers.py
music_assistant/providers/tidal/provider.py
music_assistant/providers/tidal/recommendations.py
music_assistant/providers/tidal/streaming.py
music_assistant/providers/tunein/__init__.py
music_assistant/providers/universal_group/player.py
music_assistant/providers/universal_group/provider.py
music_assistant/providers/ytmusic/__init__.py
tests/providers/nicovideo/__snapshots__/test_converters.ambr
tests/providers/nicovideo/helpers.py
tests/providers/tidal/__snapshots__/test_parsers.ambr
tests/providers/tidal/test_library.py
tests/providers/tidal/test_media.py
tests/providers/tidal/test_media_extended.py
tests/providers/tidal/test_page_parser.py
tests/providers/tidal/test_page_parser_extended.py
tests/providers/tidal/test_parsers.py
tests/providers/tidal/test_provider.py
tests/providers/tidal/test_streaming.py

index 34d8039dd228bae42cce246f80483e7823ba374f..0dd0ac7fa95f83eb68b8a100625ca6e26b2e6020 100644 (file)
@@ -496,7 +496,7 @@ class ConfigController:
         if raw_conf := self.get(f"{CONF_PLAYERS}/{player_id}"):
             if player := self.mass.players.get(player_id, False):
                 raw_conf["default_name"] = player.display_name
-                raw_conf["provider"] = player.provider.lookup_key
+                raw_conf["provider"] = player.provider.instance_id
                 # pass action and values to get_config_entries
                 if values is None:
                     values = raw_conf.get("values", {})
index 6ef0bb09f94f2a4c183a3cf554e6f8f198e9ff8a..20bf2dfd8f4fd6b02ec3a31cf23551936df22887 100644 (file)
@@ -277,14 +277,12 @@ class AudiobooksController(MediaControllerBase[Audiobook]):
         # cleanup provider specific entries for this item
         # we always prefer the library playlog entry
         for prov_mapping in media_item.provider_mappings:
-            if not (provider := self.mass.get_provider(prov_mapping.provider_instance)):
-                continue
             await self.mass.music.database.delete(
                 DB_TABLE_PLAYLOG,
                 {
                     "media_type": self.media_type.value,
                     "item_id": prov_mapping.item_id,
-                    "provider": provider.lookup_key,
+                    "provider": prov_mapping.provider_instance,
                 },
             )
         if media_item.fully_played is None and media_item.resume_position_ms is None:
index 4adaeee30ea0073ea82d709cc0a3b5eb98db1b8c..9561e6069bf56939789f381327c6f3f46cd0461d 100644 (file)
@@ -162,8 +162,8 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta):
         # search by (exact) name match
         query = f"{self.db_table}.name = :name OR {self.db_table}.sort_name = :sort_name"
         query_params = {"name": item.name, "sort_name": item.sort_name}
-        async for db_item in self.iter_library_items(
-            extra_query=query, extra_query_params=query_params
+        for db_item in await self._get_library_items_by_query(
+            extra_query_parts=[query], extra_query_params=query_params
         ):
             if compare_media_item(db_item, item, True):
                 return int(db_item.item_id)
@@ -263,15 +263,19 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta):
         """Iterate all in-database items."""
         limit: int = 500
         offset: int = 0
+        if provider is not None:
+            provider_filter = provider if isinstance(provider, list) else [provider]
+        else:
+            provider_filter = None
         while True:
-            next_items = await self.library_items(
+            next_items = await self._get_library_items_by_query(
                 favorite=favorite,
                 search=search,
                 limit=limit,
                 offset=offset,
                 order_by=order_by,
-                provider=provider,
-                extra_query=extra_query,
+                provider_filter=provider_filter,
+                extra_query_parts=[extra_query] if extra_query else None,
                 extra_query_params=extra_query_params,
             )
             for item in next_items:
@@ -348,7 +352,9 @@ class MediaControllerBase[ItemCls: "MediaItemType"](metaclass=ABCMeta):
         """Get single library item by id."""
         db_id = int(item_id)  # ensure integer
         extra_query = f"WHERE {self.db_table}.item_id = {item_id}"
-        async for db_item in self.iter_library_items(extra_query=extra_query):
+        for db_item in await self._get_library_items_by_query(
+            extra_query_parts=[extra_query],
+        ):
             return db_item
         msg = f"{self.media_type.value} not found in library: {db_id}"
         raise MediaNotFoundError(msg)
index 592a965ee11f38165fbcca7207b89ebcdde95612..f4fa8e26ed25b538a1a4660187440c8430dfdd9a 100644 (file)
@@ -249,7 +249,7 @@ class PlaylistController(MediaControllerBase[Playlist]):
                         provider_instance_id_or_domain,
                     )
                     continue
-                if item_prov.lookup_key == playlist_prov.lookup_key:
+                if item_prov.instance_id == playlist_prov.instance_id:
                     if item_id not in ids_to_add:
                         ids_to_add.append(item_id)
                     continue
@@ -285,7 +285,7 @@ class PlaylistController(MediaControllerBase[Playlist]):
                     continue
                 track_version_uri = create_uri(
                     MediaType.TRACK,
-                    item_prov.lookup_key,
+                    item_prov.instance_id,
                     track_version.item_id,
                 )
                 if track_version_uri in cur_playlist_track_uris:
@@ -305,7 +305,7 @@ class PlaylistController(MediaControllerBase[Playlist]):
                         playlist.name,
                     )
                     break
-                if item_prov.lookup_key == playlist_prov.lookup_key:
+                if item_prov.instance_id == playlist_prov.instance_id:
                     if track_version.item_id not in ids_to_add:
                         ids_to_add.append(track_version.item_id)
                     self.logger.info(
index 59183eb8d0555331d4a062935339ab64b388bf75..d1326796734d25af8400042345a5cd4a56787b49 100644 (file)
@@ -226,7 +226,7 @@ class PodcastsController(MediaControllerBase[Podcast]):
                 DB_TABLE_PLAYLOG,
                 {
                     "item_id": episode.item_id,
-                    "provider": prov.lookup_key,
+                    "provider": prov.instance_id,
                     "media_type": MediaType.PODCAST_EPISODE,
                 },
             )
index d7ed45a64dd21419fe5ee0f733660ed68c6da98d..26d85518edb367fb9e14e321071f0d93b16685d6 100644 (file)
@@ -55,6 +55,7 @@ from music_assistant.helpers.compare import compare_strings
 from music_assistant.helpers.images import create_collage, get_image_thumb
 from music_assistant.helpers.throttle_retry import Throttler
 from music_assistant.models.core_controller import CoreController
+from music_assistant.models.music_provider import MusicProvider
 
 if TYPE_CHECKING:
     from music_assistant_models.config_entries import CoreConfig
@@ -524,12 +525,16 @@ class MetaDataController(CoreController):
         for prov_mapping in sorted(
             artist.provider_mappings, key=lambda x: x.priority, reverse=True
         ):
-            if (prov := self.mass.get_provider(prov_mapping.provider_instance)) is None:
+            prov = self.mass.get_provider(
+                prov_mapping.provider_instance, provider_type=MusicProvider
+            )
+            if prov is None:
                 continue
-            if prov.lookup_key in unique_keys:
+            # prefer domain for streaming providers as the catalog is the same across instances
+            prov_key = prov.domain if prov.is_streaming_provider else prov.instance_id
+            if prov_key in unique_keys:
                 continue
-            if prov.lookup_key not in local_provs:
-                unique_keys.add(prov.lookup_key)
+            unique_keys.add(prov_key)
             with suppress(MediaNotFoundError):
                 prov_item = await self.mass.music.artists.get_provider_item(
                     prov_mapping.item_id, prov_mapping.provider_instance
@@ -577,16 +582,17 @@ class MetaDataController(CoreController):
         # note that we sort the providers by priority so that we always
         # prefer local providers over online providers
         unique_keys: set[str] = set()
-        local_provs = get_global_cache_value("non_streaming_providers")
-        if TYPE_CHECKING:
-            local_provs = cast("set[str]", local_provs)
         for prov_mapping in sorted(album.provider_mappings, key=lambda x: x.priority, reverse=True):
-            if (prov := self.mass.get_provider(prov_mapping.provider_instance)) is None:
+            prov = self.mass.get_provider(
+                prov_mapping.provider_instance, provider_type=MusicProvider
+            )
+            if prov is None:
                 continue
-            if prov.lookup_key in unique_keys:
+            # prefer domain for streaming providers as the catalog is the same across instances
+            prov_key = prov.domain if prov.is_streaming_provider else prov.instance_id
+            if prov_key in unique_keys:
                 continue
-            if prov.lookup_key not in local_provs:
-                unique_keys.add(prov.lookup_key)
+            unique_keys.add(prov_key)
             with suppress(MediaNotFoundError):
                 prov_item = await self.mass.music.albums.get_provider_item(
                     prov_mapping.item_id, prov_mapping.provider_instance
@@ -632,15 +638,17 @@ class MetaDataController(CoreController):
         # note that we sort the providers by priority so that we always
         # prefer local providers over online providers
         unique_keys: set[str] = set()
-        local_provs = get_global_cache_value("non_streaming_providers")
-        if TYPE_CHECKING:
-            local_provs = cast("set[str]", local_provs)
         for prov_mapping in sorted(track.provider_mappings, key=lambda x: x.priority, reverse=True):
-            if (prov := self.mass.get_provider(prov_mapping.provider_instance)) is None:
+            prov = self.mass.get_provider(
+                prov_mapping.provider_instance, provider_type=MusicProvider
+            )
+            if prov is None:
                 continue
-            if prov.lookup_key in unique_keys:
+            # prefer domain for streaming providers as the catalog is the same across instances
+            prov_key = prov.domain if prov.is_streaming_provider else prov.instance_id
+            if prov_key in unique_keys:
                 continue
-            unique_keys.add(prov.lookup_key)
+            unique_keys.add(prov_key)
             with suppress(MediaNotFoundError):
                 prov_item = await self.mass.music.tracks.get_provider_item(
                     prov_mapping.item_id, prov_mapping.provider_instance
@@ -762,18 +770,19 @@ class MetaDataController(CoreController):
         # note that we sort the providers by priority so that we always
         # prefer local providers over online providers
         unique_keys: set[str] = set()
-        local_provs = get_global_cache_value("non_streaming_providers")
-        if TYPE_CHECKING:
-            local_provs = cast("set[str]", local_provs)
         for prov_mapping in sorted(
             audiobook.provider_mappings, key=lambda x: x.priority, reverse=True
         ):
-            if (prov := self.mass.get_provider(prov_mapping.provider_instance)) is None:
+            prov = self.mass.get_provider(
+                prov_mapping.provider_instance, provider_type=MusicProvider
+            )
+            if prov is None:
                 continue
-            if prov.lookup_key in unique_keys:
+            # prefer domain for streaming providers as the catalog is the same across instances
+            prov_key = prov.domain if prov.is_streaming_provider else prov.instance_id
+            if prov_key in unique_keys:
                 continue
-            if prov.lookup_key not in local_provs:
-                unique_keys.add(prov.lookup_key)
+            unique_keys.add(prov_key)
             with suppress(MediaNotFoundError):
                 prov_item = await self.mass.music.audiobooks.get_provider_item(
                     prov_mapping.item_id, prov_mapping.provider_instance
@@ -810,18 +819,19 @@ class MetaDataController(CoreController):
         # note that we sort the providers by priority so that we always
         # prefer local providers over online providers
         unique_keys: set[str] = set()
-        local_provs = get_global_cache_value("non_streaming_providers")
-        if TYPE_CHECKING:
-            local_provs = cast("set[str]", local_provs)
         for prov_mapping in sorted(
             podcast.provider_mappings, key=lambda x: x.priority, reverse=True
         ):
-            if (prov := self.mass.get_provider(prov_mapping.provider_instance)) is None:
+            prov = self.mass.get_provider(
+                prov_mapping.provider_instance, provider_type=MusicProvider
+            )
+            if prov is None:
                 continue
-            if prov.lookup_key in unique_keys:
+            # prefer domain for streaming providers as the catalog is the same across instances
+            prov_key = prov.domain if prov.is_streaming_provider else prov.instance_id
+            if prov_key in unique_keys:
                 continue
-            if prov.lookup_key not in local_provs:
-                unique_keys.add(prov.lookup_key)
+            unique_keys.add(prov_key)
             with suppress(MediaNotFoundError):
                 prov_item = await self.mass.music.podcasts.get_provider_item(
                     prov_mapping.item_id, prov_mapping.provider_instance
index 98f036bbd35015c46bcc8226559e7b4db3fa44f3..b691f49479452287fac2128e0e25e3a6488fd3f6 100644 (file)
@@ -474,7 +474,7 @@ class MusicController(CoreController):
         if media_types is None:
             media_types = MediaType.ALL
         media_types_str = "(" + ",".join(f'"{x}"' for x in media_types) + ")"
-        available_providers = ("library", *get_global_cache_value("unique_providers", []))
+        available_providers = ("library", *self.get_unique_providers())
         available_providers_str = "(" + ",".join(f'"{x}"' for x in available_providers) + ")"
         query = (
             f"SELECT * FROM {DB_TABLE_PLAYLOG} "
@@ -521,7 +521,7 @@ class MusicController(CoreController):
         self, limit: int = 10, all_users: bool = False
     ) -> list[ItemMapping]:
         """Return a list of the Audiobooks and PodcastEpisodes that are in progress."""
-        available_providers = ("library", *get_global_cache_value("unique_providers", []))
+        available_providers = ("library", *self.get_unique_providers())
         available_providers_str = "(" + ",".join(f'"{x}"' for x in available_providers) + ")"
         query = (
             f"SELECT * FROM {DB_TABLE_PLAYLOG} "
@@ -885,10 +885,12 @@ class MusicController(CoreController):
         if loudness in (None, inf, -inf):
             # skip invalid values
             return
+        # prefer domain for streaming providers as the catalog is the same across instances
+        prov_key = provider.domain if provider.is_streaming_provider else provider.instance_id
         values = {
             "item_id": item_id,
             "media_type": media_type.value,
-            "provider": provider.lookup_key,
+            "provider": prov_key,
             "loudness": loudness,
         }
         if album_loudness not in (None, inf, -inf):
@@ -914,10 +916,12 @@ class MusicController(CoreController):
             return
         beats_json = await asyncio.to_thread(lambda: json_dumps(analysis.beats.tolist()))
         downbeats_json = await asyncio.to_thread(lambda: json_dumps(analysis.downbeats.tolist()))
+        # prefer domain for streaming providers as the catalog is the same across instances
+        prov_key = provider.domain if provider.is_streaming_provider else provider.instance_id
         values = {
             "fragment": analysis.fragment.value,
             "item_id": item_id,
-            "provider": provider.lookup_key,
+            "provider": prov_key,
             "bpm": analysis.bpm,
             "beats": beats_json,
             "downbeats": downbeats_json,
@@ -935,11 +939,13 @@ class MusicController(CoreController):
         """Get Smart Fades BPM analysis for a track from db."""
         if not (provider := self.mass.get_provider(provider_instance_id_or_domain)):
             return None
+        # prefer domain for streaming providers as the catalog is the same across instances
+        prov_key = provider.domain if provider.is_streaming_provider else provider.instance_id
         db_row = await self.database.get_row(
             DB_TABLE_SMART_FADES_ANALYSIS,
             {
                 "item_id": item_id,
-                "provider": provider.lookup_key,
+                "provider": prov_key,
                 "fragment": fragment.value,
             },
         )
@@ -965,12 +971,14 @@ class MusicController(CoreController):
         """Get (EBU-R128) Integrated Loudness Measurement for a mediaitem in db."""
         if not (provider := self.mass.get_provider(provider_instance_id_or_domain)):
             return None
+        # prefer domain for streaming providers as the catalog is the same across instances
+        prov_key = provider.domain if provider.is_streaming_provider else provider.instance_id
         db_row = await self.database.get_row(
             DB_TABLE_LOUDNESS_MEASUREMENTS,
             {
                 "item_id": item_id,
                 "media_type": media_type.value,
-                "provider": provider.lookup_key,
+                "provider": prov_key,
             },
         )
         if db_row and db_row["loudness"] != inf and db_row["loudness"] != -inf:
@@ -1207,18 +1215,18 @@ class MusicController(CoreController):
 
         # Try to get position from providers
         for prov_mapping in media_item.provider_mappings:
-            if not (provider := self.mass.get_provider(prov_mapping.provider_instance)):
+            if not (
+                provider := self.mass.get_provider(
+                    prov_mapping.provider_instance, provider_type=MusicProvider
+                )
+            ):
                 continue
-            # Type guard: ensure this is a MusicProvider with get_resume_position method
-            if isinstance(provider, MusicProvider):
-                with suppress(NotImplementedError):
-                    (
-                        provider_fully_played,
-                        provider_position_ms,
-                    ) = await provider.get_resume_position(
-                        prov_mapping.item_id, media_item.media_type
-                    )
-                    break  # Use first provider that returns data
+            with suppress(NotImplementedError):
+                (
+                    provider_fully_played,
+                    provider_position_ms,
+                ) = await provider.get_resume_position(prov_mapping.item_id, media_item.media_type)
+                break  # Use first provider that returns data
 
         # Get MA's internal position from playlog
         ma_fully_played = False
@@ -1275,18 +1283,18 @@ class MusicController(CoreController):
 
     def get_unique_providers(self) -> set[str]:
         """
-        Return all unique MusicProvider instance ids.
+        Return all unique MusicProvider (instance or domain) ids.
 
-        This will return all filebased instances but only one instance
-        for streaming providers.
+        This will return instance_ids for non-streaming providers
+        and domain names for streaming providers to avoid duplicates.
         """
-        instances = set()
-        domains = set()
+        result: set[str] = set()
         for provider in self.providers:
-            if provider.domain not in domains or not provider.is_streaming_provider:
-                instances.add(provider.instance_id)
-                domains.add(provider.domain)
-        return instances
+            if provider.is_streaming_provider:
+                result.add(provider.domain)
+            else:
+                result.add(provider.instance_id)
+        return result
 
     async def cleanup_provider(self, provider_instance: str) -> None:
         """Cleanup provider records from the database."""
index 39e38104ad1eb590b4ed75c3207237c1c0e7938f..83d23c7e0944fb75e3622f6a5ce691592f6fa0a0 100644 (file)
@@ -211,7 +211,7 @@ class PlayerController(CoreController):
             for player in self._players.values()
             if (player.available or return_unavailable)
             and (player.enabled or return_disabled)
-            and (provider_filter is None or player.provider.lookup_key == provider_filter)
+            and (provider_filter is None or player.provider.instance_id == provider_filter)
             and (not user_filter or player.player_id in user_filter)
             and (return_sync_groups or not isinstance(player, SyncGroupPlayer))
         ]
@@ -978,7 +978,7 @@ class PlayerController(CoreController):
             # check if player can be synced/grouped with the target player
             if not (
                 child_player_id in parent_player.can_group_with
-                or child_player.provider.lookup_key in parent_player.can_group_with
+                or child_player.provider.instance_id in parent_player.can_group_with
                 or "*" in parent_player.can_group_with
             ):
                 raise UnsupportedFeaturedException(
index 38366b23e92d594794b7c9fcf18449e9c1b10fb5..a62b0de5f6fbb3dd69ef5bd0e74ab15335cf0d03 100644 (file)
@@ -179,7 +179,7 @@ class SyncGroupPlayer(GroupPlayer):
         if self.is_dynamic and (leader := self.sync_leader):
             return leader.can_group_with
         elif self.is_dynamic:
-            return {self.provider.lookup_key}
+            return {self.provider.instance_id}
         else:
             return set()
 
@@ -565,7 +565,7 @@ class SyncGroupController:
         player_id = f"{SYNCGROUP_PREFIX}{shortuuid.random(8).lower()}"
         self.mass.config.create_default_player_config(
             player_id=player_id,
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=name,
             enabled=True,
             values={
@@ -593,7 +593,7 @@ class SyncGroupController:
     async def on_provider_loaded(self, provider: PlayerProvider) -> None:
         """Handle logic when a provider is loaded."""
         # register existing syncgroup players for this provider
-        for player_conf in await self.mass.config.get_player_configs(provider.lookup_key):
+        for player_conf in await self.mass.config.get_player_configs(provider.instance_id):
             if player_conf.player_id.startswith(SYNCGROUP_PREFIX):
                 await self._register_syncgroup_player(player_conf.player_id, provider)
 
@@ -601,7 +601,7 @@ class SyncGroupController:
         """Handle logic when a provider is (about to get) unloaded."""
         # unregister existing syncgroup players for this provider
         for player in self.mass.players.all(
-            provider_filter=provider.lookup_key, return_sync_groups=True
+            provider_filter=provider.instance_id, return_sync_groups=True
         ):
             if player.player_id.startswith(SYNCGROUP_PREFIX):
                 await self.mass.players.unregister(player.player_id, False)
index 5e918b476cf88e8b85239d69f09522fe9fdfe628..0674b0098f88c6c47933f051e62a7a99540cd2f5 100644 (file)
@@ -239,6 +239,7 @@ async def get_stream_details(
     This is called just-in-time when a PlayerQueue wants a MediaItem to be played.
     Do not try to request streamdetails too much in advance as this is expiring data.
     """
+    streamdetails: StreamDetails | None = None
     time_start = time.time()
     LOGGER.debug("Getting streamdetails for %s", queue_item.uri)
     if seek_position and (queue_item.media_type == MediaType.RADIO or not queue_item.duration):
@@ -259,36 +260,56 @@ async def get_stream_details(
         # already got a fresh/unused (or unexpired) streamdetails
         streamdetails = queue_item.streamdetails
     else:
+        # need to (re)create streamdetails
         # retrieve streamdetails from provider
+
         media_item = queue_item.media_item
         assert media_item is not None  # for type checking
-        # sort by quality and check item's availability
-        for prov_media in sorted(
-            media_item.provider_mappings, key=lambda x: x.quality or 0, reverse=True
+        preferred_providers: list[str] = []
+        if (
+            (queue := mass.player_queues.get(queue_item.queue_id))
+            and queue.userid
+            and (playback_user := await mass.webserver.auth.get_user(queue.userid))
+            and playback_user.provider_filter
         ):
-            if not prov_media.available:
-                LOGGER.debug(f"Skipping unavailable {prov_media}")
-                continue
-            # guard that provider is available
-            music_prov = mass.get_provider(prov_media.provider_instance)
-            if TYPE_CHECKING:  # avoid circular import
-                assert isinstance(music_prov, MusicProvider)
-            if not music_prov:
-                LOGGER.debug(f"Skipping {prov_media} - provider not available")
-                continue  # provider not available ?
-            # get streamdetails from provider
-            try:
-                BYPASS_THROTTLER.set(True)
-                streamdetails = await music_prov.get_stream_details(
-                    prov_media.item_id, media_item.media_type
-                )
-            except MusicAssistantError as err:
-                LOGGER.warning(str(err))
-            else:
-                break
-            finally:
-                BYPASS_THROTTLER.set(False)
+            # handle steering into user preferred providerinstance
+            preferred_providers = playback_user.provider_filter
         else:
+            preferred_providers = [x.provider_instance for x in media_item.provider_mappings]
+        for allow_other_provider in (False, True):
+            # sort by quality and check item's availability
+            for prov_media in sorted(
+                media_item.provider_mappings, key=lambda x: x.quality or 0, reverse=True
+            ):
+                if not prov_media.available:
+                    LOGGER.debug(f"Skipping unavailable {prov_media}")
+                    continue
+                if (
+                    not allow_other_provider
+                    and prov_media.provider_instance not in preferred_providers
+                ):
+                    continue
+                # guard that provider is available
+                music_prov = mass.get_provider(prov_media.provider_instance)
+                if TYPE_CHECKING:  # avoid circular import
+                    assert isinstance(music_prov, MusicProvider)
+                if not music_prov:
+                    LOGGER.debug(f"Skipping {prov_media} - provider not available")
+                    continue  # provider not available ?
+                # get streamdetails from provider
+                try:
+                    BYPASS_THROTTLER.set(True)
+                    streamdetails = await music_prov.get_stream_details(
+                        prov_media.item_id, media_item.media_type
+                    )
+                except MusicAssistantError as err:
+                    LOGGER.warning(str(err))
+                else:
+                    break
+                finally:
+                    BYPASS_THROTTLER.set(False)
+
+        if not streamdetails:
             msg = f"Unable to retrieve streamdetails for {queue_item.name} ({queue_item.uri})"
             raise MediaNotFoundError(msg)
 
index 0ff5a825c2a624f311cc932fc399ba6cdcf9be9c..886eb60183f53d3ed3c8b2098441178e3d0301ae 100644 (file)
@@ -43,9 +43,8 @@ def parse_podcast(
     *,
     feed_url: str,
     parsed_feed: dict[str, Any],
-    lookup_key: str,
-    domain: str,
     instance_id: str,
+    domain: str,
     mass_item_id: str | None = None,
 ) -> Podcast:
     """Podcast -> Mass Podcast.
@@ -58,7 +57,7 @@ def parse_podcast(
         item_id=item_id,
         name=parsed_feed.get("title", "NO_TITLE"),
         publisher=publisher,
-        provider=lookup_key,
+        provider=instance_id,
         uri=parsed_feed.get("link"),
         provider_mappings={
             ProviderMapping(
@@ -91,7 +90,7 @@ def parse_podcast(
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=podcast_cover,
-                    provider=lookup_key,
+                    provider=instance_id,
                     remotely_accessible=True,
                 )
             ]
@@ -123,9 +122,8 @@ def parse_podcast_episode(
     prov_podcast_id: str,
     episode_cnt: int,
     podcast_cover: str | None = None,
-    lookup_key: str,
-    domain: str,
     instance_id: str,
+    domain: str,
     mass_item_id: str | None = None,
 ) -> PodcastEpisode | None:
     """Podcast Episode -> Mass Podcast Episode.
@@ -157,13 +155,13 @@ def parse_podcast_episode(
     episode_id = f"{prov_podcast_id} {guid_or_stream_url}" if mass_item_id is None else mass_item_id
     mass_episode = PodcastEpisode(
         item_id=episode_id,
-        provider=lookup_key,
+        provider=instance_id,
         name=episode_title,
         duration=int(episode_duration),
         position=episode_cnt,
         podcast=ItemMapping(
             item_id=prov_podcast_id,
-            provider=lookup_key,
+            provider=instance_id,
             name=episode_title,
             media_type=MediaType.PODCAST,
         ),
@@ -200,7 +198,7 @@ def parse_podcast_episode(
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=episode_cover,
-                    provider=lookup_key,
+                    provider=instance_id,
                     remotely_accessible=True,
                 )
             ]
index f8f754840512b34b77cc5b5e35a8f485a80844d7..4d5d252e7df1545ffb2483ef9147fb6e038a0f5b 100644 (file)
@@ -8,7 +8,7 @@ import os
 import pathlib
 import threading
 from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine
-from typing import TYPE_CHECKING, Any, Self, TypeGuard, TypeVar, cast
+from typing import TYPE_CHECKING, Any, Self, TypeGuard, TypeVar, cast, overload
 from uuid import uuid4
 
 import aiofiles
@@ -86,6 +86,7 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 PROVIDERS_PATH = os.path.join(BASE_DIR, "providers")
 
 _R = TypeVar("_R")
+_ProviderT = TypeVar("_ProviderT", bound=ProviderInstanceType)
 
 
 def is_music_provider(provider: ProviderInstanceType) -> TypeGuard[MusicProvider]:
@@ -304,10 +305,35 @@ class MusicAssistant:
         """Return all loaded/running Providers (instances)."""
         return list(self._providers.values())
 
+    @overload
     def get_provider(
-        self, provider_instance_or_domain: str, return_unavailable: bool = False
-    ) -> ProviderInstanceType | None:
-        """Return provider by instance id or domain."""
+        self,
+        provider_instance_or_domain: str,
+        return_unavailable: bool = False,
+        provider_type: None = None,
+    ) -> ProviderInstanceType | None: ...
+
+    @overload
+    def get_provider(
+        self,
+        provider_instance_or_domain: str,
+        return_unavailable: bool = False,
+        *,
+        provider_type: type[_ProviderT],
+    ) -> _ProviderT | None: ...
+
+    def get_provider(
+        self,
+        provider_instance_or_domain: str,
+        return_unavailable: bool = False,
+        provider_type: type[_ProviderT] | None = None,
+    ) -> ProviderInstanceType | _ProviderT | None:
+        """Return provider by instance id or domain.
+
+        :param provider_instance_or_domain: Instance ID or domain of the provider.
+        :param return_unavailable: Also return unavailable providers.
+        :param provider_type: Optional type hint for the expected provider type (unused at runtime).
+        """
         # lookup by instance_id first
         if prov := self._providers.get(provider_instance_or_domain):
             if return_unavailable or prov.available:
@@ -899,14 +925,14 @@ class MusicAssistant:
                     *{x.domain for x in self.providers},
                     *{x.instance_id for x in self.providers},
                 },
-                "unique_providers": {x.lookup_key for x in self.providers},
+                "unique_providers": self.music.get_unique_providers(),
                 "streaming_providers": {
-                    x.lookup_key
+                    x.domain
                     for x in self.providers
                     if is_music_provider(x) and x.is_streaming_provider
                 },
                 "non_streaming_providers": {
-                    x.lookup_key
+                    x.instance_id
                     for x in self.providers
                     if not (is_music_provider(x) and x.is_streaming_provider)
                 },
index adf5d4df51810d61f4c2e3f0523128a77137c241..4c241517f37c0302ddfb19d46eea9f3105c7fe1d 100644 (file)
@@ -65,13 +65,6 @@ class MusicProvider(Provider):
         """
         return True
 
-    @property
-    def lookup_key(self) -> str:
-        """Return domain if (multi-instance) streaming_provider or instance_id otherwise."""
-        if self.is_streaming_provider or not self.manifest.multi_instance:
-            return self.domain
-        return self.instance_id
-
     async def loaded_in_mass(self) -> None:
         """Call after the provider has been loaded."""
 
index 2f42cca375b993c05a73d23c687967fafe17b2bc..480d647581d209a3c5b877d90c995b2bda302ca1 100644 (file)
@@ -726,8 +726,8 @@ class Player(ABC):
     @property
     @final
     def provider_id(self) -> str:
-        """Return the provider id of the player."""
-        return self._provider.lookup_key
+        """Return the provider (instance) id of the player."""
+        return self._provider.instance_id
 
     @property
     @final
index 5f193278413dea9137f759e1acd73b8bbe0fc161..135bffef737ccf63c4ad23c2395ca5aedee939a3 100644 (file)
@@ -83,4 +83,4 @@ class PlayerProvider(Provider):
     @property
     def players(self) -> list[Player]:
         """Return all players belonging to this provider."""
-        return self.mass.players.all(provider_filter=self.lookup_key, return_sync_groups=False)
+        return self.mass.players.all(provider_filter=self.instance_id, return_sync_groups=False)
index 10685e28050b090a0f39dbb65913203c509cdd15..432e7439a8f97bf2c8b4ef8d781e1fb5710d4ad3 100644 (file)
@@ -54,12 +54,6 @@ class Provider:
         # should not be overridden in normal circumstances
         return self._supported_features
 
-    @property
-    def lookup_key(self) -> str:
-        """Return instance_id if multi_instance capable or domain otherwise."""
-        # should not be overridden in normal circumstances
-        return self.instance_id if self.manifest.multi_instance else self.domain
-
     async def handle_async_init(self) -> None:
         """Handle async initialization of the provider."""
 
@@ -152,7 +146,6 @@ class Provider:
             "default_name": self.default_name,
             "instance_name_postfix": self.instance_name_postfix,
             "instance_id": self.instance_id,
-            "lookup_key": self.lookup_key,
             "supported_features": [x.value for x in self.supported_features],
             "available": self.available,
             "is_streaming_provider": getattr(self, "is_streaming_provider", None),
index 26669075f50e87731a5ceeeb4c4dd758c227b6b4..652039d5ca54ce1f9de9a35dace84cec17ea504f 100644 (file)
@@ -153,7 +153,7 @@ class MyDemoPluginprovider(PluginProvider):
         # the audio_format field should be the native audio format of the stream
         # that is returned by the get_audio_stream method.
         return PluginSource(
-            id=self.lookup_key,
+            id=self.instance_id,
             name=self.name,
             passive=False,
             can_play_pause=False,
index 410965eb2f6a8f7ef8abcdea50ff05c34bc7e63e..79fb43226fb8462de048f5dd6c9dd03f3769e3d1 100644 (file)
@@ -105,7 +105,7 @@ class AirPlayPlayer(Player):
             PlayerFeature.VOLUME_SET,
         }
         self._attr_volume_level = initial_volume
-        self._attr_can_group_with = {provider.lookup_key}
+        self._attr_can_group_with = {provider.instance_id}
         self._attr_enabled_by_default = not is_broken_airplay_model(manufacturer, model)
 
     @cached_property
@@ -460,7 +460,7 @@ class AirPlayPlayer(Player):
         await self.mass.cache.set(
             key=self.player_id,
             data=volume_level,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             category=CACHE_CATEGORY_PREV_VOLUME,
         )
 
index 4190f753e77a42e8bba205f02c127946178e9c22..e0ea6199aa338cca63efd71a69a04a7123906096 100644 (file)
@@ -169,7 +169,7 @@ class AirPlayProvider(PlayerProvider):
         # Get volume from cache
         if not (
             volume := await self.mass.cache.get(
-                key=player_id, provider=self.lookup_key, category=CACHE_CATEGORY_PREV_VOLUME
+                key=player_id, provider=self.instance_id, category=CACHE_CATEGORY_PREV_VOLUME
             )
         ):
             volume = FALLBACK_VOLUME
index 48ff6127ee85390ceecf701271d4574f062d75ba..f319221839bb65a479cdbd725d02ce4a23e6ebb1 100644 (file)
@@ -641,7 +641,7 @@ class AppleMusicProvider(MusicProvider):
                 ) from exc
             return StreamDetails(
                 item_id=item_id,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 path=stream_url,
                 stream_type=StreamType.HTTP,
                 audio_format=AudioFormat(content_type=ContentType.UNKNOWN),
@@ -656,7 +656,7 @@ class AppleMusicProvider(MusicProvider):
         key_id = base64.b64decode(uri.split(",")[1])
         return StreamDetails(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(content_type=ContentType.MP4, codec_type=ContentType.AAC),
             stream_type=StreamType.ENCRYPTED_HTTP,
             decryption_key=await self._get_decryption_key(license_url, key_id, uri, item_id),
@@ -698,7 +698,7 @@ class AppleMusicProvider(MusicProvider):
             # No more details available other than the id, return an ItemMapping
             return ItemMapping(
                 media_type=MediaType.ARTIST,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 item_id=artist_id,
                 name=artist_id,
             )
@@ -718,7 +718,7 @@ class AppleMusicProvider(MusicProvider):
         if artwork := attributes.get("artwork"):
             artist.metadata.add_image(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.THUMB,
                     path=artwork["url"].format(w=artwork["width"], h=artwork["height"]),
                     remotely_accessible=True,
@@ -751,7 +751,7 @@ class AppleMusicProvider(MusicProvider):
             # No more details available other than the id, return an ItemMapping
             return ItemMapping(
                 media_type=MediaType.ALBUM,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 item_id=album_id,
                 name=album_id,
             )
@@ -783,7 +783,7 @@ class AppleMusicProvider(MusicProvider):
                 [
                     ItemMapping(
                         media_type=MediaType.ARTIST,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         item_id=artist_name,
                         name=artist_name,
                     )
@@ -796,7 +796,7 @@ class AppleMusicProvider(MusicProvider):
         if artwork := attributes.get("artwork"):
             album.metadata.add_image(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.THUMB,
                     path=artwork["url"].format(w=artwork["width"], h=artwork["height"]),
                     remotely_accessible=True,
@@ -879,7 +879,7 @@ class AppleMusicProvider(MusicProvider):
                 ItemMapping(
                     media_type=MediaType.ARTIST,
                     item_id=artist_name,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     name=artist_name,
                 )
             ]
@@ -889,7 +889,7 @@ class AppleMusicProvider(MusicProvider):
         if artwork := attributes.get("artwork"):
             track.metadata.add_image(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.THUMB,
                     path=artwork["url"].format(w=artwork["width"], h=artwork["height"]),
                     remotely_accessible=True,
@@ -913,7 +913,7 @@ class AppleMusicProvider(MusicProvider):
         is_editable = attributes.get("canEdit", False)
         playlist = Playlist(
             item_id=playlist_id,
-            provider=self.instance_id if is_editable else self.lookup_key,
+            provider=self.instance_id,
             name=attributes.get("name", UNKNOWN_PLAYLIST_NAME),
             owner=attributes.get("curatorName", "me"),
             provider_mappings={
@@ -932,7 +932,7 @@ class AppleMusicProvider(MusicProvider):
                 url = url.format(w=artwork["width"], h=artwork["height"])
             playlist.metadata.add_image(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.THUMB,
                     path=url,
                     remotely_accessible=True,
index 8d706445cf68f45c507b55d0f224d5055e27ec01..2c4ca65552372e1ccb158922a975c8ab6ebbc03e 100644 (file)
@@ -381,7 +381,6 @@ class ARDAudiothek(MusicProvider):
                 podcasts += [
                     _parse_podcast(
                         self.domain,
-                        self.lookup_key,
                         self.instance_id,
                         element,
                         element["coreId"],
@@ -476,7 +475,6 @@ class ARDAudiothek(MusicProvider):
 
         return _parse_podcast(
             self.domain,
-            self.lookup_key,
             self.instance_id,
             result,
             prov_podcast_id,
@@ -514,7 +512,6 @@ class ARDAudiothek(MusicProvider):
                     progress = self._get_progress(episode_id)
                     yield _parse_podcast_episode(
                         self.domain,
-                        self.lookup_key,
                         self.instance_id,
                         episode,
                         episode_id,
@@ -535,7 +532,6 @@ class ARDAudiothek(MusicProvider):
         progress = self._get_progress(prov_episode_id)
         return _parse_podcast_episode(
             self.domain,
-            self.lookup_key,
             self.instance_id,
             result,
             result["showId"],
@@ -667,7 +663,6 @@ class ARDAudiothek(MusicProvider):
 
             podcast = _parse_podcast(
                 self.domain,
-                self.lookup_key,
                 self.instance_id,
                 pod,
                 pod["coreId"],
@@ -700,7 +695,6 @@ def _parse_social_media(
 
 def _parse_podcast(
     domain: str,
-    lookup_key: str,
     instance_id: str,
     podcast_query: dict[str, Any],
     podcast_id: str,
@@ -709,7 +703,7 @@ def _parse_podcast(
         name=podcast_query["title"],
         item_id=podcast_id,
         publisher=podcast_query["publicationService"]["title"],
-        provider=lookup_key,
+        provider=instance_id,
         provider_mappings={
             ProviderMapping(
                 item_id=podcast_id,
@@ -767,7 +761,6 @@ def _parse_radio(
 
 def _parse_podcast_episode(
     domain: str,
-    lookup_key: str,
     instance_id: str,
     episode: dict[str, Any],
     podcast_id: str,
@@ -779,10 +772,10 @@ def _parse_podcast_episode(
         name=episode["title"],
         duration=episode["duration"],
         item_id=episode["coreId"],
-        provider=lookup_key,
+        provider=instance_id,
         podcast=ItemMapping(
             item_id=podcast_id,
-            provider=lookup_key,
+            provider=instance_id,
             name=podcast_title,
             media_type=MediaType.PODCAST,
         ),
index 12aaf71b65d3a4e84c91f1584796e74525f22f91..cf5d502fe359d7d5997516c470508ff2feae90fc 100644 (file)
@@ -393,9 +393,8 @@ for more details.
                     assert isinstance(podcast_minified, LibraryItemMinifiedPodcast)
                     mass_podcast = parse_podcast(
                         abs_podcast=podcast_minified,
-                        lookup_key=self.lookup_key,
-                        domain=self.domain,
                         instance_id=self.instance_id,
+                        domain=self.domain,
                         token=self._client.token,
                         base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                     )
@@ -424,9 +423,8 @@ for more details.
         abs_podcast = await self._get_abs_expanded_podcast(prov_podcast_id=prov_podcast_id)
         return parse_podcast(
             abs_podcast=abs_podcast,
-            lookup_key=self.lookup_key,
-            domain=self.domain,
             instance_id=self.instance_id,
+            domain=self.domain,
             token=self._client.token,
             base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
         )
@@ -455,9 +453,8 @@ for more details.
                 episode=abs_episode,
                 prov_podcast_id=prov_podcast_id,
                 fallback_episode_cnt=episode_cnt,
-                lookup_key=self.lookup_key,
-                domain=self.domain,
                 instance_id=self.instance_id,
+                domain=self.domain,
                 token=self._client.token,
                 base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                 media_progress=progress,
@@ -484,9 +481,8 @@ for more details.
                     episode=abs_episode,
                     prov_podcast_id=prov_podcast_id,
                     fallback_episode_cnt=episode_cnt,
-                    lookup_key=self.lookup_key,
-                    domain=self.domain,
                     instance_id=self.instance_id,
+                    domain=self.domain,
                     token=self._client.token,
                     base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                     media_progress=progress,
@@ -515,9 +511,8 @@ for more details.
                         continue
                     mass_audiobook = parse_audiobook(
                         abs_audiobook=book_expanded,
-                        lookup_key=self.lookup_key,
-                        domain=self.domain,
                         instance_id=self.instance_id,
+                        domain=self.domain,
                         token=self._client.token,
                         base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                     )
@@ -545,9 +540,8 @@ for more details.
         abs_audiobook = await self._get_abs_expanded_audiobook(prov_audiobook_id=prov_audiobook_id)
         return parse_audiobook(
             abs_audiobook=abs_audiobook,
-            lookup_key=self.lookup_key,
-            domain=self.domain,
             instance_id=self.instance_id,
+            domain=self.domain,
             token=self._client.token,
             base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
             media_progress=progress,
@@ -591,7 +585,7 @@ for more details.
             file_parts.append(MultiPartPath(path=stream_url, duration=track.duration))
 
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=abs_audiobook.id_,
             audio_format=AudioFormat(content_type=content_type),
             media_type=MediaType.AUDIOBOOK,
@@ -624,7 +618,7 @@ for more details.
         base_url = str(self.config.get_value(CONF_URL))
         stream_url = f"{base_url}{abs_episode.audio_track.content_url}?token={self._client.token}"
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=podcast_id,
             audio_format=AudioFormat(
                 content_type=content_type,
@@ -727,7 +721,7 @@ for more details.
                     icon=ABS_SHELF_ID_ICONS.get(shelf_id),
                     # translation_key=shelf.id_,
                     items=UniqueList(recommendation_items),
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                 )
             )
 
@@ -770,7 +764,7 @@ for more details.
                 icon="mdi-bookshelf",
                 # translation_key=shelf.id_,
                 items=UniqueList(browse_items),
-                provider=self.lookup_key,
+                provider=self.instance_id,
             )
         )
 
@@ -826,9 +820,8 @@ for more details.
                             item = parse_podcast_episode(
                                 episode=entity.recent_episode,
                                 prov_podcast_id=podcast_id,
-                                lookup_key=self.lookup_key,
-                                domain=self.domain,
                                 instance_id=self.instance_id,
+                                domain=self.domain,
                                 token=self._client.token,
                                 base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                             )
@@ -851,7 +844,7 @@ for more details.
                                 BrowseFolder(
                                     item_id=entity.id_,
                                     name=entity.name,
-                                    provider=self.lookup_key,
+                                    provider=self.instance_id,
                                     path=path,
                                 )
                             )
@@ -880,7 +873,7 @@ for more details.
                             BrowseFolder(
                                 item_id=entity.id_,
                                 name=entity.name,
-                                provider=self.lookup_key,
+                                provider=self.instance_id,
                                 path=path,
                             )
                         )
@@ -1056,7 +1049,7 @@ for more details.
             return BrowseFolder(
                 item_id=lib_id,
                 name=lib_name,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 path=f"{self.instance_id}://{path}",
             )
 
@@ -1103,7 +1096,7 @@ for more details.
                 BrowseFolder(
                     item_id=item_name.lower(),
                     name=item_name,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     path=path,
                 )
             )
@@ -1118,7 +1111,7 @@ for more details.
                 BrowseFolder(
                     item_id=author.id_,
                     name=author.name,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     path=path,
                 )
             )
@@ -1134,7 +1127,7 @@ for more details.
                 BrowseFolder(
                     item_id=narrator.id_,
                     name=narrator.name,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     path=path,
                 )
             )
@@ -1152,7 +1145,7 @@ for more details.
                     BrowseFolder(
                         item_id=abs_series.id_,
                         name=abs_series.name,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         path=path,
                     )
                 )
@@ -1172,7 +1165,7 @@ for more details.
                     BrowseFolder(
                         item_id=abs_collection.id_,
                         name=abs_collection.name,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         path=path,
                     )
                 )
@@ -1213,7 +1206,7 @@ for more details.
                 BrowseFolder(
                     item_id=series.id_,
                     name=f"{series.name} ({AbsBrowseItemsBook.SERIES})",
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     path=path,
                 )
             )
@@ -1297,9 +1290,8 @@ for more details.
                 await self.mass.music.audiobooks.add_item_to_library(
                     parse_audiobook(
                         abs_audiobook=abs_item,
-                        lookup_key=self.lookup_key,
-                        domain=self.domain,
                         instance_id=self.instance_id,
+                        domain=self.domain,
                         token=self._client.token,
                         base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                     ),
@@ -1314,9 +1306,8 @@ for more details.
                 )
                 mass_podcast = parse_podcast(
                     abs_podcast=abs_item,
-                    lookup_key=self.lookup_key,
-                    domain=self.domain,
                     instance_id=self.instance_id,
+                    domain=self.domain,
                     token=self._client.token,
                     base_url=str(self.config.get_value(CONF_URL)).rstrip("/"),
                 )
@@ -1474,7 +1465,7 @@ for more details.
                 if discarded_item := await self.mass.music.get_library_item_by_prov_id(
                     media_type=MediaType.AUDIOBOOK,
                     item_id=discarded_progress_id,
-                    provider_instance_id_or_domain=self.lookup_key,
+                    provider_instance_id_or_domain=self.instance_id,
                 ):
                     self.progress_guard.add_progress(discarded_progress_id)
                     await self.mass.music.mark_item_unplayed(discarded_item)
index 6c9137d85ec846d0293451c984e5ac019abd22d1..d146fb47448be15bf53527ea1c9940b5c9c08767 100644 (file)
@@ -42,9 +42,8 @@ def parse_podcast(
     abs_podcast: AbsLibraryItemExpandedPodcast
     | AbsLibraryItemMinifiedPodcast
     | AbsLibraryItemPodcast,
-    lookup_key: str,
-    domain: str,
     instance_id: str,
+    domain: str,
     token: str | None,
     base_url: str,
 ) -> MassPodcast:
@@ -57,7 +56,7 @@ def parse_podcast(
         item_id=abs_podcast.id_,
         name=title,
         publisher=abs_podcast.media.metadata.author,
-        provider=lookup_key,
+        provider=instance_id,
         provider_mappings={
             ProviderMapping(
                 item_id=abs_podcast.id_,
@@ -70,7 +69,7 @@ def parse_podcast(
     if token is not None:
         image_url = f"{base_url}/api/items/{abs_podcast.id_}/cover?token={token}"
         mass_podcast.metadata.images = UniqueList(
-            [MediaItemImage(type=ImageType.THUMB, path=image_url, provider=lookup_key)]
+            [MediaItemImage(type=ImageType.THUMB, path=image_url, provider=instance_id)]
         )
     mass_podcast.metadata.explicit = abs_podcast.media.metadata.explicit
     if abs_podcast.media.metadata.language is not None:
@@ -98,9 +97,8 @@ def parse_podcast_episode(
     episode: AbsPodcastEpisode | AbsPodcastEpisodeExpanded,
     prov_podcast_id: str,
     fallback_episode_cnt: int | None = None,
-    lookup_key: str,
-    domain: str,
     instance_id: str,
+    domain: str,
     token: str | None,
     base_url: str,
     media_progress: AbsMediaProgress | None = None,
@@ -153,13 +151,13 @@ def parse_podcast_episode(
             position = fallback_episode_cnt
     mass_episode = MassPodcastEpisode(
         item_id=episode_id,
-        provider=lookup_key,
+        provider=instance_id,
         name=episode.title,
         duration=duration,
         position=position,
         podcast=ItemMapping(
             item_id=prov_podcast_id,
-            provider=lookup_key,
+            provider=instance_id,
             name=episode.title,
             media_type=MediaType.PODCAST,
         ),
@@ -173,7 +171,7 @@ def parse_podcast_episode(
         url_api = f"/api/items/{prov_podcast_id}/cover?token={token}"
         url_cover = f"{base_url}{url_api}"
         mass_episode.metadata.images = UniqueList(
-            [MediaItemImage(type=ImageType.THUMB, path=url_cover, provider=lookup_key)]
+            [MediaItemImage(type=ImageType.THUMB, path=url_cover, provider=instance_id)]
         )
 
     if media_progress is not None and media_progress.current_time is not None:
@@ -186,9 +184,8 @@ def parse_podcast_episode(
 def parse_audiobook(
     *,
     abs_audiobook: AbsLibraryItemExpandedBook | AbsLibraryItemMinifiedBook,
-    lookup_key: str,
-    domain: str,
     instance_id: str,
+    domain: str,
     token: str | None,
     base_url: str,
     media_progress: AbsMediaProgress | None = None,
@@ -203,7 +200,7 @@ def parse_audiobook(
         title += f" | {subtitle}"
     mass_audiobook = MassAudiobook(
         item_id=abs_audiobook.id_,
-        provider=lookup_key,
+        provider=instance_id,
         name=title,
         duration=int(abs_audiobook.media.duration),
         provider_mappings={
@@ -241,7 +238,7 @@ def parse_audiobook(
         api_url = f"/api/items/{abs_audiobook.id_}/cover?token={token}"
         cover_url = f"{base_url}{api_url}"
         mass_audiobook.metadata.images = UniqueList(
-            [MediaItemImage(type=ImageType.THUMB, path=cover_url, provider=lookup_key)]
+            [MediaItemImage(type=ImageType.THUMB, path=cover_url, provider=instance_id)]
         )
 
     # expanded version
index 50dba51fde58ea09b66f0faeea8d5b792480a078..abad06f0c5d21e55fb11cc2e9ee675568b0fb30c 100644 (file)
@@ -74,7 +74,7 @@ class BluesoundPlayer(Player):
         self._attr_source_list = []
         self._attr_needs_poll = True
         self._attr_poll_interval = IDLE_POLL_INTERVAL
-        self._attr_can_group_with = {provider.lookup_key}
+        self._attr_can_group_with = {provider.instance_id}
 
     async def setup(self) -> None:
         """Set up the player."""
index c138478ffc74ce8a8fb1b03014c1a5b372cde9d4..8069e1b66a6a3c858726b8aa3fe28811fd9b0df1 100644 (file)
@@ -308,7 +308,7 @@ class BuiltinProvider(MusicProvider):
                 self.logger.warning("Radio station %s not found: %s", item, err)
                 yield Radio(
                     item_id=item["item_id"],
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     name=item["name"],
                     provider_mappings={
                         ProviderMapping(
index e072b750e29ab42049c4287dee5cf3e42652a60a..a8cd37f4855a9c5316f752526f1260875e9435ad 100644 (file)
@@ -425,7 +425,7 @@ class DeezerProvider(MusicProvider):
         return [
             RecommendationFolder(
                 item_id="recommended_tracks",
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 name="Recommended tracks",
                 translation_key="recommended_tracks",
                 items=UniqueList(
@@ -476,7 +476,7 @@ class DeezerProvider(MusicProvider):
         url = url_details["sources"][0]["url"]
         return StreamDetails(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(
                 content_type=ContentType.try_parse(url_details["format"].split("_")[0])
             ),
@@ -557,7 +557,7 @@ class DeezerProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=track.album.cover_big,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -572,7 +572,7 @@ class DeezerProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=album.cover_big,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -587,7 +587,7 @@ class DeezerProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=artist.picture_big,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -599,7 +599,7 @@ class DeezerProvider(MusicProvider):
         """Parse the deezer-python artist to a Music Assistant artist."""
         return Artist(
             item_id=str(artist.id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=artist.name,
             media_type=MediaType.ARTIST,
             provider_mappings={
@@ -618,14 +618,14 @@ class DeezerProvider(MusicProvider):
         return Album(
             album_type=self.get_album_type(album),
             item_id=str(album.id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=album.title,
             artists=UniqueList(
                 [
                     ItemMapping(
                         media_type=MediaType.ARTIST,
                         item_id=str(album.artist.id),
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         name=album.artist.name,
                     )
                 ]
@@ -648,7 +648,7 @@ class DeezerProvider(MusicProvider):
         is_editable = creator.id == self.user.id
         return Playlist(
             item_id=str(playlist.id),
-            provider=self.instance_id if is_editable else self.lookup_key,
+            provider=self.instance_id,
             name=playlist.title,
             media_type=MediaType.PLAYLIST,
             provider_mappings={
@@ -665,7 +665,7 @@ class DeezerProvider(MusicProvider):
                         MediaItemImage(
                             type=ImageType.THUMB,
                             path=playlist.picture_big,
-                            provider=self.lookup_key,
+                            provider=self.instance_id,
                             remotely_accessible=True,
                         )
                     ]
@@ -687,7 +687,7 @@ class DeezerProvider(MusicProvider):
             artist = ItemMapping(
                 media_type=MediaType.ARTIST,
                 item_id=str(getattr(track.artist, "id", f"deezer-{track.artist.name}")),
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 name=track.artist.name,
             )
         else:
@@ -696,7 +696,7 @@ class DeezerProvider(MusicProvider):
             album = ItemMapping(
                 media_type=MediaType.ALBUM,
                 item_id=str(track.album.id),
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 name=track.album.title,
             )
         else:
@@ -704,7 +704,7 @@ class DeezerProvider(MusicProvider):
 
         item = Track(
             item_id=str(track.id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=track.title,
             sort_name=self.get_short_title(track),
             duration=track.duration,
index 13cb26fa20376fa4b4d4c32a3529fc4148ec74bd..3aafc94cf6f7cc31ad1a762afb5bd8fa94ee088e 100644 (file)
@@ -402,9 +402,8 @@ class GPodder(MusicProvider):
             yield parse_podcast(
                 feed_url=feed_url,
                 parsed_feed=parsed_podcast,
-                lookup_key=self.lookup_key,
-                domain=self.domain,
                 instance_id=self.instance_id,
+                domain=self.domain,
             )
 
         self.timestamp_subscriptions = subscriptions.timestamp
@@ -420,9 +419,8 @@ class GPodder(MusicProvider):
         return parse_podcast(
             feed_url=prov_podcast_id,
             parsed_feed=parsed_podcast,
-            lookup_key=self.lookup_key,
-            domain=self.domain,
             instance_id=self.instance_id,
+            domain=self.domain,
         )
 
     async def get_podcast_episodes(
@@ -449,7 +447,6 @@ class GPodder(MusicProvider):
                 episode_cnt=cnt,
                 podcast_cover=podcast_cover,
                 domain=self.domain,
-                lookup_key=self.lookup_key,
                 instance_id=self.instance_id,
             )
             if mass_episode is None:
@@ -578,7 +575,7 @@ class GPodder(MusicProvider):
         if stream_url is None:
             raise MediaNotFoundError
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=item_id,
             audio_format=AudioFormat(
                 content_type=ContentType.try_parse(stream_url),
index 580711b508b3230aeeda138e8118e59ca664c66a..86296f8aeddb55a8e489fefa27bda2ef32cf59f0 100644 (file)
@@ -200,7 +200,7 @@ class IBroadcastProvider(MusicProvider):
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=name,
         )
 
@@ -241,7 +241,7 @@ class IBroadcastProvider(MusicProvider):
         url = await self._client.get_full_stream_url(int(item_id), "music-assistant")
 
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=item_id,
             audio_format=AudioFormat(
                 content_type=ContentType.UNKNOWN,
@@ -270,7 +270,7 @@ class IBroadcastProvider(MusicProvider):
         artist = Artist(
             item_id=artist_id,
             name=artist_obj["name"],
-            provider=self.lookup_key,
+            provider=self.instance_id,
             provider_mappings={
                 ProviderMapping(
                     item_id=artist_id,
@@ -287,7 +287,7 @@ class IBroadcastProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=await self._client.get_artist_artwork_url(artist_id),
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -300,7 +300,7 @@ class IBroadcastProvider(MusicProvider):
         name, version = parse_title_and_version(album_obj["name"])
         album = Album(
             item_id=album_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=name,
             year=album_obj["year"],
             version=version,
@@ -318,7 +318,7 @@ class IBroadcastProvider(MusicProvider):
             artist = Artist(
                 item_id=VARIOUS_ARTISTS_MBID,
                 name=VARIOUS_ARTISTS_NAME,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 provider_mappings={
                     ProviderMapping(
                         item_id=VARIOUS_ARTISTS_MBID,
@@ -353,7 +353,7 @@ class IBroadcastProvider(MusicProvider):
         return MediaItemImage(
             type=ImageType.THUMB,
             path=url,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             remotely_accessible=True,
         )
 
@@ -361,7 +361,7 @@ class IBroadcastProvider(MusicProvider):
         """Parse an iBroadcast track object to a Track model object."""
         track = Track(
             item_id=track_obj["track_id"],
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=track_obj["title"],
             provider_mappings={
                 ProviderMapping(
@@ -442,7 +442,7 @@ class IBroadcastProvider(MusicProvider):
         playlist_id = str(playlist_obj["playlist_id"])
         playlist = Playlist(
             item_id=playlist_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=playlist_obj["name"],
             provider_mappings={
                 ProviderMapping(
index d6c123bedf2f3fa54ac3b87d4c09fa907f47898b..ddb976657785341d75b3693539a6ea5720b5b426 100644 (file)
@@ -188,7 +188,7 @@ class ITunesPodcastsProvider(MusicProvider):
                 name=result.track_name,
                 item_id=result.feed_url,
                 publisher=result.artist_name,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 provider_mappings={
                     ProviderMapping(
                         item_id=result.feed_url,
@@ -207,7 +207,7 @@ class ITunesPodcastsProvider(MusicProvider):
                 if artwork_url is not None:
                     image_list.append(
                         MediaItemImage(
-                            type=ImageType.THUMB, path=artwork_url, provider=self.lookup_key
+                            type=ImageType.THUMB, path=artwork_url, provider=self.instance_id
                         )
                     )
             podcast.metadata.images = UniqueList(image_list)
@@ -221,9 +221,8 @@ class ITunesPodcastsProvider(MusicProvider):
         return parse_podcast(
             feed_url=prov_podcast_id,
             parsed_feed=parsed,
-            lookup_key=self.lookup_key,
-            domain=self.domain,
             instance_id=self.instance_id,
+            domain=self.domain,
         )
 
     async def get_podcast_episodes(
@@ -240,7 +239,6 @@ class ITunesPodcastsProvider(MusicProvider):
                 episode_cnt=cnt,
                 podcast_cover=podcast_cover,
                 domain=self.domain,
-                lookup_key=self.lookup_key,
                 instance_id=self.instance_id,
             ):
                 yield mass_episode
@@ -269,7 +267,7 @@ class ITunesPodcastsProvider(MusicProvider):
                 icon="mdi-trending-up",
                 # translation_key=shelf.id_,
                 items=UniqueList(podcast_list),
-                provider=self.lookup_key,
+                provider=self.instance_id,
             )
         ]
 
@@ -297,7 +295,7 @@ class ITunesPodcastsProvider(MusicProvider):
         if stream_url is None:
             raise MediaNotFoundError
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=item_id,
             audio_format=AudioFormat(
                 content_type=ContentType.try_parse(stream_url),
index fb84d13d18a7452675ee80d0d15b2cacaecfd305..f417412bc67ad44a765c56df1ecdef60acf37868 100644 (file)
@@ -364,7 +364,7 @@ class JellyfinProvider(MusicProvider):
             artist = Artist(
                 item_id=UNKNOWN_ARTIST_MAPPING.item_id,
                 name=UNKNOWN_ARTIST_MAPPING.name,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 provider_mappings={
                     ProviderMapping(
                         item_id=UNKNOWN_ARTIST_MAPPING.item_id,
@@ -450,7 +450,7 @@ class JellyfinProvider(MusicProvider):
         )
         return StreamDetails(
             item_id=jellyfin_track[ITEM_KEY_ID],
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=audio_format(jellyfin_track),
             stream_type=StreamType.HTTP,
             duration=int(
index d0270837889cd51048f048c482730815004bb005..d5037477a13e3662cb2e98c739b573d241b43c0f 100644 (file)
@@ -116,7 +116,7 @@ class MusicCastPlayer(Player):
             self._attr_name = self.zone_device.zone_data.name
 
         # group
-        self._attr_can_group_with = {self.provider.lookup_key}
+        self._attr_can_group_with = {self.provider.instance_id}
 
         self._attr_available = True
 
index 632ae75c511c261c6886e1ef90ab7db08ac29148..a16440cc4a3843f01f959facaecb276f4be3f7a1 100644 (file)
@@ -96,7 +96,7 @@ class MusicCastProvider(PlayerProvider):
 
     async def unload(self, is_removed: bool = False) -> None:
         """Call on unload."""
-        for mc_player in self.mass.players.all(provider_filter=self.lookup_key):
+        for mc_player in self.mass.players.all(provider_filter=self.instance_id):
             assert isinstance(mc_player, MusicCastPlayer)  # for type checking
             mc_player.physical_device.remove()
 
index c57622270160117b62a0f90779271094655fb88d..0a7bb553dd8c8bb99feace552745e228db532fea 100644 (file)
@@ -75,7 +75,7 @@ class NicovideoAlbumConverter(NicovideoConverterBase):
         # Create album with common structure
         album = Album(
             item_id=item_id,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             name=name,
             metadata=MediaItemMetadata(
                 description=description,
@@ -95,7 +95,7 @@ class NicovideoAlbumConverter(NicovideoConverterBase):
         if owner_id:
             owner_artist = Artist(
                 item_id=str(owner_id),
-                provider=self.provider.lookup_key,
+                provider=self.provider.instance_id,
                 name=owner_name if owner_name else "",
                 provider_mappings=self.helper.create_provider_mapping(
                     item_id=str(owner_id),
@@ -113,7 +113,7 @@ class NicovideoAlbumConverter(NicovideoConverterBase):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumbnail_url,
-                        provider=self.provider.lookup_key,
+                        provider=self.provider.instance_id,
                         remotely_accessible=True,
                     )
                 ]
index d86060eb4faa690c69e9d0222add84b6a8ed497a..b4ab552dda0061e6e9dcbc66c6ad1ccd87db320d 100644 (file)
@@ -44,7 +44,7 @@ class NicovideoArtistConverter(NicovideoConverterBase):
 
         artist = Artist(
             item_id=item_id,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             name=name,
             metadata=MediaItemMetadata(
                 description=owner_or_user.description
@@ -63,7 +63,7 @@ class NicovideoArtistConverter(NicovideoConverterBase):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=icon_url,
-                    provider=self.provider.lookup_key,
+                    provider=self.provider.instance_id,
                     remotely_accessible=True,
                 )
             )
index 99dedd7d3c7b5a610de7d9a8c29f9d288e42fa4c..33170ea2c68b65cf6691562ee18dc882fb705e07 100644 (file)
@@ -30,7 +30,7 @@ class NicovideoPlaylistConverter(NicovideoConverterBase):
         """Convert a nicovideo UserMylistItem into a Playlist."""
         playlist = Playlist(
             item_id=str(mylist.id_),
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             name=(mylist.title if isinstance(mylist, EssentialMylist) else mylist.name),
             owner=mylist.owner.id_ or "",
             is_editable=True,  # Own mylists are editable by default
@@ -53,7 +53,7 @@ class NicovideoPlaylistConverter(NicovideoConverterBase):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=mylist.owner.icon_url,
-                    provider=self.provider.lookup_key,
+                    provider=self.provider.instance_id,
                     remotely_accessible=True,
                 )
             )
index 4dc4b1e644944b15b59e8b1ba5b4c2430ee0752d..c67acdb1f0bd85d02b824faf9893bcc8d9d4e242 100644 (file)
@@ -59,7 +59,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
         # Create track with available information
         return Track(
             item_id=content.id_,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             name=content.title,
             duration=content.video.duration,
             artists=artists_list,
@@ -103,7 +103,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
         # Create base track with enhanced metadata
         return Track(
             item_id=video.id_,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             name=video.title,
             duration=video.duration,
             artists=artists_list,
@@ -173,7 +173,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
         # Create base track with enhanced metadata
         track = Track(
             item_id=video.id_,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             name=video.title,
             duration=video.duration,
             artists=artists_list,
@@ -293,7 +293,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=url,
-                    provider=self.provider.lookup_key,
+                    provider=self.provider.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -354,7 +354,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumbnail_url,
-                        provider=self.provider.lookup_key,
+                        provider=self.provider.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -380,7 +380,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=thumbnail.nhd_url,
-                    provider=self.provider.lookup_key,
+                    provider=self.provider.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -391,7 +391,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=thumbnail.large_url,
-                    provider=self.provider.lookup_key,
+                    provider=self.provider.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -403,7 +403,7 @@ class NicovideoTrackConverter(NicovideoConverterBase):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=thumbnail.middle_url,
-                    provider=self.provider.lookup_key,
+                    provider=self.provider.instance_id,
                     remotely_accessible=True,
                 )
             )
index ff2ff25c18c914ae82950990bd5ce33ec3c0fcb5..8e00550a3299cd8b8d3510a35351ba8451dd6587 100644 (file)
@@ -67,7 +67,7 @@ class NicovideoMusicProviderExplorerMixin(NicovideoMusicProviderMixinBase):
                 RecommendationFolder(
                     item_id="nicovideo_recommendations",
                     name="nicovideo recommendations",
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     icon="mdi-star-circle-outline",
                     items=UniqueList(main_recommendation_tracks),
                 )
@@ -80,7 +80,7 @@ class NicovideoMusicProviderExplorerMixin(NicovideoMusicProviderMixinBase):
                 RecommendationFolder(
                     item_id="nicovideo_history",
                     name="Recently watched (nicovideo history)",
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     icon="mdi-history",
                     items=UniqueList(history_tracks),
                 )
@@ -95,7 +95,7 @@ class NicovideoMusicProviderExplorerMixin(NicovideoMusicProviderMixinBase):
                 RecommendationFolder(
                     item_id="nicovideo_following_activities",
                     name="New Tracks from Followed Users",
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     icon="mdi-account-plus-outline",
                     items=UniqueList(following_activities_tracks),
                 )
@@ -108,7 +108,7 @@ class NicovideoMusicProviderExplorerMixin(NicovideoMusicProviderMixinBase):
                 RecommendationFolder(
                     item_id="nicovideo_like_history",
                     name="Recently liked (Like history)",
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     icon="mdi-heart-outline",
                     items=UniqueList(like_history_tracks),
                 )
index 6969985ecc20cd10134becb891efbed93a78526c..380a8d34089cebadd64ac048a244d70ada9543f0 100644 (file)
@@ -201,7 +201,7 @@ class NugsProvider(MusicProvider):
         stream_url = await self._get_stream_url(item_id)
         return StreamDetails(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(
                 content_type=ContentType.UNKNOWN,
             ),
@@ -219,17 +219,17 @@ class NugsProvider(MusicProvider):
         popular_folder = RecommendationFolder(
             name="Most Popular",
             item_id="nugs_popular_shows",
-            provider=self.lookup_key,
+            provider=self.instance_id,
         )
         recommended_folder = RecommendationFolder(
             name="Recommended Shows",
             item_id="nugs_recommended_shows",
-            provider=self.lookup_key,
+            provider=self.instance_id,
         )
         recent_folder = RecommendationFolder(
             name="Recent Shows",
             item_id="nugs_recent_shows",
-            provider=self.lookup_key,
+            provider=self.instance_id,
         )
         popular_data = await self._get_data("catalog", popular, limit=20)
         for item in popular_data["items"]:
@@ -255,7 +255,7 @@ class NugsProvider(MusicProvider):
         artist_name = artist_obj.get("artistName") or artist_obj.get("name")
         artist = Artist(
             item_id=str(artist_id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=str(artist_name),
             provider_mappings={
                 ProviderMapping(
@@ -271,7 +271,7 @@ class NugsProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=artist_obj["avatarImage"]["url"],
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -283,7 +283,7 @@ class NugsProvider(MusicProvider):
         title = album_obj.get("title") or album_obj.get("containerInfo")
         album = Album(
             item_id=str(item_id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=str(title),
             # version=album_obj["type"],
             provider_mappings={
@@ -312,7 +312,7 @@ class NugsProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=path,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -335,7 +335,7 @@ class NugsProvider(MusicProvider):
         """Parse nugs playlist object to generic layout."""
         return Playlist(
             item_id=playlist_obj["id"],
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=playlist_obj["name"],
             provider_mappings={
                 ProviderMapping(
@@ -350,7 +350,7 @@ class NugsProvider(MusicProvider):
                         MediaItemImage(
                             type=ImageType.THUMB,
                             path=playlist_obj["imageUrl"],
-                            provider=self.lookup_key,
+                            provider=self.instance_id,
                             remotely_accessible=True,
                         )
                     ]
@@ -374,7 +374,7 @@ class NugsProvider(MusicProvider):
 
         track = Track(
             item_id=str(track_id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=str(track_name),
             provider_mappings={
                 ProviderMapping(
@@ -411,7 +411,7 @@ class NugsProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=image_url,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -458,7 +458,7 @@ class NugsProvider(MusicProvider):
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=name,
         )
 
index dbbe659171c2fc4a2463f26474c2316aff9e2aa4..68b877d5e955dd8b4b6337f6ea111c16e044c067 100644 (file)
@@ -544,14 +544,14 @@ class PlexProvider(MusicProvider):
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=mapped_name,
             version=mapped_version,
         )
 
     async def _get_or_create_artist_by_name(self, artist_name: str) -> Artist | ItemMapping:
         if library_items := await self.mass.music.artists._get_library_items_by_query(
-            search=artist_name, provider_filter=[self.lookup_key]
+            search=artist_name, provider_filter=[self.instance_id]
         ):
             return ItemMapping.from_item(library_items[0])
 
@@ -559,7 +559,7 @@ class PlexProvider(MusicProvider):
         return Artist(
             item_id=artist_id,
             name=artist_name or UNKNOWN_ARTIST,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             provider_mappings={
                 ProviderMapping(
                     item_id=str(artist_id),
@@ -651,7 +651,7 @@ class PlexProvider(MusicProvider):
         album_id = plex_album.key
         album = Album(
             item_id=album_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=plex_album.title or "[Unknown]",
             provider_mappings={
                 ProviderMapping(
@@ -676,7 +676,7 @@ class PlexProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumb,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=False,
                     )
                 ]
@@ -702,7 +702,7 @@ class PlexProvider(MusicProvider):
         artist = Artist(
             item_id=artist_id,
             name=plex_artist.title or UNKNOWN_ARTIST,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             provider_mappings={
                 ProviderMapping(
                     item_id=str(artist_id),
@@ -720,7 +720,7 @@ class PlexProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumb,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=False,
                     )
                 ]
@@ -731,7 +731,7 @@ class PlexProvider(MusicProvider):
         """Parse a Plex Playlist response to a Playlist object."""
         playlist = Playlist(
             item_id=plex_playlist.key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=plex_playlist.title or "[Unknown]",
             provider_mappings={
                 ProviderMapping(
@@ -750,7 +750,7 @@ class PlexProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumb,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=False,
                     )
                 ]
@@ -766,7 +766,7 @@ class PlexProvider(MusicProvider):
         # Collections are imported as playlists with the configured prefix
         playlist = Playlist(
             item_id=f"collection:{plex_collection.key}",
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"{collection_prefix}{plex_collection.title}",
             provider_mappings={
                 ProviderMapping(
@@ -783,7 +783,7 @@ class PlexProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumb,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=False,
                     )
                 ]
@@ -805,7 +805,7 @@ class PlexProvider(MusicProvider):
             content = None
         track = Track(
             item_id=plex_track.key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=plex_track.title or "[Unknown]",
             provider_mappings={
                 ProviderMapping(
@@ -855,7 +855,7 @@ class PlexProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=thumb,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=False,
                     )
                 ]
@@ -1174,7 +1174,7 @@ class PlexProvider(MusicProvider):
                 folder = RecommendationFolder(
                     name=hub.title,
                     item_id=f"{self.instance_id}_{hub.hubIdentifier}",
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     icon="mdi-music",
                 )
 
@@ -1266,7 +1266,7 @@ class PlexProvider(MusicProvider):
 
         stream_details = StreamDetails(
             item_id=plex_track.key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(
                 content_type=content_type,
                 channels=media.audioChannels,
index 858fa8d188320a5c945e011578a652a6fdae193e..4c254d64dd7c3693906a55838683fc6dd5a28b69 100644 (file)
@@ -81,7 +81,7 @@ async def make_api_request(
 
 
 def parse_podcast_from_feed(
-    feed_data: dict[str, Any], lookup_key: str, domain: str, instance_id: str
+    feed_data: dict[str, Any], instance_id: str, domain: str
 ) -> Podcast | None:
     """Parse podcast from API feed data."""
     feed_url = feed_data.get("url")
@@ -94,7 +94,7 @@ def parse_podcast_from_feed(
         item_id=str(podcast_id),
         name=feed_data.get("title", "Unknown Podcast"),
         publisher=feed_data.get("author") or feed_data.get("ownerName", "Unknown"),
-        provider=lookup_key,
+        provider=instance_id,
         provider_mappings={
             ProviderMapping(
                 item_id=str(podcast_id),
@@ -121,7 +121,7 @@ def parse_podcast_from_feed(
             MediaItemImage(
                 type=ImageType.THUMB,
                 path=image_url,
-                provider=lookup_key,
+                provider=instance_id,
                 remotely_accessible=True,
             )
         )
@@ -143,9 +143,8 @@ def parse_episode_from_data(
     episode_data: dict[str, Any],
     podcast_id: str,
     episode_idx: int,
-    lookup_key: str,
-    domain: str,
     instance_id: str,
+    domain: str,
     podcast_name: str | None = None,
 ) -> PodcastEpisode | None:
     """Parse episode from API episode data."""
@@ -170,13 +169,13 @@ def parse_episode_from_data(
 
     episode = PodcastEpisode(
         item_id=episode_id,
-        provider=lookup_key,
+        provider=instance_id,
         name=episode_data.get("title", "Unknown Episode"),
         duration=duration,
         position=position,
         podcast=ItemMapping(
             item_id=podcast_id,
-            provider=lookup_key,
+            provider=instance_id,
             name=podcast_name,
             media_type=MediaType.PODCAST,
         ),
@@ -210,7 +209,7 @@ def parse_episode_from_data(
             MediaItemImage(
                 type=ImageType.THUMB,
                 path=image_url,
-                provider=lookup_key,
+                provider=instance_id,
                 remotely_accessible=True,
             )
         )
index 120bf00a2b7d6c3c2d1750f8b848ea0d654b5049..413b3ef4c10e6f30e6b7ba0c78fd5ef2d0943c2f 100644 (file)
@@ -86,9 +86,7 @@ class PodcastIndexProvider(MusicProvider):
 
         podcasts = []
         for feed_data in response.get("feeds", []):
-            podcast = parse_podcast_from_feed(
-                feed_data, self.lookup_key, self.domain, self.instance_id
-            )
+            podcast = parse_podcast_from_feed(feed_data, self.instance_id, self.domain)
             if podcast:
                 podcasts.append(podcast)
 
@@ -218,9 +216,7 @@ class PodcastIndexProvider(MusicProvider):
             # Try by ID first
             response = await self._api_request("podcasts/byfeedid", params={"id": prov_podcast_id})
             if response.get("feed"):
-                podcast = parse_podcast_from_feed(
-                    response["feed"], self.lookup_key, self.domain, self.instance_id
-                )
+                podcast = parse_podcast_from_feed(response["feed"], self.instance_id, self.domain)
                 if podcast:
                     return podcast
         except (ProviderUnavailableError, InvalidDataError):
@@ -272,9 +268,8 @@ class PodcastIndexProvider(MusicProvider):
                     episode_data,
                     prov_podcast_id,
                     idx,
-                    self.lookup_key,
-                    self.domain,
                     self.instance_id,
+                    self.domain,
                     podcast_name,
                 )
                 if episode:
@@ -303,7 +298,7 @@ class PodcastIndexProvider(MusicProvider):
 
             if episode_data:
                 episode = parse_episode_from_data(
-                    episode_data, podcast_id, 0, self.lookup_key, self.domain, self.instance_id
+                    episode_data, podcast_id, 0, self.instance_id, self.domain
                 )
                 if episode:
                     return episode
@@ -341,7 +336,7 @@ class PodcastIndexProvider(MusicProvider):
                 stream_url = episode_data.get("enclosureUrl")
                 if stream_url:
                     return StreamDetails(
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         item_id=item_id,
                         audio_format=AudioFormat(
                             content_type=ContentType.try_parse(
@@ -381,9 +376,7 @@ class PodcastIndexProvider(MusicProvider):
         response = await self._api_request(endpoint, params)
         podcasts = []
         for feed_data in response.get("feeds", []):
-            podcast = parse_podcast_from_feed(
-                feed_data, self.lookup_key, self.domain, self.instance_id
-            )
+            podcast = parse_podcast_from_feed(feed_data, self.instance_id, self.domain)
             if podcast:
                 podcasts.append(podcast)
         return podcasts
@@ -444,9 +437,8 @@ class PodcastIndexProvider(MusicProvider):
                     episode_data,
                     podcast_id,
                     idx,
-                    self.lookup_key,
-                    self.domain,
                     self.instance_id,
+                    self.domain,
                     podcast_name,
                 )
                 if episode:
@@ -504,9 +496,7 @@ class PodcastIndexProvider(MusicProvider):
 
             podcasts = []
             for feed_data in search_response.get("feeds", []):
-                podcast = parse_podcast_from_feed(
-                    feed_data, self.lookup_key, self.domain, self.instance_id
-                )
+                podcast = parse_podcast_from_feed(feed_data, self.instance_id, self.domain)
                 if podcast:
                     podcasts.append(podcast)
 
index 5e82534b08508ee8c9b59ff6eec732f61580a8af..039590d7c100218202db6fac44f505cf61c6cc70 100644 (file)
@@ -177,7 +177,7 @@ class PodcastMusicprovider(MusicProvider):
             if item_id == episode["guid"]:
                 stream_url = episode["enclosures"][0]["url"]
                 return StreamDetails(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     item_id=item_id,
                     audio_format=AudioFormat(
                         content_type=ContentType.try_parse(stream_url),
@@ -200,9 +200,8 @@ class PodcastMusicprovider(MusicProvider):
         return parse_podcast(
             feed_url=self.feed_url,
             parsed_feed=self.parsed_podcast,
-            lookup_key=self.lookup_key,
-            domain=self.domain,
             instance_id=self.instance_id,
+            domain=self.domain,
             mass_item_id=self.podcast_id,
         )
 
@@ -214,9 +213,8 @@ class PodcastMusicprovider(MusicProvider):
             prov_podcast_id=self.podcast_id,
             episode_cnt=fallback_position,
             podcast_cover=self.parsed_podcast.get("cover_url"),
-            lookup_key=self.lookup_key,
-            domain=self.domain,
             instance_id=self.instance_id,
+            domain=self.domain,
             mass_item_id=episode_obj["guid"],
         )
         # Override remotely_accessible as these providers can have unreliable image URLs
index 80a97d5e136c3575b049635f2d91a17008ff094b..c701f82c2c5af000702d4cb225c436c1c380a126 100644 (file)
@@ -467,7 +467,7 @@ class QobuzProvider(MusicProvider):
         self.mass.create_task(self._report_playback_started(streamdata))
         return StreamDetails(
             item_id=str(item_id),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(
                 content_type=content_type,
                 sample_rate=int(streamdata["sampling_rate"] * 1000),
@@ -551,7 +551,7 @@ class QobuzProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=img,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -614,7 +614,7 @@ class QobuzProvider(MusicProvider):
         if img := self.__get_image(album_obj):
             album.metadata.add_image(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.THUMB,
                     path=img,
                     remotely_accessible=True,
@@ -710,7 +710,7 @@ class QobuzProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=img,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
@@ -728,7 +728,7 @@ class QobuzProvider(MusicProvider):
         )
         playlist = Playlist(
             item_id=str(playlist_obj["id"]),
-            provider=self.instance_id if is_editable else self.lookup_key,
+            provider=self.instance_id,
             name=playlist_obj["name"],
             owner=playlist_obj["owner"]["name"],
             provider_mappings={
@@ -746,7 +746,7 @@ class QobuzProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=img,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
index 44d9bd3034a36b9ef433eb46977e0cd26c279f3e..da22a34cd6a54ed13c58306eeb9e582849a7ded3 100644 (file)
@@ -333,7 +333,7 @@ class RadioBrowserProvider(MusicProvider):
                 folder.image = MediaItemImage(
                     type=ImageType.THUMB,
                     path=country.favicon,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             items.append(folder)
@@ -471,7 +471,7 @@ class RadioBrowserProvider(MusicProvider):
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=radio_obj.favicon,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             ]
index 8f04b7d9ea45040f370e7ea9793a5d031ba70e91..062b7ba8999f8bdf69037eec837d7ffee09c3cb3 100644 (file)
@@ -14,14 +14,12 @@ from .constants import RADIO_PARADISE_CHANNELS, STATION_ICONS_BASE_URL
 from .helpers import enhance_title_with_upcoming  # noqa: F401
 
 
-def parse_radio(
-    channel_id: str, provider_lookup_key: str, provider_domain: str, instance_id: str
-) -> Radio:
+def parse_radio(channel_id: str, instance_id: str, provider_domain: str) -> Radio:
     """Create a Radio object from cached channel information."""
     channel_info = RADIO_PARADISE_CHANNELS.get(channel_id, {})
 
     radio = Radio(
-        provider=provider_lookup_key,
+        provider=instance_id,
         item_id=channel_id,
         name=channel_info.get("name", "Unknown Radio"),
         provider_mappings={
@@ -40,7 +38,7 @@ def parse_radio(
         icon_url = f"{STATION_ICONS_BASE_URL}/{station_icon}"
         radio.metadata.add_image(
             MediaItemImage(
-                provider=provider_lookup_key,
+                provider=instance_id,
                 type=ImageType.THUMB,
                 path=icon_url,
                 remotely_accessible=True,
index d4ffb09f2e33a57d45509d877d30e3da29b87e90..6b06587bcf753bc00ffa84e05341b0565264309d 100644 (file)
@@ -65,7 +65,7 @@ class RadioParadiseProvider(MusicProvider):
 
         stream_details = StreamDetails(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(
                 content_type=content_type,
                 channels=2,
@@ -112,7 +112,7 @@ class RadioParadiseProvider(MusicProvider):
 
     def _parse_radio(self, channel_id: str) -> Radio:
         """Create a Radio object from cached channel information."""
-        return parsers.parse_radio(channel_id, self.lookup_key, self.domain, self.instance_id)
+        return parsers.parse_radio(channel_id, self.instance_id, self.domain)
 
     async def _get_channel_metadata(self, channel_id: str) -> dict[str, Any] | None:
         """Get current track and upcoming tracks from Radio Paradise's block API.
index 0d100a04a9fa665bfd459cfc2cebdde105c96572..eb0e3bb7374775f003ab2ec1c808c3441ec5b271 100644 (file)
@@ -205,7 +205,7 @@ class SendspinPlayer(Player):
             PlayerFeature.VOLUME_SET,
             PlayerFeature.VOLUME_MUTE,
         }
-        self._attr_can_group_with = {provider.lookup_key}
+        self._attr_can_group_with = {provider.instance_id}
         self._attr_power_control = PLAYER_CONTROL_NONE
         self._attr_device_info = DeviceInfo()
         if player_client := sendspin_client.player:
index 028c537d221ea06e428fd4799ccc86366d10264f..e53c0b8e0cf5376a7a22c40a8bda904e5c44a9bb 100644 (file)
@@ -233,7 +233,7 @@ class SiriusXMProvider(MusicProvider):
         # See `_channel_updated` for where this is handled.
         self._current_stream_details = StreamDetails(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             audio_format=AudioFormat(
                 content_type=ContentType.AAC,
             ),
@@ -292,7 +292,7 @@ class SiriusXMProvider(MusicProvider):
 
     def _parse_radio(self, channel: XMChannel) -> Radio:
         radio = Radio(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=channel.id,
             name=channel.name,
             provider_mappings={
@@ -314,7 +314,7 @@ class SiriusXMProvider(MusicProvider):
         if icon is not None:
             images.append(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.THUMB,
                     path=icon,
                     remotely_accessible=True,
@@ -322,7 +322,7 @@ class SiriusXMProvider(MusicProvider):
             )
             images.append(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.LOGO,
                     path=icon,
                     remotely_accessible=True,
@@ -332,7 +332,7 @@ class SiriusXMProvider(MusicProvider):
         if banner is not None:
             images.append(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.BANNER,
                     path=banner,
                     remotely_accessible=True,
@@ -340,7 +340,7 @@ class SiriusXMProvider(MusicProvider):
             )
             images.append(
                 MediaItemImage(
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     type=ImageType.LANDSCAPE,
                     path=banner,
                     remotely_accessible=True,
index 71533846f76e707b65fe15c4bb9f5ecfccd44027..dd7e2974707117f9bfa0492f7336b3758d916fcf 100644 (file)
@@ -83,7 +83,7 @@ class SnapCastPlayer(Player):
             PlayerFeature.VOLUME_MUTE,
             PlayerFeature.PLAY_ANNOUNCEMENT,
         }
-        self._attr_can_group_with = {self.provider.lookup_key}
+        self._attr_can_group_with = {self.provider.instance_id}
 
     async def volume_set(self, volume_level: int) -> None:
         """Send VOLUME_SET command to given player."""
index b57629d4096b62bf63e90f953b9c766b37603404..8f6f7efea999ca404f4fa125518578f0e0046da6 100644 (file)
@@ -154,7 +154,7 @@ class SonosPlayer(Player):
         )
         self._attr_device_info.model = self.discovery_info["device"]["modelDisplayName"]
         self._attr_device_info.manufacturer = self._provider.manifest.name
-        self._attr_can_group_with = {self._provider.lookup_key}
+        self._attr_can_group_with = {self._provider.instance_id}
 
         if SonosCapability.LINE_IN in self.discovery_info["device"]["capabilities"]:
             self._attr_source_list.append(PLAYER_SOURCE_MAP[SOURCE_LINE_IN])
@@ -572,7 +572,7 @@ class SonosPlayer(Player):
                     if x.player_id != airplay_player.player_id
                 )
             else:
-                self._attr_can_group_with = {self._provider.lookup_key}
+                self._attr_can_group_with = {self._provider.instance_id}
         else:
             # player is group child (synced to another player)
             group_parent: SonosPlayer = self.mass.players.get(
index 12c11661ca37a9e29eeaca6ed6d1f18532e1d93d..1d4137aec90485d6c4328fccb8478ea48515177e 100644 (file)
@@ -85,7 +85,7 @@ class SonosPlayer(Player):
         self._attr_needs_poll = True
         self._attr_poll_interval = 5
         self._attr_available = True
-        self._attr_can_group_with = {provider.lookup_key}
+        self._attr_can_group_with = {provider.instance_id}
 
         # Subscriptions and events
         self._subscriptions: list[SubscriptionBase] = []
@@ -708,7 +708,7 @@ class SonosPlayer(Player):
         except TimeoutError:
             self.logger.warning("Timeout waiting for target groups %s", groups)
 
-        if players := self.mass.players.all(provider_filter=_provider.lookup_key):
+        if players := self.mass.players.all(provider_filter=_provider.instance_id):
             any_speaker = cast("SonosPlayer", players[0])
             any_speaker.soco.zone_group_state.clear_cache()
 
index 91d81c2a3b2605400db898e5258c0b17f207ebb3..37011b59b76ca65de8361f914650ce2401915152 100644 (file)
@@ -57,7 +57,7 @@ class SonosPlayerProvider(PlayerProvider):
         while self._discovery_running:
             await asyncio.sleep(0.5)
         # Clean up subscriptions and connections
-        for sonos_player in self.mass.players.all(provider_filter=self.lookup_key):
+        for sonos_player in self.mass.players.all(provider_filter=self.instance_id):
             sonos_player = cast("SonosPlayer", sonos_player)
             await sonos_player.offline()
         # Stop the async event listener
index 41323299366abde68fbb022f60e555196cf43f1a..955d175580dfc555fee38839f47483cfa4df5604 100644 (file)
@@ -233,7 +233,7 @@ class SoundcloudMusicProvider(MusicProvider):
             folder = RecommendationFolder(
                 name=collection["title"],
                 item_id=f"{self.instance_id}_{collection['id']}",
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 icon="mdi-playlist-music",
             )
             for playlist in collection.get("items").get("collection", []):
@@ -252,7 +252,7 @@ class SoundcloudMusicProvider(MusicProvider):
             folder = RecommendationFolder(
                 name="SoundCloud Feed",
                 item_id=f"{self.instance_id}_sc_subscribed_feed",
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 icon="mdi-rss",
             )
             for item in feed["collection"]:
@@ -372,7 +372,7 @@ class SoundcloudMusicProvider(MusicProvider):
         """Return the content details for the given track when it will be streamed."""
         url: str = await self._soundcloud.get_stream_url(track_id=item_id, presets=["mp3"])
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=item_id,
             # let ffmpeg work out the details itself as
             # soundcloud uses a mix of different content types and streaming methods
@@ -419,7 +419,7 @@ class SoundcloudMusicProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=img_url,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -453,7 +453,7 @@ class SoundcloudMusicProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=self._transform_artwork_url(playlist_obj["artwork_url"]),
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -499,7 +499,7 @@ class SoundcloudMusicProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=self._transform_artwork_url(track_obj["artwork_url"]),
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
index 81720fc87e88234be1cfc0d84a86f09661d028aa..626813d9edbc8b78612411d051d166e6c9421a1a 100644 (file)
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
 
 
 def parse_images(
-    images_list: list[dict[str, Any]], lookup_key: str, exclude_generic: bool = False
+    images_list: list[dict[str, Any]], instance_id: str, exclude_generic: bool = False
 ) -> UniqueList[MediaItemImage]:
     """Parse images list into MediaItemImage objects."""
     if not images_list:
@@ -54,7 +54,7 @@ def parse_images(
             MediaItemImage(
                 type=ImageType.THUMB,
                 path=best_image["url"],
-                provider=lookup_key,
+                provider=instance_id,
                 remotely_accessible=True,
             )
         ]
@@ -65,7 +65,7 @@ def parse_artist(artist_obj: dict[str, Any], provider: SpotifyProvider) -> Artis
     """Parse spotify artist object to generic layout."""
     artist = Artist(
         item_id=artist_obj["id"],
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=artist_obj["name"] or artist_obj["id"],
         provider_mappings={
             ProviderMapping(
@@ -81,7 +81,7 @@ def parse_artist(artist_obj: dict[str, Any], provider: SpotifyProvider) -> Artis
 
     # Use unified image parsing with generic exclusion
     artist.metadata.images = parse_images(
-        artist_obj.get("images", []), provider.lookup_key, exclude_generic=True
+        artist_obj.get("images", []), provider.instance_id, exclude_generic=True
     )
     return artist
 
@@ -91,7 +91,7 @@ def parse_album(album_obj: dict[str, Any], provider: SpotifyProvider) -> Album:
     name, version = parse_title_and_version(album_obj["name"])
     album = Album(
         item_id=album_obj["id"],
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=name,
         version=version,
         provider_mappings={
@@ -125,7 +125,7 @@ def parse_album(album_obj: dict[str, Any], provider: SpotifyProvider) -> Album:
     if "genres" in album_obj:
         album.metadata.genres = set(album_obj["genres"])
 
-    album.metadata.images = parse_images(album_obj.get("images", []), provider.lookup_key)
+    album.metadata.images = parse_images(album_obj.get("images", []), provider.instance_id)
 
     if "label" in album_obj:
         album.metadata.label = album_obj["label"]
@@ -147,7 +147,7 @@ def parse_track(
     name, version = parse_title_and_version(track_obj["name"])
     track = Track(
         item_id=track_obj["id"],
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=name,
         version=version,
         duration=track_obj["duration_ms"] / 1000,
@@ -182,7 +182,7 @@ def parse_track(
     if "album" in track_obj:
         track.album = parse_album(track_obj["album"], provider)
         track.metadata.images = parse_images(
-            track_obj["album"].get("images", []), provider.lookup_key
+            track_obj["album"].get("images", []), provider.instance_id
         )
     if track_obj.get("copyright"):
         track.metadata.copyright = track_obj["copyright"]
@@ -206,7 +206,7 @@ def parse_playlist(playlist_obj: dict[str, Any], provider: SpotifyProvider) -> P
 
     playlist = Playlist(
         item_id=playlist_obj["id"],
-        provider=provider.instance_id if is_editable else provider.lookup_key,
+        provider=provider.instance_id,
         name=playlist_obj["name"],
         owner=owner_name,
         provider_mappings={
@@ -220,7 +220,7 @@ def parse_playlist(playlist_obj: dict[str, Any], provider: SpotifyProvider) -> P
         is_editable=is_editable,
     )
 
-    playlist.metadata.images = parse_images(playlist_obj.get("images", []), provider.lookup_key)
+    playlist.metadata.images = parse_images(playlist_obj.get("images", []), provider.instance_id)
     return playlist
 
 
@@ -228,7 +228,7 @@ def parse_podcast(podcast_obj: dict[str, Any], provider: SpotifyProvider) -> Pod
     """Parse spotify podcast (show) object to generic layout."""
     podcast = Podcast(
         item_id=podcast_obj["id"],
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=podcast_obj["name"],
         provider_mappings={
             ProviderMapping(
@@ -246,7 +246,7 @@ def parse_podcast(podcast_obj: dict[str, Any], provider: SpotifyProvider) -> Pod
     if podcast_obj.get("description"):
         podcast.metadata.description = podcast_obj["description"]
 
-    podcast.metadata.images = parse_images(podcast_obj.get("images", []), provider.lookup_key)
+    podcast.metadata.images = parse_images(podcast_obj.get("images", []), provider.instance_id)
 
     if "explicit" in podcast_obj:
         podcast.metadata.explicit = podcast_obj["explicit"]
@@ -266,7 +266,7 @@ def parse_podcast_episode(
     if podcast is None and "show" in episode_obj:
         podcast = Podcast(
             item_id=episode_obj["show"]["id"],
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=episode_obj["show"]["name"],
             provider_mappings={
                 ProviderMapping(
@@ -281,14 +281,14 @@ def parse_podcast_episode(
         # Create a minimal podcast reference if none available
         podcast = Podcast(
             item_id="unknown",
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name="Unknown Podcast",
             provider_mappings=set(),
         )
 
     episode = PodcastEpisode(
         item_id=episode_obj["id"],
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=episode_obj["name"],
         duration=episode_obj["duration_ms"] // 1000 if episode_obj.get("duration_ms") else 0,
         podcast=podcast,
@@ -322,7 +322,7 @@ def parse_podcast_episode(
 
             episode.metadata.release_date = datetime.fromisoformat(date_str)
 
-    episode.metadata.images = parse_images(episode_obj.get("images", []), provider.lookup_key)
+    episode.metadata.images = parse_images(episode_obj.get("images", []), provider.instance_id)
 
     # Use podcast artwork if episode has none
     if not episode.metadata.images and isinstance(podcast, Podcast) and podcast.metadata.images:
@@ -385,7 +385,7 @@ def parse_audiobook(audiobook_obj: dict[str, Any], provider: SpotifyProvider) ->
     if audiobook_obj.get("publisher"):
         audiobook.publisher = audiobook_obj["publisher"]
 
-    audiobook.metadata.images = parse_images(audiobook_obj.get("images", []), provider.lookup_key)
+    audiobook.metadata.images = parse_images(audiobook_obj.get("images", []), provider.instance_id)
 
     if audiobook_obj.get("explicit"):
         audiobook.metadata.explicit = audiobook_obj["explicit"]
index 5b6f887ddf5e94c0c64ffae0cd651f8523405002..631f49351fc3b7bef25b88120cedde5620a68e75 100644 (file)
@@ -695,7 +695,7 @@ class SpotifyProvider(MusicProvider):
         # For all other media types (tracks, podcast episodes)
         return StreamDetails(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             media_type=media_type,
             audio_format=AudioFormat(content_type=ContentType.OGG, bit_rate=320),
             stream_type=StreamType.CUSTOM,
@@ -845,7 +845,7 @@ class SpotifyProvider(MusicProvider):
 
         liked_songs = Playlist(
             item_id=self._get_liked_songs_playlist_id(),
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Liked Songs {self._sp_user['display_name']}",  # TODO to be translated
             owner=self._sp_user["display_name"],
             provider_mappings={
@@ -864,7 +864,7 @@ class SpotifyProvider(MusicProvider):
         image = MediaItemImage(
             type=ImageType.THUMB,
             path="https://misc.scdn.co/liked-songs/liked-songs-64.png",
-            provider=self.lookup_key,
+            provider=self.instance_id,
             remotely_accessible=True,
         )
         if liked_songs.metadata.images is None:
index c1a5f6f39e8f961919a11694aa61052102936d97..1419bfdd52ce342100d8d30dc8501ed8af4ec370 100644 (file)
@@ -93,7 +93,7 @@ class SqueezelitePlayer(Player):
             PlayerFeature.GAPLESS_PLAYBACK,
             PlayerFeature.GAPLESS_DIFFERENT_SAMPLERATE,
         }
-        self._attr_can_group_with = {provider.lookup_key}
+        self._attr_can_group_with = {provider.instance_id}
         self.multi_client_stream: MultiClientStream | None = None
         self._sync_playpoints: deque[SyncPlayPoint] = deque(maxlen=MIN_REQ_PLAYPOINTS)
         self._do_not_resync_before: float = 0.0
index b006ad048d4e1605221b18449fea8d4996c2e22d..8703589d6fe9126084620103b7b7e4015530e1c2 100644 (file)
@@ -149,7 +149,7 @@ class TestProvider(MusicProvider):
         artist_idx, album_idx, track_idx = prov_track_id.split("_", 3)
         return Track(
             item_id=prov_track_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Test Track {artist_idx} - {album_idx} - {track_idx}",
             duration=60,
             artists=UniqueList([await self.get_artist(artist_idx)]),
@@ -170,7 +170,7 @@ class TestProvider(MusicProvider):
         """Get full artist details by id."""
         return Artist(
             item_id=prov_artist_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Test Artist {prov_artist_id}",
             metadata=MediaItemMetadata(images=UniqueList([DEFAULT_THUMB, DEFAULT_FANART])),
             provider_mappings={
@@ -187,7 +187,7 @@ class TestProvider(MusicProvider):
         artist_idx, album_idx = prov_album_id.split("_", 2)
         return Album(
             item_id=prov_album_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Test Album {album_idx}",
             artists=UniqueList([await self.get_artist(artist_idx)]),
             provider_mappings={
@@ -204,7 +204,7 @@ class TestProvider(MusicProvider):
         """Get full podcast details by id."""
         return Podcast(
             item_id=prov_podcast_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Test Podcast {prov_podcast_id}",
             metadata=MediaItemMetadata(images=UniqueList([DEFAULT_THUMB])),
             provider_mappings={
@@ -221,7 +221,7 @@ class TestProvider(MusicProvider):
         """Get full audiobook details by id."""
         return Audiobook(
             item_id=prov_audiobook_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Test Audiobook {prov_audiobook_id}",
             metadata=MediaItemMetadata(
                 images=UniqueList([DEFAULT_THUMB]),
@@ -305,12 +305,12 @@ class TestProvider(MusicProvider):
         podcast_id, episode_idx = prov_episode_id.split("_", 2)
         return PodcastEpisode(
             item_id=prov_episode_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=f"Test PodcastEpisode {podcast_id}-{episode_idx}",
             duration=60,
             podcast=ItemMapping(
                 item_id=podcast_id,
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 name=f"Test Podcast {podcast_id}",
                 media_type=MediaType.PODCAST,
                 image=DEFAULT_THUMB,
@@ -332,7 +332,7 @@ class TestProvider(MusicProvider):
     async def get_stream_details(self, item_id: str, media_type: MediaType) -> StreamDetails:
         """Get streamdetails for a track/radio."""
         return StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=item_id,
             audio_format=AudioFormat(
                 content_type=ContentType.OGG,
index 384c5f07c5c7f6274fa42a6a38d0f15621d5e67a..4fb37165d3ddbbbec32dec1c764c9cda11d820b7 100644 (file)
@@ -256,7 +256,7 @@ class AudioDbMetadataProvider(MetadataProvider):
                         MediaItemImage(
                             type=img_type,
                             path=img,
-                            provider=self.lookup_key,
+                            provider=self.instance_id,
                             remotely_accessible=True,
                         )
                     )
@@ -304,7 +304,7 @@ class AudioDbMetadataProvider(MetadataProvider):
                         MediaItemImage(
                             type=img_type,
                             path=img,
-                            provider=self.lookup_key,
+                            provider=self.instance_id,
                             remotely_accessible=True,
                         )
                     )
@@ -358,7 +358,7 @@ class AudioDbMetadataProvider(MetadataProvider):
                         MediaItemImage(
                             type=img_type,
                             path=img,
-                            provider=self.lookup_key,
+                            provider=self.instance_id,
                             remotely_accessible=True,
                         )
                     )
index 0a6cf51990d0f308a113865b5e53cb7e968d5ee5..142fb6c4cba0d132bda5c2eef700934725f92a52 100644 (file)
@@ -37,7 +37,7 @@ def parse_artist(provider: TidalProvider, artist_obj: dict[str, Any]) -> Artist:
     artist_id = str(artist_obj["id"])
     artist = Artist(
         item_id=artist_id,
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=artist_obj["name"],
         provider_mappings={
             ProviderMapping(
@@ -59,7 +59,7 @@ def parse_artist(provider: TidalProvider, artist_obj: dict[str, Any]) -> Artist:
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=image_url,
-                    provider=provider.lookup_key,
+                    provider=provider.instance_id,
                     remotely_accessible=True,
                 )
             ]
@@ -76,7 +76,7 @@ def parse_album(provider: TidalProvider, album_obj: dict[str, Any]) -> Album:
 
     album = Album(
         item_id=album_id,
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=name,
         version=version,
         provider_mappings={
@@ -147,7 +147,7 @@ def parse_album(provider: TidalProvider, album_obj: dict[str, Any]) -> Album:
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=image_url,
-                    provider=provider.lookup_key,
+                    provider=provider.instance_id,
                     remotely_accessible=True,
                 )
             ]
@@ -169,7 +169,7 @@ def parse_track(
     hi_res_lossless = any(tag in tags for tag in ["HIRES_LOSSLESS", "HI_RES_LOSSLESS"])
     track = Track(
         item_id=track_id,
-        provider=provider.lookup_key,
+        provider=provider.instance_id,
         name=track_obj.get("title", "Unknown"),
         version=version,
         duration=track_obj.get("duration", 0),
@@ -220,7 +220,7 @@ def parse_track(
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=image_url,
-                        provider=provider.lookup_key,
+                        provider=provider.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -263,7 +263,7 @@ def parse_playlist(
 
     playlist = Playlist(
         item_id=playlist_id,
-        provider=provider.instance_id if is_editable else provider.lookup_key,
+        provider=provider.instance_id,
         name=playlist_obj.get("title", "Unknown"),
         owner=owner_name,
         provider_mappings={
@@ -295,7 +295,7 @@ def parse_playlist(
                         MediaItemImage(
                             type=ImageType.THUMB,
                             path=image_url,
-                            provider=provider.lookup_key,
+                            provider=provider.instance_id,
                             remotely_accessible=True,
                         )
                     ]
@@ -308,7 +308,7 @@ def parse_playlist(
                 MediaItemImage(
                     type=ImageType.THUMB,
                     path=image_url,
-                    provider=provider.lookup_key,
+                    provider=provider.instance_id,
                     remotely_accessible=True,
                 )
             ]
index b4afbc9147493b7674c095d9bd3d6a7261cc11a8..3185d8e6b0d7d3a7832b060d288a328d5c66cc67 100644 (file)
@@ -192,7 +192,7 @@ class TidalProvider(MusicProvider):
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=name,
         )
 
index 844c72d5da6ff5e612110a5debf5d492fd6d426d..64f270d8b55dceefdb0e94e6ec25a92236b9a0f1 100644 (file)
@@ -110,7 +110,7 @@ class TidalRecommendationManager:
                     RecommendationFolder(
                         item_id=item_id,
                         name=folder_name,
-                        provider=self.provider.lookup_key,
+                        provider=self.provider.instance_id,
                         items=UniqueList[MediaItemType | ItemMapping | BrowseFolder](unique_items),
                         subtitle=f"From {page_name} • {len(unique_items)} items",
                         translation_key=item_id,
index 194bf95fc786ce4da2d15629cdbb8461b21e9af2..3830f64af0a957dda1cf370ffe533adf5b10fdd8 100644 (file)
@@ -74,7 +74,7 @@ class TidalStreamingManager:
 
         return StreamDetails(
             item_id=track.item_id,
-            provider=self.provider.lookup_key,
+            provider=self.provider.instance_id,
             audio_format=AudioFormat(
                 content_type=content_type,
                 sample_rate=stream_data.get("sampleRate", 44100),
index 8c74706039ac4256d322dad699e4c27bdf754ec1..671a9bddf1911dd6781f08de011b8af0a8459ac3 100644 (file)
@@ -182,7 +182,7 @@ class TuneInProvider(MusicProvider):
             preferred_stream = stream_info[0]
             radio = Radio(
                 item_id=details["preset_id"],
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 name=name,
                 provider_mappings={
                     ProviderMapping(
@@ -202,7 +202,7 @@ class TuneInProvider(MusicProvider):
             # custom url (no stream object present)
             radio = Radio(
                 item_id=details["URL"],
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 name=name,
                 provider_mappings={
                     ProviderMapping(
@@ -230,7 +230,7 @@ class TuneInProvider(MusicProvider):
                     MediaItemImage(
                         type=ImageType.THUMB,
                         path=img,
-                        provider=self.lookup_key,
+                        provider=self.instance_id,
                         remotely_accessible=True,
                     )
                 ]
@@ -267,7 +267,7 @@ class TuneInProvider(MusicProvider):
         if item_id.startswith("http"):
             # custom url
             return StreamDetails(
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 item_id=item_id,
                 audio_format=AudioFormat(
                     content_type=ContentType.UNKNOWN,
@@ -286,7 +286,7 @@ class TuneInProvider(MusicProvider):
             # and the first one is the best quality
             preferred_stream = stream_info[0]
             return StreamDetails(
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 item_id=item_id,
                 # set contenttype to unknown so ffmpeg can auto detect it
                 audio_format=AudioFormat(content_type=ContentType.UNKNOWN),
index b94a04791394c95e259b12282de9c6dfb0840b1b..ed5a9f0e4144707e9dc47b8cf0a428b083853926 100644 (file)
@@ -74,9 +74,9 @@ class UniversalGroupPlayer(GroupPlayer):
         )
         # allow grouping with all providers, except the ugp provider itself
         self._attr_can_group_with = {
-            x.lookup_key
+            x.instance_id
             for x in self.mass.players.providers
-            if x.lookup_key != self.provider.lookup_key
+            if x.instance_id != self.provider.instance_id
         }
         self._set_attributes()
 
index 9d22310c5d8ada55fad502433140b0e1f2797dbb..b742a0a4d2c446dfad21485de461160dd338aa4d 100644 (file)
@@ -30,7 +30,7 @@ class UniversalGroupProvider(PlayerProvider):
         player_id = f"{UGP_PREFIX}{shortuuid.random(8).lower()}"
         self.mass.config.create_default_player_config(
             player_id=player_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=name,
             enabled=True,
             values={
@@ -53,7 +53,7 @@ class UniversalGroupProvider(PlayerProvider):
 
     async def discover_players(self) -> None:
         """Discover players."""
-        for player_conf in await self.mass.config.get_player_configs(self.lookup_key):
+        for player_conf in await self.mass.config.get_player_configs(self.instance_id):
             if player_conf.player_id.startswith(UGP_PREFIX):
                 await self._register_player(player_conf.player_id)
 
index 771eeb1d575767d888a54867a0c7b7f9a171fc2d..ab68bc17ba1276414f02edb8359e8fe85ccefa36 100644 (file)
@@ -621,7 +621,7 @@ class YoutubeMusicProvider(MusicProvider):
         stream_format = await self._get_stream_format(item_id=item_id)
         self.logger.debug("Found stream_format: %s for song %s", stream_format["format"], item_id)
         stream_details = StreamDetails(
-            provider=self.lookup_key,
+            provider=self.instance_id,
             item_id=item_id,
             audio_format=AudioFormat(
                 content_type=ContentType.try_parse(stream_format["audio_ext"]),
@@ -649,7 +649,7 @@ class YoutubeMusicProvider(MusicProvider):
             folder = RecommendationFolder(
                 name=section["title"],
                 item_id=f"{self.instance_id}_{section['title']}",
-                provider=self.lookup_key,
+                provider=self.instance_id,
                 icon=determine_recommendation_icon(section["title"]),
             )
             for recommended_item in section.get("contents", []):
@@ -746,7 +746,7 @@ class YoutubeMusicProvider(MusicProvider):
         album = Album(
             item_id=album_id,
             name=name,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             provider_mappings={
                 ProviderMapping(
                     item_id=str(album_id),
@@ -808,7 +808,7 @@ class YoutubeMusicProvider(MusicProvider):
         artist = Artist(
             item_id=artist_id,
             name=artist_obj["name"],
-            provider=self.lookup_key,
+            provider=self.instance_id,
             provider_mappings={
                 ProviderMapping(
                     item_id=str(artist_id),
@@ -837,7 +837,7 @@ class YoutubeMusicProvider(MusicProvider):
             playlist_name = f"{playlist_name} ({self.name})"
         playlist = Playlist(
             item_id=playlist_id,
-            provider=self.instance_id if is_editable else self.lookup_key,
+            provider=self.instance_id,
             name=playlist_name,
             provider_mappings={
                 ProviderMapping(
@@ -874,7 +874,7 @@ class YoutubeMusicProvider(MusicProvider):
         track_id = str(track_obj["videoId"])
         track = Track(
             item_id=track_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=track_obj["title"],
             provider_mappings={
                 ProviderMapping(
@@ -930,7 +930,7 @@ class YoutubeMusicProvider(MusicProvider):
         podcast = Podcast(
             item_id=podcast_obj["podcastId"],
             name=podcast_obj["title"],
-            provider=self.lookup_key,
+            provider=self.instance_id,
             provider_mappings={
                 ProviderMapping(
                     item_id=podcast_obj["podcastId"],
@@ -956,7 +956,7 @@ class YoutubeMusicProvider(MusicProvider):
         item_id = f"{podcast.item_id}{PODCAST_EPISODE_SPLITTER}{episode_id}"
         episode = PodcastEpisode(
             item_id=item_id,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=episode_obj.get("title"),
             podcast=podcast,
             provider_mappings={
@@ -1024,7 +1024,7 @@ class YoutubeMusicProvider(MusicProvider):
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=self.lookup_key,
+            provider=self.instance_id,
             name=name,
         )
 
@@ -1079,7 +1079,7 @@ class YoutubeMusicProvider(MusicProvider):
                 MediaItemImage(
                     type=image_type,
                     path=url,
-                    provider=self.lookup_key,
+                    provider=self.instance_id,
                     remotely_accessible=True,
                 )
             )
index b56309559d75bc10f968d7e8972a75da76c8bf27..27e0fa24adf591a92b21d3396042c44de4a9d643 100644 (file)
@@ -34,7 +34,7 @@
         }),
         'name': '',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
@@ -57,7 +57,7 @@
         ]),
         'sort_name': '',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
@@ -96,7 +96,7 @@
     }),
     'name': 'テストシリーズ68461151-527007',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'テストシリース68461151-527007',
     'translation_key': None,
-    'uri': 'nicovideo://album/527007',
+    'uri': 'nicovideo_test://album/527007',
     'version': '',
     'year': None,
   })
           }),
           'name': 'ゲスト',
           'position': None,
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'provider_mappings': list([
             dict({
               'audio_format': dict({
           ]),
           'sort_name': 'ケスト',
           'translation_key': None,
-          'uri': 'nicovideo://artist/68461151',
+          'uri': 'nicovideo_test://artist/68461151',
           'version': '',
         }),
       ]),
       }),
       'name': 'テストシリーズ68461151-527007',
       'position': None,
-      'provider': 'nicovideo',
+      'provider': 'nicovideo_test',
       'provider_mappings': list([
         dict({
           'audio_format': dict({
       ]),
       'sort_name': 'テストシリース68461151-527007',
       'translation_key': None,
-      'uri': 'nicovideo://album/527007',
+      'uri': 'nicovideo_test://album/527007',
       'version': '',
       'year': None,
     }),
               'images': list([
                 dict({
                   'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-                  'provider': 'nicovideo',
+                  'provider': 'nicovideo_test',
                   'remotely_accessible': True,
                   'type': 'thumb',
                 }),
             }),
             'name': 'ゲスト',
             'position': None,
-            'provider': 'nicovideo',
+            'provider': 'nicovideo_test',
             'provider_mappings': list([
               dict({
                 'audio_format': dict({
             ]),
             'sort_name': 'ケスト',
             'translation_key': None,
-            'uri': 'nicovideo://artist/68461151',
+            'uri': 'nicovideo_test://artist/68461151',
             'version': '',
           }),
         ]),
           'images': list([
             dict({
               'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
             dict({
               'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'APIテスト用',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         'sort_name': 'apiテスト用',
         'track_number': 0,
         'translation_key': None,
-        'uri': 'nicovideo://track/sm45285955',
+        'uri': 'nicovideo_test://track/sm45285955',
         'version': '',
       }),
     ]),
         }),
         'name': '',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': '',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
     }),
     'name': 'テストシリーズ68461151-527007',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'テストシリース68461151-527007',
     'translation_key': None,
-    'uri': 'nicovideo://album/527007',
+    'uri': 'nicovideo_test://album/527007',
     'version': '',
     'year': None,
   })
       'images': list([
         dict({
           'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': '中の',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': '中の',
     'translation_key': None,
-    'uri': 'nicovideo://artist/4',
+    'uri': 'nicovideo_test://artist/4',
     'version': '',
   })
 # ---
       'images': list([
         dict({
           'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'ゲスト',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'ケスト',
     'translation_key': None,
-    'uri': 'nicovideo://artist/68461151',
+    'uri': 'nicovideo_test://artist/68461151',
     'version': '',
   })
 # ---
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
       'images': list([
         dict({
           'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     'name': 'テストマイリスト68461151-78597499',
     'owner': '68461151',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'テストマイリスト68461151-78597499',
     'translation_key': None,
-    'uri': 'nicovideo://playlist/78597499',
+    'uri': 'nicovideo_test://playlist/78597499',
     'version': '',
   })
 # ---
       'images': list([
         dict({
           'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     'name': 'テストマイリスト68461151-78597499',
     'owner': '68461151',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'テストマイリスト68461151-78597499',
     'translation_key': None,
-    'uri': 'nicovideo://playlist/78597499',
+    'uri': 'nicovideo_test://playlist/78597499',
     'version': '',
   })
 # ---
         'images': list([
           dict({
             'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-            'provider': 'nicovideo',
+            'provider': 'nicovideo_test',
             'remotely_accessible': True,
             'type': 'thumb',
           }),
       'name': 'テストマイリスト68461151-78597499',
       'owner': '68461151',
       'position': None,
-      'provider': 'nicovideo',
+      'provider': 'nicovideo_test',
       'provider_mappings': list([
         dict({
           'audio_format': dict({
       ]),
       'sort_name': 'テストマイリスト68461151-78597499',
       'translation_key': None,
-      'uri': 'nicovideo://playlist/78597499',
+      'uri': 'nicovideo_test://playlist/78597499',
       'version': '',
     }),
     'tracks': list([
               'images': list([
                 dict({
                   'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-                  'provider': 'nicovideo',
+                  'provider': 'nicovideo_test',
                   'remotely_accessible': True,
                   'type': 'thumb',
                 }),
             }),
             'name': 'ゲスト',
             'position': None,
-            'provider': 'nicovideo',
+            'provider': 'nicovideo_test',
             'provider_mappings': list([
               dict({
                 'audio_format': dict({
             ]),
             'sort_name': 'ケスト',
             'translation_key': None,
-            'uri': 'nicovideo://artist/68461151',
+            'uri': 'nicovideo_test://artist/68461151',
             'version': '',
           }),
         ]),
           'images': list([
             dict({
               'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
             dict({
               'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'APIテスト用',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         'sort_name': 'apiテスト用',
         'track_number': 0,
         'translation_key': None,
-        'uri': 'nicovideo://track/sm45285955',
+        'uri': 'nicovideo_test://track/sm45285955',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     'name': 'テストマイリスト68461151-78597499',
     'owner': '68461151',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'テストマイリスト68461151-78597499',
     'translation_key': None,
-    'uri': 'nicovideo://playlist/78597499',
+    'uri': 'nicovideo_test://playlist/78597499',
     'version': '',
   })
 # ---
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
     }),
     'name': 'テストシリーズ68461151-527007',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'テストシリース68461151-527007',
     'translation_key': None,
-    'uri': 'nicovideo://album/527007',
+    'uri': 'nicovideo_test://album/527007',
     'version': '',
     'year': None,
   })
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://img.cdn.nimg.jp/s/nicovideo/thumbnails/45285955/45285955.27227006.original/r640x360l?key=7098d92a9c12d0b14101a4c3c8297e04c6e7a59eb59ffe9e71c88fa0181b0cb5',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
             'images': list([
               dict({
                 'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-                'provider': 'nicovideo',
+                'provider': 'nicovideo_test',
                 'remotely_accessible': True,
                 'type': 'thumb',
               }),
           }),
           'name': 'ゲスト',
           'position': None,
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'provider_mappings': list([
             dict({
               'audio_format': dict({
           ]),
           'sort_name': 'ケスト',
           'translation_key': None,
-          'uri': 'nicovideo://artist/68461151',
+          'uri': 'nicovideo_test://artist/68461151',
           'version': '',
         }),
       ]),
       }),
       'name': 'テストシリーズ68461151-527007',
       'position': None,
-      'provider': 'nicovideo',
+      'provider': 'nicovideo_test',
       'provider_mappings': list([
         dict({
           'audio_format': dict({
       ]),
       'sort_name': 'テストシリース68461151-527007',
       'translation_key': None,
-      'uri': 'nicovideo://album/527007',
+      'uri': 'nicovideo_test://album/527007',
       'version': '',
       'year': None,
     }),
           'images': list([
             dict({
               'path': 'https://secure-dcdn.cdn.nimg.jp/nicoaccount/usericon/defaults/blank.jpg',
-              'provider': 'nicovideo',
+              'provider': 'nicovideo_test',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'ゲスト',
         'position': None,
-        'provider': 'nicovideo',
+        'provider': 'nicovideo_test',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'ケスト',
         'translation_key': None,
-        'uri': 'nicovideo://artist/68461151',
+        'uri': 'nicovideo_test://artist/68461151',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.L',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
         dict({
           'path': 'https://nicovideo.cdn.nimg.jp/thumbnails/45285955/45285955.27227006.M',
-          'provider': 'nicovideo',
+          'provider': 'nicovideo_test',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'APIテスト用',
     'position': None,
-    'provider': 'nicovideo',
+    'provider': 'nicovideo_test',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'apiテスト用',
     'track_number': 0,
     'translation_key': None,
-    'uri': 'nicovideo://track/sm45285955',
+    'uri': 'nicovideo_test://track/sm45285955',
     'version': '',
   })
 # ---
index a27b7e709ea2a9e7472bde7ad9adab998b7e30b0..5f8620e4ae09b8073090c92b17744fdbad8235b1 100644 (file)
@@ -19,7 +19,6 @@ def create_converter_manager() -> NicovideoConverterManager:
     """Create a NicovideoConverterManager for testing."""
     # Create mock provider
     mock_provider = Mock()
-    mock_provider.lookup_key = "nicovideo"
     mock_provider.instance_id = "nicovideo_test"
     mock_provider.domain = "nicovideo"
 
index be38d7f060e4103b0ac7022c7b465ac0a40e4409..6a5b10795c01e49e8d7bcc2090d2ca16e04116a6 100644 (file)
@@ -20,7 +20,7 @@
           'images': list([
             dict({
               'path': 'https://resources.tidal.com/images/1234/5678/90ab/cdef/750x750.jpg',
-              'provider': 'tidal',
+              'provider': 'tidal_instance',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
@@ -41,7 +41,7 @@
         }),
         'name': 'Test Artist',
         'position': None,
-        'provider': 'tidal',
+        'provider': 'tidal_instance',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
@@ -64,7 +64,7 @@
         ]),
         'sort_name': 'test artist',
         'translation_key': None,
-        'uri': 'tidal://artist/12345',
+        'uri': 'tidal_instance://artist/12345',
         'version': '',
       }),
     ]),
@@ -88,7 +88,7 @@
       'images': list([
         dict({
           'path': 'https://resources.tidal.com/images/abcd/ef01/2345/6789/750x750.jpg',
-          'provider': 'tidal',
+          'provider': 'tidal_instance',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'Test Album',
     'position': None,
-    'provider': 'tidal',
+    'provider': 'tidal_instance',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'test album',
     'translation_key': None,
-    'uri': 'tidal://album/67890',
+    'uri': 'tidal_instance://album/67890',
     'version': 'Deluxe Edition',
     'year': 2023,
   })
       'images': list([
         dict({
           'path': 'https://resources.tidal.com/images/1234/5678/90ab/cdef/750x750.jpg',
-          'provider': 'tidal',
+          'provider': 'tidal_instance',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'Test Artist',
     'position': None,
-    'provider': 'tidal',
+    'provider': 'tidal_instance',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'test artist',
     'translation_key': None,
-    'uri': 'tidal://artist/12345',
+    'uri': 'tidal_instance://artist/12345',
     'version': '',
   })
 # ---
       'images': list([
         dict({
           'path': 'http://example.com/mix-medium.jpg',
-          'provider': 'tidal',
+          'provider': 'tidal_instance',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     'name': 'My Daily Discovery',
     'owner': 'Created by Tidal',
     'position': None,
-    'provider': 'tidal',
+    'provider': 'tidal_instance',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'my daily discovery',
     'translation_key': None,
-    'uri': 'tidal://playlist/mix_mix_123',
+    'uri': 'tidal_instance://playlist/mix_mix_123',
     'version': '',
   })
 # ---
       'images': list([
         dict({
           'path': 'https://resources.tidal.com/images/playlist/square/image/id/750x750.jpg',
-          'provider': 'tidal',
+          'provider': 'tidal_instance',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     'name': 'Test Playlist',
     'owner': 'Tidal',
     'position': None,
-    'provider': 'tidal',
+    'provider': 'tidal_instance',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     ]),
     'sort_name': 'test playlist',
     'translation_key': None,
-    'uri': 'tidal://playlist/aabbcc-1122-3344-5566',
+    'uri': 'tidal_instance://playlist/aabbcc-1122-3344-5566',
     'version': '',
   })
 # ---
       'item_id': '67890',
       'media_type': 'album',
       'name': 'Test Album',
-      'provider': 'tidal',
+      'provider': 'tidal_instance',
       'sort_name': 'test album',
       'translation_key': None,
-      'uri': 'tidal://album/67890',
+      'uri': 'tidal_instance://album/67890',
       'version': '',
     }),
     'artists': list([
           'images': list([
             dict({
               'path': 'https://resources.tidal.com/images/1234/5678/90ab/cdef/750x750.jpg',
-              'provider': 'tidal',
+              'provider': 'tidal_instance',
               'remotely_accessible': True,
               'type': 'thumb',
             }),
         }),
         'name': 'Test Artist',
         'position': None,
-        'provider': 'tidal',
+        'provider': 'tidal_instance',
         'provider_mappings': list([
           dict({
             'audio_format': dict({
         ]),
         'sort_name': 'test artist',
         'translation_key': None,
-        'uri': 'tidal://artist/12345',
+        'uri': 'tidal_instance://artist/12345',
         'version': '',
       }),
     ]),
       'images': list([
         dict({
           'path': 'https://resources.tidal.com/images/abcd/ef01/2345/6789/750x750.jpg',
-          'provider': 'tidal',
+          'provider': 'tidal_instance',
           'remotely_accessible': True,
           'type': 'thumb',
         }),
     }),
     'name': 'Test Track',
     'position': None,
-    'provider': 'tidal',
+    'provider': 'tidal_instance',
     'provider_mappings': list([
       dict({
         'audio_format': dict({
     'sort_name': 'test track',
     'track_number': 1,
     'translation_key': None,
-    'uri': 'tidal://track/112233',
+    'uri': 'tidal_instance://track/112233',
     'version': 'Remastered',
   })
 # ---
index 0d28dfc145278f8cdc92e79e99f30bcd247410df..3efb038a616b351b1e497a415abcef64d7666ade 100644 (file)
@@ -15,7 +15,6 @@ from music_assistant.providers.tidal.library import TidalLibraryManager
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
     provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.auth.user_id = "12345"
@@ -37,7 +36,7 @@ def provider_mock() -> Mock:
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=name,
         )
 
index 05549c314da41d5c07aad5859353b399955a5b39..f90ef1e70a26c8e639a53a35ea282baf26a7aead 100644 (file)
@@ -14,7 +14,6 @@ from music_assistant.providers.tidal.media import TidalMediaManager
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
     provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.auth.user_id = "12345"
@@ -36,7 +35,7 @@ def provider_mock() -> Mock:
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=name,
         )
 
index 6ba419de87f8480f7f42274311471b5f2d7b4c88..43b25e0ba0335f40aa75dbee8e5de71291f113d2 100644 (file)
@@ -14,7 +14,6 @@ from music_assistant.providers.tidal.media import TidalMediaManager
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
     provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.auth.user_id = "12345"
@@ -27,7 +26,7 @@ def provider_mock() -> Mock:
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=name,
         )
 
index 7ab0c9f8ce67052dcf08ec6e43465770bb05af3d..80b4517206c18a69e06ae74076dd27c3b9aa6e8f 100644 (file)
@@ -17,7 +17,6 @@ PAGE_FIXTURES = list(FIXTURES_DIR.glob("pages/*.json"))
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
     provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.auth.user_id = "12345"
index ce622e7fd90199e38fd9432c43e6ceda30e24e25..135cb632d47c51ba0a645cdd2df7487cb895c495 100644 (file)
@@ -14,7 +14,6 @@ from music_assistant.providers.tidal.tidal_page_parser import TidalPageParser
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
     provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.auth.user_id = "12345"
@@ -27,7 +26,7 @@ def provider_mock() -> Mock:
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=name,
         )
 
index 3197136015bbac58288641f534ee261433693dcf..11999af99025bb06e2b2630432b5883307ece6da 100644 (file)
@@ -27,7 +27,6 @@ PLAYLIST_FIXTURES = list(FIXTURES_DIR.glob("playlists/*.json"))
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
     provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.auth.user_id = "12345"
@@ -38,7 +37,7 @@ def provider_mock() -> Mock:
         return ItemMapping(
             media_type=media_type,
             item_id=key,
-            provider=provider.lookup_key,
+            provider=provider.instance_id,
             name=name,
         )
 
index a3d6a9f532e5c2bd1cc2b31b5659b59239849d9c..9d148734f2dfe1abd60783034f339e47415a5026 100644 (file)
@@ -242,7 +242,7 @@ async def test_get_item_mapping(provider: TidalProvider) -> None:
 
     assert mapping.media_type == MediaType.ARTIST
     assert mapping.item_id == "123"
-    assert mapping.provider == provider.lookup_key
+    assert mapping.provider == provider.instance_id
     assert mapping.name == "Test Artist"
 
 
index 72f2c11d3f2f0eaa4d1a1c67e41254b6902c2c3a..d10618bad39145fb98cc5c890edbd763f055bac1 100644 (file)
@@ -14,7 +14,7 @@ from music_assistant.providers.tidal.streaming import TidalStreamingManager
 def provider_mock() -> Mock:
     """Return a mock provider."""
     provider = Mock()
-    provider.lookup_key = "tidal"
+    provider.domain = "tidal"
     provider.instance_id = "tidal_instance"
     provider.config.get_value.return_value = "HIGH"
     provider.api = AsyncMock()
@@ -73,7 +73,7 @@ async def test_get_stream_details_lossless(
     stream_details = await streaming_manager.get_stream_details("123")
 
     assert stream_details.item_id == "123"
-    assert stream_details.provider == "tidal"
+    assert stream_details.provider == "tidal_instance"
     assert stream_details.audio_format.content_type == ContentType.FLAC
     assert stream_details.audio_format.sample_rate == 44100
     assert stream_details.audio_format.bit_depth == 16