Remove print calls; colored output; minor fixes

Close #37
This commit is contained in:
vitiko98 2020-12-17 21:27:08 -04:00
parent 1bf8bb16de
commit fd260ee0f3
9 changed files with 143 additions and 84 deletions

View File

@ -1,11 +1,18 @@
import base64 import base64
import configparser import configparser
import logging
import os import os
import sys import sys
import qobuz_dl.spoofbuz as spoofbuz import qobuz_dl.spoofbuz as spoofbuz
from qobuz_dl.core import QobuzDL from qobuz_dl.color import DF, GREEN, MAGENTA, RED, YELLOW
from qobuz_dl.commands import qobuz_dl_args from qobuz_dl.commands import qobuz_dl_args
from qobuz_dl.core import QobuzDL
logging.basicConfig(
level=logging.INFO,
format="%(message)s",
)
if os.name == "nt": if os.name == "nt":
OS_CONFIG = os.environ.get("APPDATA") OS_CONFIG = os.environ.get("APPDATA")
@ -17,32 +24,34 @@ CONFIG_FILE = os.path.join(CONFIG_PATH, "config.ini")
def reset_config(config_file): def reset_config(config_file):
print("Creating config file: " + config_file) logging.info(f"{YELLOW}Creating config file: {config_file}")
config = configparser.ConfigParser() config = configparser.ConfigParser()
config["DEFAULT"]["email"] = input("\nEnter your email:\n- ") config["DEFAULT"]["email"] = input(f"{MAGENTA}Enter your email:\n-{DF} ")
config["DEFAULT"]["password"] = base64.b64encode( config["DEFAULT"]["password"] = base64.b64encode(
input("\nEnter your password\n- ").encode() input(f"{MAGENTA}Enter your password\n-{DF} ").encode()
).decode() ).decode()
config["DEFAULT"]["default_folder"] = ( config["DEFAULT"]["default_folder"] = (
input("\nFolder for downloads (leave empy for default 'Qobuz Downloads')\n- ") input(
f"{MAGENTA}Folder for downloads (leave empy for default 'Qobuz Downloads')\n-{DF} "
)
or "Qobuz Downloads" or "Qobuz Downloads"
) )
config["DEFAULT"]["default_quality"] = ( config["DEFAULT"]["default_quality"] = (
input( input(
"\nDownload quality (5, 6, 7, 27) " f"{MAGENTA}Download quality (5, 6, 7, 27) "
"[320, LOSSLESS, 24B <96KHZ, 24B >96KHZ]" "[320, LOSSLESS, 24B <96KHZ, 24B >96KHZ]"
"\n(leave empy for default '6')\n- " f"\n(leave empy for default '6')\n-{DF} "
) )
or "6" or "6"
) )
config["DEFAULT"]["default_limit"] = "20" config["DEFAULT"]["default_limit"] = "20"
print("Getting tokens. Please wait...") logging.info(f"{YELLOW}Getting tokens. Please wait...")
spoofer = spoofbuz.Spoofer() spoofer = spoofbuz.Spoofer()
config["DEFAULT"]["app_id"] = str(spoofer.getAppId()) config["DEFAULT"]["app_id"] = str(spoofer.getAppId())
config["DEFAULT"]["secrets"] = ",".join(spoofer.getSecrets().values()) config["DEFAULT"]["secrets"] = ",".join(spoofer.getSecrets().values())
with open(config_file, "w") as configfile: with open(config_file, "w") as configfile:
config.write(configfile) config.write(configfile)
print("Config file updated.") logging.info(f"{GREEN}Config file updated.")
def main(): def main():
@ -77,7 +86,9 @@ def main():
except (KeyError, UnicodeDecodeError): except (KeyError, UnicodeDecodeError):
arguments = qobuz_dl_args().parse_args() arguments = qobuz_dl_args().parse_args()
if not arguments.reset: if not arguments.reset:
print("Your config file is corrupted! Run 'qobuz-dl -r' to fix this\n") logging.warning(
f"{RED}Your config file is corrupted! Run 'qobuz-dl -r' to fix this"
)
if arguments.reset: if arguments.reset:
sys.exit(reset_config(CONFIG_FILE)) sys.exit(reset_config(CONFIG_FILE))
@ -91,16 +102,22 @@ def main():
) )
qobuz.initialize_client(email, password, app_id, secrets) qobuz.initialize_client(email, password, app_id, secrets)
if arguments.command == "dl": try:
qobuz.download_list_of_urls(arguments.SOURCE) if arguments.command == "dl":
elif arguments.command == "lucky": qobuz.download_list_of_urls(arguments.SOURCE)
query = " ".join(arguments.QUERY) elif arguments.command == "lucky":
qobuz.lucky_type = arguments.type query = " ".join(arguments.QUERY)
qobuz.lucky_limit = arguments.number qobuz.lucky_type = arguments.type
qobuz.lucky_mode(query) qobuz.lucky_limit = arguments.number
else: qobuz.lucky_mode(query)
qobuz.interactive_limit = arguments.limit else:
qobuz.interactive() qobuz.interactive_limit = arguments.limit
qobuz.interactive()
except KeyboardInterrupt:
logging.info(
f"{RED}Interrupted by user\n{MAGENTA}Already downloaded items will "
"be skipped if you try to download the same releases again"
)
if __name__ == "__main__": if __name__ == "__main__":

