Wire Amazon Music into enhanced search and global search source picker

- Add 'amazon' to VALID_SOURCES (and transitively VALID_STREAM_SOURCES)
  in core/search/orchestrator.py so the backend accepts it as a
  requested source without returning 400
- Add resolve_client('amazon') case — mirrors musicbrainz pattern,
  gets the cached AmazonClient from the metadata registry
- Add 'amazon' to _alternate_sources() so it appears as a tab when
  another source is primary (always available, no credentials)
- Add SERVICE_CONFIG_REGISTRY entry 'amazon': {'always': True} so
  /api/settings/config-status reports it as configured
- Add SOURCE_LABELS['amazon'] and SOURCE_ORDER entry in
  shared-helpers.js so both enhanced search and global search show
  the Amazon Music tab
- Add 'amazon' to _ALWAYS_CONFIGURED_SOURCES so the picker never
  dims the tab (no credentials required)
- Add .enh-tab-amazon.active CSS (Amazon orange #FF9900)
- 3530 tests pass
This commit is contained in:
Broque Thomas 2026-05-16 14:18:18 -07:00
parent 1f579cede8
commit d39679951b
4 changed files with 18 additions and 3 deletions

View file

@ -31,7 +31,7 @@ from . import sources
logger = logging.getLogger(__name__)
VALID_SOURCES = (
'spotify', 'itunes', 'deezer', 'discogs', 'hydrabase', 'musicbrainz',
'spotify', 'itunes', 'deezer', 'discogs', 'hydrabase', 'musicbrainz', 'amazon',
)
VALID_STREAM_SOURCES = VALID_SOURCES + ('youtube_videos',)
@ -88,6 +88,13 @@ def resolve_client(source_name: str, deps: SearchDeps) -> tuple[Any, bool]:
except Exception as e:
logger.warning(f"MusicBrainz search client init failed: {e}")
return None, False
if source_name == 'amazon':
try:
from core.metadata.registry import get_amazon_client
return get_amazon_client(), True
except Exception as e:
logger.warning(f"Amazon Music client init failed: {e}")
return None, False
return None, False
@ -183,6 +190,8 @@ def _alternate_sources(primary_source: str, deps: SearchDeps) -> list[str]:
alts.append('discogs')
if primary_source != 'hydrabase' and hydrabase_available:
alts.append('hydrabase')
if primary_source != 'amazon':
alts.append('amazon') # always available (T2Tunes, no auth)
alts.append('youtube_videos') # always available (yt-dlp, no auth)
alts.append('musicbrainz') # always available (public API)
return alts

View file

@ -1858,6 +1858,7 @@ SERVICE_CONFIG_REGISTRY = {
'spotify': {'required': ['client_id', 'client_secret']},
'itunes': {'always': True}, # default storefront works anon
'deezer': {'always': True}, # anon search works, premium ARL is optional
'amazon': {'always': True}, # T2Tunes proxy, no credentials required
'discogs': {'required': ['token']},
'tidal': {'custom': lambda _svc: _tidal_has_auth_token()},
'qobuz': {'any_of': [['email', 'password'], ['token'], ['user_auth_token']]},

View file

@ -62,6 +62,10 @@ const SOURCE_LABELS = {
logo: '/static/hydrabase.png',
tabClass: 'enh-tab-hydrabase', badgeClass: 'enh-badge-hydrabase',
},
amazon: {
text: 'Amazon Music', icon: '🛒',
tabClass: 'enh-tab-amazon', badgeClass: 'enh-badge-amazon',
},
musicbrainz: {
text: 'MusicBrainz', icon: '🧠',
logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/MusicBrainz_Logo_%282016%29.svg/500px-MusicBrainz_Logo_%282016%29.svg.png',
@ -82,7 +86,7 @@ const SOURCE_LABELS = {
// Canonical display order for the source picker. Standard metadata sources
// first, then YouTube Music Videos, then Soulseek (basic-file source).
const SOURCE_ORDER = [
'spotify', 'itunes', 'deezer', 'discogs', 'hydrabase', 'musicbrainz',
'spotify', 'itunes', 'deezer', 'discogs', 'hydrabase', 'amazon', 'musicbrainz',
'youtube_videos', 'soulseek',
];
@ -91,7 +95,7 @@ const SOURCE_ORDER = [
// Soulseek IS configurable (needs slskd URL), so it's intentionally not here:
// /api/settings/config-status reports its real state and the picker dims it
// when no slskd is set up, redirecting clicks to Settings → Downloads.
const _ALWAYS_CONFIGURED_SOURCES = new Set(['musicbrainz', 'youtube_videos']);
const _ALWAYS_CONFIGURED_SOURCES = new Set(['amazon', 'musicbrainz', 'youtube_videos']);
// Fetch /api/settings/config-status and return a map { src -> bool }
// covering every source in SOURCE_ORDER. Sources not present in the backend

View file

@ -34545,6 +34545,7 @@ div.artist-hero-badge {
.enh-source-tab.enh-tab-deezer.active { background: rgba(162, 56, 255, 0.2); color: #a238ff; }
.enh-source-tab.enh-tab-discogs.active { background: rgba(212, 165, 116, 0.2); color: #D4A574; }
.enh-source-tab.enh-tab-hydrabase.active { background: rgba(0, 180, 216, 0.2); color: #00b4d8; }
.enh-source-tab.enh-tab-amazon.active { background: rgba(255, 153, 0, 0.2); color: #FF9900; }
.enh-source-tab.enh-tab-youtube.active { background: rgba(255, 0, 0, 0.2); color: #ff4444; }
.enh-source-tab.enh-tab-musicbrainz.active { background: rgba(186, 51, 88, 0.2); color: #BA3358; }