2025-08-01 19:16:20 +02:00
|
|
|
import requests
|
|
|
|
import urllib.parse
|
|
|
|
import os.path
|
|
|
|
import os
|
|
|
|
import sys
|
2025-08-01 23:21:21 +02:00
|
|
|
from dataclasses import dataclass
|
2025-08-01 19:16:20 +02:00
|
|
|
|
|
|
|
sys.path.append("/yt-dlp")
|
|
|
|
import yt_dlp
|
|
|
|
|
|
|
|
|
2025-08-01 23:21:21 +02:00
|
|
|
@dataclass
|
|
|
|
class SongInfo:
|
|
|
|
artist: str
|
|
|
|
title: str
|
|
|
|
img_id: str
|
|
|
|
tags: list[str]
|
|
|
|
|
|
|
|
|
|
|
|
def _lastfm_search(query: str) -> tuple[str, str]:
|
2025-08-01 19:16:20 +02:00
|
|
|
response = requests.get(
|
|
|
|
url="https://ws.audioscrobbler.com/2.0/?method=track.search&format=json",
|
|
|
|
params={"limit": 5, "track": query, "api_key": os.environ["LASTFM_API_KEY"]},
|
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
track_info = response.json()["results"]["trackmatches"]["track"][0]
|
|
|
|
|
|
|
|
return track_info["name"], track_info["artist"]
|
|
|
|
|
|
|
|
|
2025-08-01 23:21:21 +02:00
|
|
|
def _lastfm_getinfo(name: str, artist: str) -> tuple[str, list[str]]: # ( image_id, tags )
|
2025-08-01 19:16:20 +02:00
|
|
|
response = requests.get(
|
|
|
|
url="https://ws.audioscrobbler.com/2.0/?method=track.getInfo&format=json",
|
|
|
|
params={
|
|
|
|
"track": name,
|
|
|
|
"artist": artist,
|
|
|
|
"api_key": os.environ["LASTFM_API_KEY"],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
track_info = response.json()["track"]
|
|
|
|
|
|
|
|
image_url = urllib.parse.urlparse(track_info["album"]["image"][0]["#text"])
|
|
|
|
|
|
|
|
return (
|
2025-08-01 23:21:21 +02:00
|
|
|
# track_info["mbid"],
|
2025-08-01 19:16:20 +02:00
|
|
|
os.path.splitext(os.path.basename(image_url.path))[0],
|
2025-08-01 23:21:21 +02:00
|
|
|
[t["name"] for t in track_info["toptags"]["tag"]],
|
2025-08-01 19:16:20 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2025-08-01 23:21:21 +02:00
|
|
|
def lastfm_query_search(query: str) -> SongInfo:
|
|
|
|
name, artist = _lastfm_search(query)
|
|
|
|
|
|
|
|
img_id, tags = _lastfm_getinfo(name, artist)
|
|
|
|
|
|
|
|
return SongInfo(artist=artist, title=name, img_id=img_id, tags=tags)
|
|
|
|
|
|
|
|
|
2025-08-01 21:21:24 +02:00
|
|
|
def download_song_mp3(name: str, artist: str) -> tuple[str, str] | None: # ( id, audio )
|
|
|
|
ydl_opts = {
|
|
|
|
"format": "bestaudio",
|
|
|
|
"default_search": "ytsearch1",
|
|
|
|
"outtmpl": "%(title)s.%(ext)s",
|
|
|
|
"skip_download": True,
|
|
|
|
}
|
2025-08-01 19:16:20 +02:00
|
|
|
|
2025-08-01 21:21:24 +02:00
|
|
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
|
|
|
info = ydl.extract_info(f"{name!r} - {artist!r}", download=False)
|
2025-08-01 19:16:20 +02:00
|
|
|
|
2025-08-01 21:21:24 +02:00
|
|
|
first_entry = info["entries"][0]
|
2025-08-01 19:16:20 +02:00
|
|
|
|
2025-08-01 21:21:24 +02:00
|
|
|
video_id = first_entry["id"]
|
2025-08-01 19:16:20 +02:00
|
|
|
|
2025-08-01 21:21:24 +02:00
|
|
|
for fmt in first_entry["formats"]:
|
|
|
|
if "acodec" in fmt and fmt["acodec"] != "none":
|
|
|
|
return video_id, fmt["url"]
|
2025-08-01 19:16:20 +02:00
|
|
|
|
2025-08-01 21:21:24 +02:00
|
|
|
return None
|