Add Metadata

This commit is contained in:
Christoph Stahl 2025-08-09 23:18:56 +02:00
parent 6b130dac00
commit 14f58de9e5

51
qobuz-dl-remote.py Normal file → Executable file
View file

@ -1,8 +1,13 @@
#!/usr/bin/env python3
from typing import Any, Literal from typing import Any, Literal
import requests import requests
import tqdm import tqdm
from mutagen.flac import FLAC, Picture
import sys import sys
from dataclasses import dataclass from dataclasses import dataclass
import datetime
from PIL import Image
from io import BytesIO
QOBUZ_API_BASE = "https://us.qobuz.squid.wtf/api" QOBUZ_API_BASE = "https://us.qobuz.squid.wtf/api"
QUALITY = 27 QUALITY = 27
@ -11,7 +16,12 @@ QUALITY = 27
@dataclass @dataclass
class Track: class Track:
title: str title: str
artist: str
nr: int
id: str id: str
album_name: str
release_date: datetime.date
cover: Image.Image | None
@dataclass @dataclass
@ -44,14 +54,35 @@ class Album:
maximum_sampling_rate: int maximum_sampling_rate: int
id: str id: str
image: ImageLink image: ImageLink
release_date: datetime.date
@classmethod @classmethod
def from_dict(cls, data: dict[Any, Any]) -> "Album": def from_dict(cls, data: dict[Any, Any]) -> "Album":
tracks = None tracks = None
release_date = datetime.date.fromtimestamp(data["released_at"])
image = ImageLink.from_dict(data["image"])
image_data = None
if image.large is not None:
# Fetch image
print(f"Fetching image from {image.large}")
image_content = BytesIO(requests.get(image.large).content)
image_data = Image.open(image_content)
if "tracks" in data and "track_ids" in data: if "tracks" in data and "track_ids" in data:
tracks = [ tracks = [
Track(id=tid, title=track["title"]) Track(
for tid, track in zip(data["track_ids"], data["tracks"]["items"]) nr=tnr + 1,
artist=data["artist"]["name"],
id=tid,
title=track["title"],
album_name=data["title"],
release_date=release_date,
cover=image_data,
)
for tnr, (tid, track) in enumerate(
zip(data["track_ids"], data["tracks"]["items"])
)
] ]
return cls( return cls(
artist=data["artist"]["name"], artist=data["artist"]["name"],
@ -61,6 +92,7 @@ class Album:
maximum_sampling_rate=data["maximum_sampling_rate"], maximum_sampling_rate=data["maximum_sampling_rate"],
id=data["id"], id=data["id"],
image=ImageLink.from_dict(data["image"]), image=ImageLink.from_dict(data["image"]),
release_date=release_date,
) )
@ -80,8 +112,8 @@ class Qobuz:
return result_json["data"]["url"] return result_json["data"]["url"]
raise RuntimeError(f"Failed to get download URL, result: {result}") raise RuntimeError(f"Failed to get download URL, result: {result}")
def download_track(self, track_id: str, filename: str) -> None: def download_track(self, track: Track, filename: str) -> None:
download_url = self.get_download_url(track_id) download_url = self.get_download_url(track.id)
response = requests.get(download_url, stream=True) response = requests.get(download_url, stream=True)
if response.status_code == 200: if response.status_code == 200:
total_size = int(response.headers.get("content-length", 0)) total_size = int(response.headers.get("content-length", 0))
@ -92,6 +124,15 @@ class Qobuz:
for chunk in response.iter_content(chunk_size=8192): for chunk in response.iter_content(chunk_size=8192):
file.write(chunk) file.write(chunk)
pbar.update(len(chunk)) pbar.update(len(chunk))
# Set metadata using mutagen
audio = FLAC(filename)
audio["title"] = track.title
audio["artist"] = track.artist
audio["tracknumber"] = str(track.nr)
audio["album"] = track.album_name
audio["date"] = track.release_date.isoformat()
audio.save()
print(f"Downloaded {filename}") print(f"Downloaded {filename}")
else: else:
raise RuntimeError("Failed to download track") raise RuntimeError("Failed to download track")
@ -112,7 +153,7 @@ class Qobuz:
for nr, track in enumerate(tracks): for nr, track in enumerate(tracks):
print(f"Downloading track #{nr + 1:02d} {track.title}...") print(f"Downloading track #{nr + 1:02d} {track.title}...")
filename = f"{artist} - {album_title} - {nr + 1:02d} {track.title}.flac" filename = f"{artist} - {album_title} - {nr + 1:02d} {track.title}.flac"
self.download_track(track.id, filename) self.download_track(track, filename)
def search_album(self, query: str) -> Album: def search_album(self, query: str) -> Album:
print(f'Searching for "{query}"') print(f'Searching for "{query}"')