feat(spotify): add Spotify search, prioritize Spotify in ranking and recommendations

This commit is contained in:
Antigravity committed 2026-10-12 15:42:26 +03:00
1 parent fb1a5e01dd
commit 3db9bdb38b
6 files changed
+207 -14

No files matched your search

+82 -4
View File
@@ -30,9 +30,9 @@ class UniversalProvider(BaseProvider):
def _resolve_sync(self, url: str) -> List[TrackMetadata]:
lower = url.lower()
# Handle Spotify via open oEmbed API & yt-dlp fallback
# Handle Spotify via spotifyscraper & open oEmbed fallback
if "spotify.com" in lower:
tracks = self._resolve_spotify_oembed(url)
tracks = self._resolve_spotify(url)
if tracks:
return tracks
@@ -45,6 +45,86 @@ class UniversalProvider(BaseProvider):
# Default: Handle YouTube / YouTube Music and other generic links via yt-dlp
return self._resolve_ytdlp(url)
def _resolve_spotify(self, url: str) -> List[TrackMetadata]:
tracks: List[TrackMetadata] = []
try:
from spotify_scraper import SpotifyClient
m_track = re.search(r'track/([a-zA-Z0-9]+)', url)
m_album = re.search(r'album/([a-zA-Z0-9]+)', url)
m_playlist = re.search(r'playlist/([a-zA-Z0-9]+)', url)
with SpotifyClient() as client:
if m_track:
t = client.get_track(m_track.group(1))
if t:
artist = ", ".join(a.name for a in t.artists) if t.artists else "Unknown"
album = t.album.name if (t.album and hasattr(t.album, "name")) else "Singles"
cover = None
if hasattr(t, "images") and t.images:
cover = getattr(t.images[0], "url", None)
elif hasattr(t, "album") and t.album and getattr(t.album, "images", None):
cover = getattr(t.album.images[0], "url", None)
tracks.append(TrackMetadata(
service="spotify",
service_id=str(t.id),
title=t.name,
artist=artist,
album=album,
duration_ms=getattr(t, "duration_ms", None),
cover_url=cover,
is_explicit=getattr(t, "explicit", False),
source_url=t.url or url
))
return tracks
elif m_album:
alb = client.get_album(m_album.group(1))
if alb and hasattr(alb, "tracks"):
cover = getattr(alb.images[0], "url", None) if hasattr(alb, "images") and alb.images else None
for idx, t in enumerate(alb.tracks, 1):
artist = ", ".join(a.name for a in t.artists) if t.artists else "Unknown"
tracks.append(TrackMetadata(
service="spotify",
service_id=str(t.id),
title=t.name,
artist=artist,
album=alb.name,
track_number=idx,
duration_ms=getattr(t, "duration_ms", None),
cover_url=cover,
is_explicit=getattr(t, "explicit", False),
source_url=t.url or url
))
return tracks
elif m_playlist:
pl = client.get_playlist(m_playlist.group(1))
if pl and hasattr(pl, "tracks"):
cover = getattr(pl.images[0], "url", None) if hasattr(pl, "images") and pl.images else None
for idx, t in enumerate(pl.tracks, 1):
artist = ", ".join(a.name for a in t.artists) if t.artists else "Unknown"
album = getattr(t.album, "name", pl.name) if getattr(t, "album", None) else pl.name
tracks.append(TrackMetadata(
service="spotify",
service_id=str(t.id),
title=t.name,
artist=artist,
album=album,
track_number=idx,
duration_ms=getattr(t, "duration_ms", None),
cover_url=cover,
is_explicit=getattr(t, "explicit", False),
source_url=t.url or url
))
return tracks
except Exception as e:
print(f"Spotify scraper resolution failed: {e}")
# Fallback to open oembed if scraper failed
return self._resolve_spotify_oembed(url)
def _resolve_spotify_oembed(self, url: str) -> List[TrackMetadata]:
try:
oembed_url = f"https://open.spotify.com/oembed?url={url}"
@@ -52,9 +132,7 @@ class UniversalProvider(BaseProvider):
resp = client.get(oembed_url)
if resp.status_code == 200:
data = resp.json()
# data has title ("Track Name" or "Track Name - Single"), thumbnail_url
full_title = data.get("title", "Unknown")
# Spotfiy oEmbed titles are often "Title" or "Artist - Title"
artist = "Unknown"
title = full_title
if " - " in full_title: