iTunes storefront fallback with configurable country setting

This commit is contained in:
Broque Thomas 2026-03-10 12:13:21 -07:00
parent 03442327ee
commit 87d567151e
3 changed files with 84 additions and 10 deletions

View file

@ -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 []

View file

@ -3322,6 +3322,35 @@
</div>
</div>
<!-- iTunes Settings -->
<div class="api-service-frame">
<h4 class="service-title itunes-title">iTunes / Apple Music</h4>
<div class="form-group">
<label>Storefront Country:</label>
<select id="itunes-country">
<option value="US">United States (US)</option>
<option value="GB">United Kingdom (GB)</option>
<option value="CA">Canada (CA)</option>
<option value="AU">Australia (AU)</option>
<option value="DE">Germany (DE)</option>
<option value="FR">France (FR)</option>
<option value="JP">Japan (JP)</option>
<option value="KR">South Korea (KR)</option>
<option value="BR">Brazil (BR)</option>
<option value="SE">Sweden (SE)</option>
<option value="NL">Netherlands (NL)</option>
<option value="IT">Italy (IT)</option>
<option value="ES">Spain (ES)</option>
<option value="MX">Mexico (MX)</option>
<option value="IN">India (IN)</option>
<option value="RU">Russia (RU)</option>
</select>
</div>
<div class="callback-info">
<div class="callback-help">Sets the primary Apple Music storefront. Region-specific albums are auto-searched across other storefronts as fallback.</div>
</div>
</div>
<!-- Test Connection Buttons -->
<div class="api-test-buttons">
<button class="test-button" onclick="testConnection('spotify')">Test

View file

@ -3913,6 +3913,9 @@ async function loadSettingsData() {
// Populate Genius settings
document.getElementById('genius-access-token').value = settings.genius?.access_token || '';
// Populate iTunes settings
document.getElementById('itunes-country').value = settings.itunes?.country || 'US';
// Populate Download settings (right column)
document.getElementById('download-path').value = settings.soulseek?.download_path || './downloads';
document.getElementById('transfer-path').value = settings.soulseek?.transfer_path || './Transfer';
@ -4629,6 +4632,9 @@ async function saveSettings(quiet = false) {
genius: {
access_token: document.getElementById('genius-access-token').value
},
itunes: {
country: document.getElementById('itunes-country').value || 'US'
},
download_source: {
mode: document.getElementById('download-source-mode').value,
hybrid_primary: document.getElementById('hybrid-primary-source').value,