13
qobuz_dl/color.py Normal file
View File

@ -0,0 +1,13 @@
from colorama import Style, Fore, init
init(autoreset=True)
DF = Style.NORMAL
BG = Style.BRIGHT
OFF = Style.DIM
RED = Fore.RED
BLUE = Fore.BLUE
GREEN = Fore.GREEN
YELLOW = Fore.YELLOW
CYAN = Fore.CYAN
MAGENTA = Fore.MAGENTA

View File

@ -1,3 +1,4 @@
import logging
import os import os
import re import re
import string import string
@ -6,18 +7,21 @@ import time
import requests import requests
from bs4 import BeautifulSoup as bso from bs4 import BeautifulSoup as bso
from mutagen.flac import FLAC
from mutagen.mp3 import EasyMP3
from pathvalidate import sanitize_filename from pathvalidate import sanitize_filename
import qobuz_dl.spoofbuz as spoofbuz import qobuz_dl.spoofbuz as spoofbuz
from qobuz_dl import downloader, qopy from qobuz_dl import downloader, qopy
from qobuz_dl.color import MAGENTA, OFF, RED, YELLOW, DF
from mutagen.flac import FLAC
from mutagen.mp3 import EasyMP3
WEB_URL = "https://play.qobuz.com/" WEB_URL = "https://play.qobuz.com/"
ARTISTS_SELECTOR = "td.chartlist-artist > a" ARTISTS_SELECTOR = "td.chartlist-artist > a"
TITLE_SELECTOR = "td.chartlist-name > a" TITLE_SELECTOR = "td.chartlist-name > a"
EXTENSIONS = (".mp3", ".flac") EXTENSIONS = (".mp3", ".flac")
QUALITIES = {5: "5 - MP3", 6: "6 - FLAC", 7: "7 - 24B<96kHz", 27: "27 - 24B>96kHz"}
logger = logging.getLogger(__name__)
class PartialFormatter(string.Formatter): class PartialFormatter(string.Formatter):
@ -67,6 +71,7 @@ class QobuzDL:
def initialize_client(self, email, pwd, app_id, secrets): def initialize_client(self, email, pwd, app_id, secrets):
self.client = qopy.Client(email, pwd, app_id, secrets) self.client = qopy.Client(email, pwd, app_id, secrets)
logger.info(f"{YELLOW}Set quality: {QUALITIES[int(self.quality)]}")
def get_tokens(self): def get_tokens(self):
spoofer = spoofbuz.Spoofer() spoofer = spoofbuz.Spoofer()
@ -83,8 +88,8 @@ class QobuzDL:
def get_id(self, url): def get_id(self, url):
return re.match( return re.match(
r"https?://(?:w{0,3}|play|open)\.qobuz\.com/(?:(?:album|track|artist" r"https?://(?:w{0,3}|play|open)\.qobuz\.com/(?:(?:album|track|artist"
"|playlist|label)/|[a-z]{2}-[a-z]{2}/album/-?\w+(?:-\w+)*-?/|user/" r"|playlist|label)/|[a-z]{2}-[a-z]{2}/album/-?\w+(?:-\w+)*-?/|user/"
"library/favorites/)(\w+)", r"library/favorites/)(\w+)",
url, url,
).group(1) ).group(1)
@ -122,23 +127,21 @@ class QobuzDL:
type_dict = possibles[url_type] type_dict = possibles[url_type]
item_id = self.get_id(url) item_id = self.get_id(url)
except (KeyError, IndexError): except (KeyError, IndexError):
print( logger.info(
'Invalid url: "{}". Use urls from https://play.qobuz.com!'.format(url) f'{RED}Invalid url: "{url}". Use urls from https://play.qobuz.com!'
) )
return return
if type_dict["func"]: if type_dict["func"]:
content = [item for item in type_dict["func"](item_id)] content = [item for item in type_dict["func"](item_id)]
content_name = content[0]["name"] content_name = content[0]["name"]
print( logger.info(
"\nDownloading all the music from {} ({})!".format( f"{YELLOW}Downloading all the music from {content_name} ({url_type})!"
content_name, url_type
)
) )
new_path = self.create_dir( new_path = self.create_dir(
os.path.join(self.directory, sanitize_filename(content_name)) os.path.join(self.directory, sanitize_filename(content_name))
) )
items = [item[type_dict["iterable_key"]]["items"] for item in content][0] items = [item[type_dict["iterable_key"]]["items"] for item in content][0]
print("{} downloads in queue".format(len(items))) logger.info(f"{YELLOW}{len(items)} downloads in queue")
for item in items: for item in items:
self.download_from_id( self.download_from_id(
item["id"], item["id"],
@ -152,7 +155,7 @@ class QobuzDL:
def download_list_of_urls(self, urls): def download_list_of_urls(self, urls):
if not urls or not isinstance(urls, list): if not urls or not isinstance(urls, list):
print("Nothing to download") logger.info(f"{OFF}Nothing to download")
return return
for url in urls: for url in urls:
if "last.fm" in url: if "last.fm" in url:
@ -167,24 +170,21 @@ class QobuzDL:
try: try:
urls = txt.read().strip().split() urls = txt.read().strip().split()
except Exception as e: except Exception as e:
print("Invalid text file: " + str(e)) logger.error(f"{RED}Invalid text file: {e}")
return return
print( logger.info(
'qobuz-dl will download {} urls from file: "{}"\n'.format( f'{YELLOW}qobuz-dl will download {len(urls)} urls from file: "{txt_file}"'
len(urls), txt_file
)
) )
self.download_list_of_urls(urls) self.download_list_of_urls(urls)
def lucky_mode(self, query, download=True): def lucky_mode(self, query, download=True):
if len(query) < 3: if len(query) < 3:
sys.exit("Your search query is too short or invalid!") logger.info(f"{RED}Your search query is too short or invalid")
return
print( logger.info(
'Searching {}s for "{}".\n' f'{YELLOW}Searching {self.lucky_type}s for "{query}".\n'
"qobuz-dl will attempt to download the first {} results.".format( f"{YELLOW}qobuz-dl will attempt to download the first {self.lucky_limit} results."
self.lucky_type, query, self.lucky_limit
)
) )
results = self.search_by_type(query, self.lucky_type, self.lucky_limit, True) results = self.search_by_type(query, self.lucky_type, self.lucky_limit, True)
@ -198,7 +198,7 @@ class QobuzDL:
def search_by_type(self, query, item_type, limit=10, lucky=False): def search_by_type(self, query, item_type, limit=10, lucky=False):
if len(query) < 3: if len(query) < 3:
print("Your search query is too short or invalid!") logger.info("{RED}Your search query is too short or invalid")
return return
possibles = { possibles = {
@ -252,7 +252,7 @@ class QobuzDL:
item_list.append({"text": text, "url": url} if not lucky else url) item_list.append({"text": text, "url": url} if not lucky else url)
return item_list return item_list
except (KeyError, IndexError): except (KeyError, IndexError):
print("Invalid mode: " + item_type) logger.info(f"{RED}Invalid type: {item_type}")
return return
def interactive(self, download=True): def interactive(self, download=True):
@ -260,8 +260,9 @@ class QobuzDL:
from pick import pick from pick import pick
except (ImportError, ModuleNotFoundError): except (ImportError, ModuleNotFoundError):
if os.name == "nt": if os.name == "nt":
print('Please install curses with "pip3 install windows-curses"') sys.exit(
return 'Please install curses with "pip3 install windows-curses" to continue'
)
raise raise
qualities = [ qualities = [
@ -282,22 +283,22 @@ class QobuzDL:
selected_type = pick(item_types, "I'll search for:\n[press Intro]")[0][ selected_type = pick(item_types, "I'll search for:\n[press Intro]")[0][
:-1 :-1
].lower() ].lower()
print("Ok, we'll search for " + selected_type + "s") logger.info(f"{YELLOW}Ok, we'll search for {selected_type}s")
final_url_list = [] final_url_list = []
while True: while True:
query = input("\nEnter your search: [Ctrl + c to quit]\n- ") query = input(f"{MAGENTA}Enter your search: [Ctrl + c to quit]\n-{DF} ")
print("Searching...") logger.info(f"{YELLOW}Searching...")
options = self.search_by_type( options = self.search_by_type(
query, selected_type, self.interactive_limit query, selected_type, self.interactive_limit
) )
if not options: if not options:
print("Nothing found!") logger.info(f"{OFF}Nothing found")
continue continue
title = ( title = (
'*** RESULTS FOR "{}" ***\n\n' f'*** RESULTS FOR "{query.title()}" ***\n\n'
"Select [space] the item(s) you want to download " "Select [space] the item(s) you want to download "
"(one or more)\nPress Ctrl + c to quit\n" "(one or more)\nPress Ctrl + c to quit\n"
"Don't select anything to try another search".format(query.title()) "Don't select anything to try another search"
) )
selected_items = pick( selected_items = pick(
options, options,
@ -315,12 +316,12 @@ class QobuzDL:
if y_n[0][0] == "N": if y_n[0][0] == "N":
break break
else: else:
print("\nOk, try again...") logger.info(f"{YELLOW}Ok, try again...")
continue continue
if final_url_list: if final_url_list:
desc = ( desc = (
"Select [intro] the quality (the quality will be automat" "Select [intro] the quality (the quality will "
"ically\ndowngraded if the selected is not found)" "be automatically\ndowngraded if the selected is not found)"
) )
self.quality = pick( self.quality = pick(
qualities, qualities,
@ -334,29 +335,36 @@ class QobuzDL:
return final_url_list return final_url_list
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nBye") logger.info(f"{YELLOW}Bye")
return return
def download_lastfm_pl(self, playlist_url): def download_lastfm_pl(self, playlist_url):
# Apparently, last fm API doesn't have a playlist endpoint. If you # Apparently, last fm API doesn't have a playlist endpoint. If you
# find out that it has, please fix this! # find out that it has, please fix this!
r = requests.get(playlist_url) try:
r = requests.get(playlist_url, timeout=10)
except requests.exceptions.RequestException as e:
logger.error(f"{RED}Playlist download failed: {e}")
return
soup = bso(r.content, "html.parser") soup = bso(r.content, "html.parser")
artists = [artist.text for artist in soup.select(ARTISTS_SELECTOR)] artists = [artist.text for artist in soup.select(ARTISTS_SELECTOR)]
titles = [title.text for title in soup.select(TITLE_SELECTOR)] titles = [title.text for title in soup.select(TITLE_SELECTOR)]
track_list = []
if len(artists) == len(titles) and artists: if len(artists) == len(titles) and artists:
track_list = [ track_list = [
artist + " " + title for artist, title in zip(artists, titles) artist + " " + title for artist, title in zip(artists, titles)
] ]
if not track_list: if not track_list:
print("Nothing found") logger.info(f"{OFF}Nothing found")
return return
pl_title = sanitize_filename(soup.select_one("h1").text) pl_title = sanitize_filename(soup.select_one("h1").text)
pl_directory = os.path.join(self.directory, pl_title) pl_directory = os.path.join(self.directory, pl_title)
print("Downloading playlist: {} ({} tracks)".format(pl_title, len(track_list))) logger.info(
f"{YELLOW}Downloading playlist: {pl_title} ({len(track_list)} tracks)"
)
for i in track_list: for i in track_list:
track_id = self.get_id(self.search_by_type(i, "track", 1, lucky=True)[0]) track_id = self.get_id(self.search_by_type(i, "track", 1, lucky=True)[0])

View File

@ -1,3 +1,4 @@
import logging
import os import os
import requests import requests
@ -5,8 +6,10 @@ from pathvalidate import sanitize_filename
from tqdm import tqdm from tqdm import tqdm
import qobuz_dl.metadata as metadata import qobuz_dl.metadata as metadata
from qobuz_dl.color import OFF, GREEN, RED, YELLOW, CYAN
QL_DOWNGRADE = "FormatRestrictedByFormatAvailability" QL_DOWNGRADE = "FormatRestrictedByFormatAvailability"
logger = logging.getLogger(__name__)
def tqdm_download(url, fname, track_name): def tqdm_download(url, fname, track_name):
@ -18,7 +21,7 @@ def tqdm_download(url, fname, track_name):
unit_scale=True, unit_scale=True,
unit_divisor=1024, unit_divisor=1024,
desc=track_name, desc=track_name,
bar_format="{n_fmt}/{total_fmt} /// {desc}", bar_format=CYAN + "{n_fmt}/{total_fmt} /// {desc}",
) as bar: ) as bar:
for data in r.iter_content(chunk_size=1024): for data in r.iter_content(chunk_size=1024):
size = file.write(data) size = file.write(data)
@ -87,12 +90,12 @@ def get_title(item_dict):
def get_extra(i, dirn, extra="cover.jpg"): def get_extra(i, dirn, extra="cover.jpg"):
extra_file = os.path.join(dirn, extra) extra_file = os.path.join(dirn, extra)
if os.path.isfile(extra_file): if os.path.isfile(extra_file):
print(extra.split(".")[0].title() + " already downloaded") logger.info(f"{OFF}{extra} was already downloaded")
return return
tqdm_download( tqdm_download(
i.replace("_600.", "_org."), i.replace("_600.", "_org."),
extra_file, extra_file,
"Downloading " + extra.split(".")[0], extra,
) )
@ -127,7 +130,7 @@ def download_and_tag(
try: try:
url = track_url_dict["url"] url = track_url_dict["url"]
except KeyError: except KeyError:
print("Track not available for download") logger.info(f"{OFF}Track not available for download")
return return
if multiple: if multiple:
@ -142,7 +145,7 @@ def download_and_tag(
) )
final_file = os.path.join(root_dir, track_file) final_file = os.path.join(root_dir, track_file)
if os.path.isfile(final_file): if os.path.isfile(final_file):
print(track_metadata["title"] + " was already downloaded. Skipping...") logger.info(f'{OFF}{track_metadata["title"]}was already downloaded')
return return
desc = get_description(track_url_dict, track_metadata, multiple) desc = get_description(track_url_dict, track_metadata, multiple)
@ -159,8 +162,7 @@ def download_and_tag(
embed_art, embed_art,
) )
except Exception as e: except Exception as e:
print("Error tagging the file: " + str(e)) logger.error(f"{RED}Error tagging the file: {e}")
os.remove(filename)
def download_id_by_type( def download_id_by_type(
@ -194,16 +196,18 @@ def download_id_by_type(
meta.get("release_type") != "album" meta.get("release_type") != "album"
or meta.get("artist").get("name") == "Various Artists" or meta.get("artist").get("name") == "Various Artists"
): ):
print("Ignoring Single/EP/VA: " + meta.get("title", "")) logger.info(f'{OFF}Ignoring Single/EP/VA: {meta.get("title", "")}')
return return
album_title = get_title(meta) album_title = get_title(meta)
album_format, quality_met = get_format(client, meta, quality) album_format, quality_met = get_format(client, meta, quality)
if not downgrade_quality and not quality_met: if not downgrade_quality and not quality_met:
print("Skipping release as doesn't met quality requirement") logger.info(
f"{OFF}Skipping {album_title} as doesn't met quality requirement"
)
return return
print("\nDownloading: {}\n".format(album_title)) logger.info(f"\n{YELLOW}Downloading: {album_title} [{album_format}]\n")
dirT = ( dirT = (
meta["artist"]["name"], meta["artist"]["name"],
album_title, album_title,
@ -225,7 +229,7 @@ def download_id_by_type(
try: try:
parse = client.get_track_url(i["id"], quality) parse = client.get_track_url(i["id"], quality)
except requests.exceptions.HTTPError: except requests.exceptions.HTTPError:
print("Nothing found") logger.info(f"{OFF}Nothing found")
continue continue
if "sample" not in parse and parse["sampling_rate"]: if "sample" not in parse and parse["sampling_rate"]:
is_mp3 = True if int(quality) == 5 else False is_mp3 = True if int(quality) == 5 else False
@ -241,22 +245,24 @@ def download_id_by_type(
i["media_number"] if is_multiple else None, i["media_number"] if is_multiple else None,
) )
else: else:
print("Demo. Skipping") logger.info(f"{OFF}Demo. Skipping")
count = count + 1 count = count + 1
else: else:
try: try:
parse = client.get_track_url(item_id, quality) parse = client.get_track_url(item_id, quality)
except requests.exceptions.HTTPError: except requests.exceptions.HTTPError:
print("Nothing found") logger.info(f"{OFF}Nothing found")
return return
if "sample" not in parse and parse["sampling_rate"]: if "sample" not in parse and parse["sampling_rate"]:
meta = client.get_track_meta(item_id) meta = client.get_track_meta(item_id)
track_title = get_title(meta) track_title = get_title(meta)
print("\nDownloading: {}\n".format(track_title)) logger.info(f"\n{YELLOW}Downloading: {track_title}")
track_format, quality_met = get_format(client, meta, quality, True, parse) track_format, quality_met = get_format(client, meta, quality, True, parse)
if not downgrade_quality and not quality_met: if not downgrade_quality and not quality_met:
print("Skipping track as doesn't met quality requirement") logger.info(
f"{OFF}Skipping {track_title} as doesn't met quality requirement"
)
return return
dirT = ( dirT = (
meta["album"]["artist"]["name"], meta["album"]["artist"]["name"],
@ -271,5 +277,5 @@ def download_id_by_type(
is_mp3 = True if int(quality) == 5 else False is_mp3 = True if int(quality) == 5 else False
download_and_tag(dirn, count, parse, meta, meta, True, is_mp3, embed_art) download_and_tag(dirn, count, parse, meta, meta, True, is_mp3, embed_art)
else: else:
print("Demo. Skipping") logger.info(f"{OFF}Demo. Skipping")
print("\nCompleted\n") logger.info(f"{GREEN}Completed")

View File

@ -12,3 +12,7 @@ class InvalidAppIdError(Exception):
class InvalidAppSecretError(Exception): class InvalidAppSecretError(Exception):
pass pass
class InvalidQuality(Exception):
pass

View File

@ -1,8 +1,11 @@
import os import os
import logging
from mutagen.flac import FLAC, Picture from mutagen.flac import FLAC, Picture
from mutagen.mp3 import EasyMP3 from mutagen.mp3 import EasyMP3
logger = logging.getLogger(__name__)
def get_title(track_dict): def get_title(track_dict):
try: try:
@ -38,6 +41,7 @@ def tag_flac(filename, root_dir, final_name, d, album, istrack=True, em_image=Fa
audio["TITLE"] = get_title(d) audio["TITLE"] = get_title(d)
audio["TRACKNUMBER"] = str(d["track_number"]) # TRACK NUMBER audio["TRACKNUMBER"] = str(d["track_number"]) # TRACK NUMBER
audio["DISCNUMBER"] = str(d["media_number"])
try: try:
audio["COMPOSER"] = d["composer"]["name"] # COMPOSER audio["COMPOSER"] = d["composer"]["name"] # COMPOSER
@ -80,7 +84,7 @@ def tag_flac(filename, root_dir, final_name, d, album, istrack=True, em_image=Fa
image.data = img.read() image.data = img.read()
audio.add_picture(image) audio.add_picture(image)
except Exception as e: except Exception as e:
print("Error embedding image: " + str(e)) logger.error(f"Error embedding image: {e}", exc_info=True)
audio.save() audio.save()
os.rename(filename, final_name) os.rename(filename, final_name)

View File

@ -3,6 +3,7 @@
# original author. # original author.
import hashlib import hashlib
import logging
import time import time
import requests import requests
@ -12,14 +13,18 @@ from qobuz_dl.exceptions import (
IneligibleError, IneligibleError,
InvalidAppIdError, InvalidAppIdError,
InvalidAppSecretError, InvalidAppSecretError,
InvalidQuality,
) )
from qobuz_dl.color import GREEN, YELLOW
RESET = "Reset your credentials with 'qobuz-dl -r'" RESET = "Reset your credentials with 'qobuz-dl -r'"
logger = logging.getLogger(__name__)
class Client: class Client:
def __init__(self, email, pwd, app_id, secrets): def __init__(self, email, pwd, app_id, secrets):
print("Logging...") logger.info(f"{YELLOW}Logging...")
self.secrets = secrets self.secrets = secrets
self.id = app_id self.id = app_id
self.session = requests.Session() self.session = requests.Session()
@ -80,6 +85,8 @@ class Client:
unix = time.time() unix = time.time()
track_id = kwargs["id"] track_id = kwargs["id"]
fmt_id = kwargs["fmt_id"] fmt_id = kwargs["fmt_id"]
if int(fmt_id) not in (5, 6, 7, 27):
raise InvalidQuality("Invalid quality id: choose between 5, 6, 7 or 27")
r_sig = "trackgetFileUrlformat_id{}intentstreamtrack_id{}{}{}".format( r_sig = "trackgetFileUrlformat_id{}intentstreamtrack_id{}{}{}".format(
fmt_id, track_id, unix, self.sec fmt_id, track_id, unix, self.sec
) )
@ -94,14 +101,13 @@ class Client:
else: else:
params = kwargs params = kwargs
r = self.session.get(self.base + epoint, params=params) r = self.session.get(self.base + epoint, params=params)
# Do ref header.
if epoint == "user/login": if epoint == "user/login":
if r.status_code == 401: if r.status_code == 401:
raise AuthenticationError("Invalid credentials.\n" + RESET) raise AuthenticationError("Invalid credentials.\n" + RESET)
elif r.status_code == 400: elif r.status_code == 400:
raise InvalidAppIdError("Invalid app id.\n" + RESET) raise InvalidAppIdError("Invalid app id.\n" + RESET)
else: else:
print("Logged: OK") logger.info(f"{GREEN}Logged: OK")
elif epoint in ["track/getFileUrl", "userLibrary/getAlbumsList"]: elif epoint in ["track/getFileUrl", "userLibrary/getAlbumsList"]:
if r.status_code == 400: if r.status_code == 400:
raise InvalidAppSecretError("Invalid app secret.\n" + RESET) raise InvalidAppSecretError("Invalid app secret.\n" + RESET)
@ -115,7 +121,7 @@ class Client:
self.uat = usr_info["user_auth_token"] self.uat = usr_info["user_auth_token"]
self.session.headers.update({"X-User-Auth-Token": self.uat}) self.session.headers.update({"X-User-Auth-Token": self.uat})
self.label = usr_info["user"]["credential"]["parameters"]["short_label"] self.label = usr_info["user"]["credential"]["parameters"]["short_label"]
print("Membership: {}\n".format(self.label)) logger.info(f"{GREEN}Membership: {self.label}")
def multi_meta(self, epoint, key, id, type): def multi_meta(self, epoint, key, id, type):
total = 1 total = 1

View File

@ -4,3 +4,4 @@ mutagen
tqdm tqdm
pick pick
beautifulsoup4 beautifulsoup4
colorama

View File

@ -13,7 +13,7 @@ requirements = read_file("requirements.txt").strip().split()
setup( setup(
name=pkg_name, name=pkg_name,
version="0.6.0", version="0.7.0",
author="Vitiko", author="Vitiko",
author_email="vhnz98@gmail.com", author_email="vhnz98@gmail.com",
description="The complete Lossless and Hi-Res music downloader for Qobuz", description="The complete Lossless and Hi-Res music downloader for Qobuz",