From 87d567151e1d33dfa9de68d64239584ec30ab744 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 10 Mar 2026 12:13:21 -0700 Subject: [PATCH] iTunes storefront fallback with configurable country setting --- core/itunes_client.py | 59 +++++++++++++++++++++++++++++++++++------- webui/index.html | 29 +++++++++++++++++++++ webui/static/script.js | 6 +++++ 3 files changed, 84 insertions(+), 10 deletions(-) diff --git a/core/itunes_client.py b/core/itunes_client.py index a30f230a..5309aebe 100644 --- a/core/itunes_client.py +++ b/core/itunes_client.py @@ -227,15 +227,24 @@ class iTunesClient: SEARCH_URL = "https://itunes.apple.com/search" LOOKUP_URL = "https://itunes.apple.com/lookup" - - def __init__(self, country: str = "US"): - self.country = country + + # Fallback storefronts to try when primary country returns no results + FALLBACK_COUNTRIES = ['US', 'GB', 'FR', 'DE', 'JP', 'AU', 'CA', 'BR', 'KR', 'SE'] + + def __init__(self, country: str = None): + if country is None: + try: + from config.settings import config_manager + country = config_manager.get('itunes.country', 'US') + except Exception: + country = 'US' + self.country = country.upper() if country else "US" self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'SoulSync/1.0', 'Accept': 'application/json' }) - logger.info(f"iTunes client initialized for country: {country}") + logger.info(f"iTunes client initialized for country: {self.country}") def is_authenticated(self) -> bool: """ @@ -281,23 +290,53 @@ class iTunesClient: return [] def _lookup(self, **params) -> List[Dict[str, Any]]: - """Generic lookup method (not rate limited)""" + """Generic lookup method with storefront fallback. + Tries the configured country first, then falls back to other storefronts + if the result is empty (album/track may be region-restricted).""" try: params['country'] = self.country - + response = self.session.get( self.LOOKUP_URL, params=params, timeout=30 ) - + if response.status_code != 200: logger.error(f"iTunes lookup failed with status {response.status_code}") return [] - + data = response.json() - return data.get('results', []) - + results = data.get('results', []) + + # If we got results, return them + if results: + return results + + # No results — try fallback storefronts for ID-based lookups + # (only worth retrying when looking up a specific ID, not general searches) + if 'id' in params: + for fallback in self.FALLBACK_COUNTRIES: + if fallback == self.country: + continue + try: + params['country'] = fallback + response = self.session.get( + self.LOOKUP_URL, + params=params, + timeout=15 + ) + if response.status_code == 200: + data = response.json() + results = data.get('results', []) + if results: + logger.info(f"iTunes lookup found results via fallback storefront: {fallback}") + return results + except Exception: + continue + + return [] + except Exception as e: logger.error(f"Error in iTunes lookup: {e}") return [] diff --git a/webui/index.html b/webui/index.html index cf7d797d..311e4525 100644 --- a/webui/index.html +++ b/webui/index.html @@ -3322,6 +3322,35 @@ + +
+

iTunes / Apple Music

+
+ + +
+
+
Sets the primary Apple Music storefront. Region-specific albums are auto-searched across other storefronts as fallback.
+
+
+