feat(spotify): add Spotify search, prioritize Spotify in ranking and recommendations
This commit is contained in:
1 parent
fb1a5e01dd
commit
3db9bdb38b
6 files changed
+207
-14
No files matched your search
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user