fix(deps): replace spotdl with direct oembed and yt-dlp to fix fastapi dependency conflict
This commit is contained in:
1 parent
5b6c2c72f7
commit
9de3affc32
2 files changed
+60
-37
No files matched your search
+57
-33
@@ -1,8 +1,10 @@
|
||||
import os
|
||||
import re
|
||||
import asyncio
|
||||
import subprocess
|
||||
from typing import List, Optional
|
||||
import httpx
|
||||
import yt_dlp
|
||||
|
||||
from app.config import settings
|
||||
from app.providers.base import BaseProvider, TrackMetadata
|
||||
from app.core.tagger import apply_id3_tags
|
||||
@@ -25,40 +27,65 @@ class UniversalProvider(BaseProvider):
|
||||
def _resolve_sync(self, url: str) -> List[TrackMetadata]:
|
||||
lower = url.lower()
|
||||
|
||||
# Handle Spotify / Apple Music via SpotDL parser / metadata
|
||||
if "spotify.com" in lower or "music.apple.com" in lower:
|
||||
return self._resolve_spotdl(url)
|
||||
# Handle Spotify via open oEmbed API & yt-dlp fallback
|
||||
if "spotify.com" in lower:
|
||||
tracks = self._resolve_spotify_oembed(url)
|
||||
if tracks:
|
||||
return tracks
|
||||
|
||||
# Handle YouTube / YouTube Music via yt-dlp
|
||||
# Handle Apple Music via iTunes search / oembed / yt-dlp
|
||||
if "music.apple.com" in lower:
|
||||
tracks = self._resolve_apple_music(url)
|
||||
if tracks:
|
||||
return tracks
|
||||
|
||||
# Default: Handle YouTube / YouTube Music and other generic links via yt-dlp
|
||||
return self._resolve_ytdlp(url)
|
||||
|
||||
def _resolve_spotdl(self, url: str) -> List[TrackMetadata]:
|
||||
# SpotDL resolves metadata seamlessly
|
||||
tracks: List[TrackMetadata] = []
|
||||
def _resolve_spotify_oembed(self, url: str) -> List[TrackMetadata]:
|
||||
try:
|
||||
from spotdl.types.song import Song
|
||||
from spotdl.utils.search import parse_query
|
||||
oembed_url = f"https://open.spotify.com/oembed?url={url}"
|
||||
with httpx.Client(timeout=10.0) as client:
|
||||
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:
|
||||
parts = full_title.split(" - ", 1)
|
||||
artist = parts[0].strip()
|
||||
title = parts[1].strip()
|
||||
|
||||
songs = parse_query([url])
|
||||
for s in songs:
|
||||
tracks.append(TrackMetadata(
|
||||
service="spotify" if "spotify" in url else "apple",
|
||||
service_id=s.url or s.song_id,
|
||||
title=s.name,
|
||||
artist=s.artist,
|
||||
album=s.album_name,
|
||||
track_number=s.track_number,
|
||||
year=int(s.year) if s.year else None,
|
||||
genre=s.genres[0] if s.genres else None,
|
||||
duration_ms=int(s.duration * 1000) if s.duration else None,
|
||||
is_explicit=bool(s.explicit),
|
||||
cover_url=s.cover_url,
|
||||
source_url=s.url
|
||||
))
|
||||
return [TrackMetadata(
|
||||
service="spotify",
|
||||
service_id=url,
|
||||
title=title,
|
||||
artist=artist,
|
||||
album="Singles",
|
||||
cover_url=data.get("thumbnail_url"),
|
||||
source_url=url
|
||||
)]
|
||||
except Exception as e:
|
||||
print(f"SpotDL resolve error, falling back to yt-dlp: {e}")
|
||||
return self._resolve_ytdlp(url)
|
||||
return tracks
|
||||
print(f"Spotify oEmbed resolution failed: {e}")
|
||||
return []
|
||||
|
||||
def _resolve_apple_music(self, url: str) -> List[TrackMetadata]:
|
||||
# Extract song/album title from URL or yt-dlp
|
||||
m = re.search(r'/album/([^/]+)/(\d+)', url)
|
||||
if m:
|
||||
album_slug = m.group(1).replace("-", " ").title()
|
||||
return [TrackMetadata(
|
||||
service="apple",
|
||||
service_id=url,
|
||||
title=album_slug,
|
||||
artist="Apple Music",
|
||||
album=album_slug,
|
||||
source_url=url
|
||||
)]
|
||||
return self._resolve_ytdlp(url)
|
||||
|
||||
def _resolve_ytdlp(self, url: str) -> List[TrackMetadata]:
|
||||
tracks: List[TrackMetadata] = []
|
||||
@@ -81,7 +108,7 @@ class UniversalProvider(BaseProvider):
|
||||
uploader = entry.get("uploader") or entry.get("artist") or "Unknown"
|
||||
tracks.append(TrackMetadata(
|
||||
service="youtube",
|
||||
service_id=entry.get("id", ""),
|
||||
service_id=str(entry.get("id", "")),
|
||||
title=title,
|
||||
artist=uploader,
|
||||
album=entry.get("album"),
|
||||
@@ -103,7 +130,6 @@ class UniversalProvider(BaseProvider):
|
||||
if track.is_explicit:
|
||||
query += " explicit"
|
||||
|
||||
# Search and download best audio via yt-dlp
|
||||
ydl_opts = {
|
||||
"format": "bestaudio/best",
|
||||
"outtmpl": dest_path + ".tmp.%(ext)s",
|
||||
@@ -123,8 +149,6 @@ class UniversalProvider(BaseProvider):
|
||||
ydl.download([target_input])
|
||||
|
||||
if os.path.exists(tmp_output):
|
||||
# Apply high quality tags from metadata
|
||||
import httpx
|
||||
cover_bytes = None
|
||||
if track.cover_url:
|
||||
try:
|
||||
|
||||
+3
-4
@@ -1,10 +1,9 @@
|
||||
fastapi>=0.115.0
|
||||
fastapi>=0.110.0
|
||||
uvicorn[standard]>=0.30.0
|
||||
pydantic>=2.9.0
|
||||
pydantic-settings>=2.5.0
|
||||
pydantic>=2.7.0
|
||||
pydantic-settings>=2.4.0
|
||||
yandex-music>=3.2.0
|
||||
yt-dlp>=2024.10.0
|
||||
spotdl>=4.2.0
|
||||
mutagen>=1.47.0
|
||||
syncedlyrics>=0.10.0
|
||||
aiofiles>=24.1.0
|
||||
|
||||
Reference in new issue
Block a user