iTunes storefront fallback with configurable country setting
This commit is contained in:
parent
03442327ee
commit
87d567151e
3 changed files with 84 additions and 10 deletions
|
|
@ -227,15 +227,24 @@ class iTunesClient:
|
||||||
|
|
||||||
SEARCH_URL = "https://itunes.apple.com/search"
|
SEARCH_URL = "https://itunes.apple.com/search"
|
||||||
LOOKUP_URL = "https://itunes.apple.com/lookup"
|
LOOKUP_URL = "https://itunes.apple.com/lookup"
|
||||||
|
|
||||||
def __init__(self, country: str = "US"):
|
# Fallback storefronts to try when primary country returns no results
|
||||||
self.country = country
|
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 = requests.Session()
|
||||||
self.session.headers.update({
|
self.session.headers.update({
|
||||||
'User-Agent': 'SoulSync/1.0',
|
'User-Agent': 'SoulSync/1.0',
|
||||||
'Accept': 'application/json'
|
'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:
|
def is_authenticated(self) -> bool:
|
||||||
"""
|
"""
|
||||||
|
|
@ -281,23 +290,53 @@ class iTunesClient:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def _lookup(self, **params) -> List[Dict[str, Any]]:
|
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:
|
try:
|
||||||
params['country'] = self.country
|
params['country'] = self.country
|
||||||
|
|
||||||
response = self.session.get(
|
response = self.session.get(
|
||||||
self.LOOKUP_URL,
|
self.LOOKUP_URL,
|
||||||
params=params,
|
params=params,
|
||||||
timeout=30
|
timeout=30
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
logger.error(f"iTunes lookup failed with status {response.status_code}")
|
logger.error(f"iTunes lookup failed with status {response.status_code}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
data = response.json()
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error in iTunes lookup: {e}")
|
logger.error(f"Error in iTunes lookup: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -3322,6 +3322,35 @@
|
||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- Test Connection Buttons -->
|
||||||
<div class="api-test-buttons">
|
<div class="api-test-buttons">
|
||||||
<button class="test-button" onclick="testConnection('spotify')">Test
|
<button class="test-button" onclick="testConnection('spotify')">Test
|
||||||
|
|
|
||||||
|
|
@ -3913,6 +3913,9 @@ async function loadSettingsData() {
|
||||||
// Populate Genius settings
|
// Populate Genius settings
|
||||||
document.getElementById('genius-access-token').value = settings.genius?.access_token || '';
|
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)
|
// Populate Download settings (right column)
|
||||||
document.getElementById('download-path').value = settings.soulseek?.download_path || './downloads';
|
document.getElementById('download-path').value = settings.soulseek?.download_path || './downloads';
|
||||||
document.getElementById('transfer-path').value = settings.soulseek?.transfer_path || './Transfer';
|
document.getElementById('transfer-path').value = settings.soulseek?.transfer_path || './Transfer';
|
||||||
|
|
@ -4629,6 +4632,9 @@ async function saveSettings(quiet = false) {
|
||||||
genius: {
|
genius: {
|
||||||
access_token: document.getElementById('genius-access-token').value
|
access_token: document.getElementById('genius-access-token').value
|
||||||
},
|
},
|
||||||
|
itunes: {
|
||||||
|
country: document.getElementById('itunes-country').value || 'US'
|
||||||
|
},
|
||||||
download_source: {
|
download_source: {
|
||||||
mode: document.getElementById('download-source-mode').value,
|
mode: document.getElementById('download-source-mode').value,
|
||||||
hybrid_primary: document.getElementById('hybrid-primary-source').value,
|
hybrid_primary: document.getElementById('hybrid-primary-source').value,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue