various small tweaks
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 6 May 2024 00:37:00 +0000 (02:37 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 6 May 2024 00:37:00 +0000 (02:37 +0200)
music_assistant/server/controllers/media/base.py
music_assistant/server/controllers/metadata.py
music_assistant/server/providers/musicbrainz/__init__.py
music_assistant/server/providers/tidal/__init__.py

index 858884eba9762f8f9f3f8fab90e6bdd7890cc728..7f3b6352a2b3d31d5a69c83b96ceeefdd5ae8130 100644 (file)
@@ -235,7 +235,9 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta):
         )
         if library_item and (time() - (library_item.metadata.last_refresh or 0)) > REFRESH_INTERVAL:
             # it's been too long since the full metadata was last retrieved (or never at all)
-            metadata_lookup = True
+            if library_item.available:
+                # do not attempts metadata refresh on unavailable items as it has side effects
+                metadata_lookup = True
         if library_item and (force_refresh or metadata_lookup):
             # get (first) provider item id belonging to this library item
             add_to_library = True
@@ -259,6 +261,10 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta):
                 force_refresh=force_refresh,
                 fallback=details,
             )
+        if not details and library_item:
+            # something went wrong while trying to fetch/refresh this item
+            # return the existing (unavailable) library item and leave this for another day
+            return library_item
         if not details:
             # we couldn't get a match from any of the providers, raise error
             msg = f"Item not found: {provider_instance_id_or_domain}/{item_id}"
@@ -337,11 +343,11 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta):
     async def get_provider_mapping(self, item: ItemCls) -> tuple[str, str]:
         """Return (first) provider and item id."""
         if not getattr(item, "provider_mappings", None):
-            # make sure we have a full object
-            item = await self.get_library_item(item.item_id)
+            if item.provider == "library":
+                item = await self.get_library_item(item.item_id)
+            return (item.provider, item.item_id)
         for prefer_unique in (True, False):
             for prov_mapping in item.provider_mappings:
-                # returns the first provider that is available
                 if not prov_mapping.available:
                     continue
                 if provider := self.mass.get_provider(
@@ -352,6 +358,10 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta):
                     if prefer_unique and provider.is_streaming_provider:
                         continue
                     return (prov_mapping.provider_instance, prov_mapping.item_id)
+        # last resort: return just the first entry
+        for prov_mapping in item.provider_mappings:
+            return (prov_mapping.provider_domain, prov_mapping.item_id)
+
         return (None, None)
 
     async def get_library_item(self, item_id: int | str) -> ItemCls:
index 31a5dedbac65b5346bb3211012113761a490ddc2..f9274177dac553c73949cfba3fa23e2df643b3a7 100644 (file)
@@ -186,12 +186,15 @@ class MetaDataController(CoreController):
             if lang in (locale_code.lower(), lang_name.lower()):
                 self.mass.config.set_raw_core_config_value(self.domain, CONF_LANGUAGE, locale_code)
                 return
-        # attempt loose match on either language or country code
-        for locale_code in tuple(LOCALES):
-            language_code, region_code = locale_code.lower().split("_", 1)
-            if lang in (language_code, region_code):
-                self.mass.config.set_raw_core_config_value(self.domain, CONF_LANGUAGE, locale_code)
-                return
+        # attempt loose match on language code or region code
+        for lang_part in (lang[:2], lang[:-2]):
+            for locale_code in tuple(LOCALES):
+                language_code, region_code = locale_code.lower().split("_", 1)
+                if lang_part in (language_code, region_code):
+                    self.mass.config.set_raw_core_config_value(
+                        self.domain, CONF_LANGUAGE, locale_code
+                    )
+                    return
         # if we reach this point, we couldn't match the language
         self.logger.warning("%s is not a valid language", lang)
 
@@ -508,7 +511,7 @@ class MetaDataController(CoreController):
             # assuming that images do not/rarely change
             return web.Response(
                 body=image_data,
-                headers={"Cache-Control": "max-age=31536000"},
+                headers={"Cache-Control": "max-age=31536000", "Access-Control-Allow-Origin": "*"},
                 content_type=f"image/{image_format}",
             )
         return web.Response(status=404)
index c2f970ce67c6c2bc953a23fa5c1cc93096c063b7..469968f4810a919c02e25c19b070877897975cd2 100644 (file)
@@ -447,7 +447,7 @@ class MusicbrainzProvider(MetadataProvider):
             if response.status in (502, 503):
                 raise ResourceTemporarilyUnavailable(backoff_time=30)
             # handle 404 not found, convert to MediaNotFoundError
-            if response.status == 404:
+            if response.status in (400, 401, 404):
                 raise MediaNotFoundError(f"{endpoint} not found")
             response.raise_for_status()
             return await response.json(loads=json_loads)
index fc0820ccc52ff4a55db70c4bb938128df718599f..9eab6912de41a9ace900dc173ef83ae11908e766 100644 (file)
@@ -218,7 +218,18 @@ class TidalProvider(MusicProvider):
     async def handle_async_init(self) -> None:
         """Handle async initialization of the provider."""
         self._tidal_user_id: str = self.config.get_value(CONF_USER_ID)
-        self._tidal_session = await self._get_tidal_session()
+        try:
+            self._tidal_session = await self._get_tidal_session()
+        except Exception as err:
+            if "401 Client Error: Unauthorized" in str(err):
+                self.mass.config.set_raw_provider_config_value(
+                    self.instance_id, CONF_AUTH_TOKEN, None
+                )
+                self.mass.config.set_raw_provider_config_value(
+                    self.instance_id, CONF_REFRESH_TOKEN, None
+                )
+                raise LoginFailed("Credentials, expired, you need to re-setup")
+            raise
 
     @property
     def supported_features(self) -> tuple[ProviderFeature, ...]: