)
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
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}"
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(
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:
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)
# 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)
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)
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, ...]: