Moved to uv, not convinced. Added ConfigParser and Argparser

This commit is contained in:
Christoph Stahl 2025-08-10 21:35:53 +02:00
parent 41b363bb37
commit 52e1bfbfed
4 changed files with 80 additions and 32 deletions

View file

@ -1,16 +1,23 @@
[project]
name = "qobuz-dl-remote"
version = "0.1.0"
authors = [
{name = "Christoph Stahl", email = "christoph.stahl@tu-dortmund.de"}
]
description = "Add your description here"
readme = "README.md"
authors = [
{ name = "Christoph Stahl", email = "christoph.stahl@tu-dortmund.de" }
]
requires-python = ">=3.10"
dependencies = [
"music-tag>=0.4.3",
"musicbrainzngs>=0.7.1",
"mutagen>=1.47.0",
"pillow>=11.3.0",
"requests>=2.32.4",
"tqdm>=4.67.1",
]
[project.scripts]
qobuz-dl-remote = "qobuz_dl_remote.main:main"
[build-system]
requires = ["uv_build>=0.8.4,<0.9.0"]
build-backend = "uv_build"

View file

@ -1,16 +1,15 @@
#!/usr/bin/env python3
import datetime
import os
from dataclasses import dataclass
from io import BytesIO
from typing import Any, Literal
from configparser import ConfigParser
from argparse import ArgumentParser
import requests
import tqdm
import music_tag
import sys
from dataclasses import dataclass
import datetime
QOBUZ_API_BASE = "https://us.qobuz.squid.wtf/api"
QUALITY = 27
from mutagen.flac import FLAC, Picture
from PIL import Image
@dataclass
@ -93,12 +92,12 @@ class Album:
class Qobuz:
def __init__(self, api_base=QOBUZ_API_BASE, quality: int = QUALITY) -> None:
def __init__(self, api_base, quality: int) -> None:
self.api_base = api_base
self.quality = quality
def get_download_url(self, track_id: str) -> str:
url = f"{QOBUZ_API_BASE}/download-music"
url = f"{self.api_base}/download-music"
result = requests.get(
url, params={"track_id": track_id, "quality": self.quality}
)
@ -110,24 +109,35 @@ class Qobuz:
def download_track(self, track: Track, filename: str, dest: str) -> None:
download_url = self.get_download_url(track.id)
dest_path = os.path.join(dest, filename)
response = requests.get(download_url, stream=True)
if response.status_code == 200:
total_size = int(response.headers.get("content-length", 0))
with tqdm.tqdm(
total=total_size, unit="B", unit_scale=True, desc=filename
) as pbar:
with open(os.path.join(dest, filename), "wb") as file:
with open(dest_path, "wb") as file:
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
pbar.update(len(chunk))
# Set metadata using mutagen
audio = music_tag.load_file(filename)
audio["tracktitle"] = track.title
audio = FLAC(dest_path)
audio["title"] = track.title
audio["artist"] = track.album.artist
audio["tracknumber"] = str(track.nr)
audio["album"] = track.album.title
audio["year"] = track.album.release_date.year
audio["artwork"] = track.album.cover_data
audio["release_date"] = track.album.release_date.isoformat()
if track.album.cover_data is not None:
picture = Picture()
pil_image = Image.open(BytesIO(track.album.cover_data))
picture.type = 3 # Front cover
picture.data = track.album.cover_data
picture.mime = pil_image.get_format_mimetype()
picture.desc = "Cover"
picture.width = pil_image.width
picture.height = pil_image.height
picture.depth = len(pil_image.getbands()) * 8
audio.add_picture(picture)
audio.save()
print(f"Downloaded {filename}")
@ -167,11 +177,42 @@ class Qobuz:
self.download_album(album, f"{album.artist} - {album.title}")
def get_config() -> ConfigParser:
config = ConfigParser()
if os.path.exists("config.ini"):
config_file = "config.ini"
else:
config_file = os.path.expanduser("~/.config/qobuz_dl_remote/config.ini")
if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file)
return config
def main() -> None:
# trackid = sys.argv[1]
# filename = sys.argv[2]
query = sys.argv[1]
qb = Qobuz()
config = get_config()
base_url = config.get("qobuz", "api_base")
if not base_url:
print("No API base URL configured, please set api_base under [qobuz].")
quality = config.getint("qobuz", "quality", fallback=27)
parser = ArgumentParser(description="Qobuz Remote Downloader")
parser.add_argument(
"--type",
"-t",
type=str,
choices=["album", "discography"],
default="album",
help="Type of search to perform",
)
parser.add_argument(
"query", type=str, help="Search query for the album to download"
)
parser.add_argument("--beet", "-b", action="store_true", help="Import into Beet")
args = parser.parse_args()
query = args.query
qb = Qobuz(api_base=base_url, quality=quality)
print(qb.search_and_download(query))

16
uv.lock generated
View file

@ -94,13 +94,13 @@ wheels = [
]
[[package]]
name = "music-tag"
version = "0.4.3"
name = "musicbrainzngs"
version = "0.7.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mutagen" },
sdist = { url = "https://files.pythonhosted.org/packages/0a/67/3e74ae93d90ceeba72ed1a266dd3ca9abd625f315f0afd35f9b034acedd1/musicbrainzngs-0.7.1.tar.gz", hash = "sha256:ab1c0100fd0b305852e65f2ed4113c6de12e68afd55186987b8ed97e0f98e627", size = 117469, upload-time = "2020-01-11T17:38:47.581Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6d/fd/cef7b2580436910ccd2f8d3deec0f3c81743e15c0eb5b97dde3fbf33c0c8/musicbrainzngs-0.7.1-py2.py3-none-any.whl", hash = "sha256:e841a8f975104c0a72290b09f59326050194081a5ae62ee512f41915090e1a10", size = 25289, upload-time = "2020-01-11T17:38:45.469Z" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/f4/ebcdd2fc9bfaf569b795250090e4f4088dc65a5a3e32c53baa9bfc3fc296/music-tag-0.4.3.tar.gz", hash = "sha256:0aab6e6eeda8df0f5316ec2d2190bd74561b7e03562ab091ce8d5687cdbcfff6", size = 23153, upload-time = "2021-06-05T17:20:23.552Z" }
[[package]]
name = "mutagen"
@ -216,9 +216,9 @@ wheels = [
[[package]]
name = "qobuz-dl-remote"
version = "0.1.0"
source = { virtual = "." }
source = { editable = "." }
dependencies = [
{ name = "music-tag" },
{ name = "musicbrainzngs" },
{ name = "mutagen" },
{ name = "pillow" },
{ name = "requests" },
@ -227,7 +227,7 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "music-tag", specifier = ">=0.4.3" },
{ name = "musicbrainzngs", specifier = ">=0.7.1" },
{ name = "mutagen", specifier = ">=1.47.0" },
{ name = "pillow", specifier = ">=11.3.0" },
{ name = "requests", specifier = ">=2.32.4" },