diff --git a/main.py b/main.py index 6d5485f..ad43a1b 100644 --- a/main.py +++ b/main.py @@ -1,35 +1,46 @@ -from qo_utils.search import Search -from qo_utils import downloader -from pick import pick import argparse import itertools -import re -import os -import sys import json +import os +import re +import sys + +from pick import pick + import qopy +from qo_utils import downloader +from qo_utils.search import Search def getArgs(): - parser = argparse.ArgumentParser(prog='python3 main.py') - parser.add_argument("-a", action="store_true", - help="enable albums-only search") - parser.add_argument("-i", action="store_true", - help="run Qo-Dl-curses on URL input mode") - parser.add_argument("-q", metavar="int", default=6, - help="quality (5, 6, 7, 27) (default: 6)") - parser.add_argument("-l", metavar="int", default=10, - help="limit of search results by type (default: 10)") - parser.add_argument("-d", metavar="PATH", default='Qobuz Downloads', - help="custom directory for downloads") + parser = argparse.ArgumentParser(prog="python3 main.py") + parser.add_argument("-a", action="store_true", help="enable albums-only search") + parser.add_argument( + "-i", action="store_true", help="run Qo-Dl-curses on URL input mode" + ) + parser.add_argument( + "-q", metavar="int", default=6, help="quality (5, 6, 7, 27) (default: 6)" + ) + parser.add_argument( + "-l", + metavar="int", + default=10, + help="limit of search results by type (default: 10)", + ) + parser.add_argument( + "-d", + metavar="PATH", + default="Qobuz Downloads", + help="custom directory for downloads", + ) return parser.parse_args() def getSession(): - print('Logging...') - with open('config.json') as f: + print("Logging...") + with open("config.json") as f: config = json.load(f) - return qopy.Client(config['email'], config['password']) + return qopy.Client(config["email"], config["password"]) def musicDir(dir): @@ -40,13 +51,16 @@ def musicDir(dir): def get_id(url): - return re.match(r'https?://(?:w{0,3}|play|open)\.qobuz\.com/(?:(?' - ':album|track)/|[a-z]{2}-[a-z]{2}/album/-?\w+(?:-\w+)' - '*-?/|user/library/favorites/)(\w+)', url).group(1) + return re.match( + r"https?://(?:w{0,3}|play|open)\.qobuz\.com/(?:(?" + ":album|track)/|[a-z]{2}-[a-z]{2}/album/-?\w+(?:-\w+)" + "*-?/|user/library/favorites/)(\w+)", + url, + ).group(1) def searchSelected(Qz, path, albums, ids, types, quality): - q = ['5', '6', '7', '27'] + q = ["5", "6", "7", "27"] quality = q[quality[1]] for alb, id_, type_ in zip(albums, ids, types): for al in alb: @@ -57,7 +71,7 @@ def searchSelected(Qz, path, albums, ids, types, quality): def fromUrl(Qz, path, link, quality): - if '/track/' in link: + if "/track/" in link: id = get_id(link) downloader.iterateIDs(Qz, id, path, quality, False) else: @@ -71,32 +85,37 @@ def interactive(Qz, path, limit, tracks=True): try: while True: query = input("\nEnter your search: [Ctrl + c to quit]\n- ") - print('Searching...') + print("Searching...") start = Search(Qz, query, limit) start.getResults(tracks) Types.append(start.Types) IDs.append(start.IDs) - title = ('Select [space] the item(s) you want to download ' - '(one or more)\nPress Ctrl + c to quit\n') - Selected = pick(start.Total, title, - multiselect=True, min_selection_count=1) + title = ( + "Select [space] the item(s) you want to download " + "(one or more)\nPress Ctrl + c to quit\n" + ) + Selected = pick( + start.Total, title, multiselect=True, min_selection_count=1 + ) Albums.append(Selected) - y_n = pick(['Yes', 'No'], 'Items were added to queue to ' - 'be downloaded. Keep searching?') - if y_n[0][0] == 'N': + y_n = pick( + ["Yes", "No"], + "Items were added to queue to be downloaded. Keep searching?", + ) + if y_n[0][0] == "N": break - else: - pass - desc = ('Select [intro] the quality (the quality will be automat' - 'ically\ndowngraded if the selected is not found)') - Qualits = ['320', 'Lossless', 'Hi-res =< 96kHz', 'Hi-Res > 96 kHz'] + desc = ( + "Select [intro] the quality (the quality will be automat" + "ically\ndowngraded if the selected is not found)" + ) + Qualits = ["320", "Lossless", "Hi-res =< 96kHz", "Hi-Res > 96 kHz"] quality = pick(Qualits, desc) searchSelected(Qz, path, Albums, IDs, Types, quality) except KeyboardInterrupt: - sys.exit('\nBye') + sys.exit("\nBye") def inputMode(Qz, path, quality): @@ -105,18 +124,15 @@ def inputMode(Qz, path, quality): link = input("\nAlbum/track URL: [Ctrl + c to quit]\n- ") fromUrl(Qz, path, link, quality) except KeyboardInterrupt: - sys.exit('\nBye') + sys.exit("\nBye") def main(): arguments = getArgs() - directory = musicDir(arguments.d) + '/' + directory = musicDir(arguments.d) + "/" Qz = getSession() if not arguments.i: - if arguments.a: - interactive(Qz, directory, arguments.l, False) - else: - interactive(Qz, directory, arguments.l, True) + interactive(Qz, directory, arguments.l, not arguments.a) else: inputMode(Qz, directory, arguments.q) diff --git a/qo_utils/downloader.py b/qo_utils/downloader.py index 0d997c1..ada43fb 100644 --- a/qo_utils/downloader.py +++ b/qo_utils/downloader.py @@ -1,20 +1,22 @@ import os + import requests -from qo_utils import metadata from pathvalidate import sanitize_filename from tqdm import tqdm +from qo_utils import metadata + def req_tqdm(url, fname, track_name): r = requests.get(url, allow_redirects=True, stream=True) - total = int(r.headers.get('content-length', 0)) - with open(fname, 'wb') as file, tqdm( + total = int(r.headers.get("content-length", 0)) + with open(fname, "wb") as file, tqdm( total=total, - unit='iB', + unit="iB", unit_scale=True, unit_divisor=1024, desc=track_name, - bar_format='{n_fmt}/{total_fmt} /// {desc}', + bar_format="{n_fmt}/{total_fmt} /// {desc}", ) as bar: for data in r.iter_content(chunk_size=1024): size = file.write(data) @@ -25,25 +27,25 @@ def mkDir(dirn): try: os.mkdir(dirn) except FileExistsError: - print('Warning: folder already exists. Overwriting...') + print("Warning: folder already exists. Overwriting...") def getDesc(u, mt): - return '{} [{}/{}]'.format(mt['title'], u['bit_depth'], u['sampling_rate']) + return "{} [{}/{}]".format(mt["title"], u["bit_depth"], u["sampling_rate"]) def getCover(i, dirn): - req_tqdm(i, dirn + '/cover.jpg', 'Downloading cover art') + req_tqdm(i, dirn + "/cover.jpg", "Downloading cover art") # Download and tag a file def downloadItem(dirn, count, parse, meta, album, url, is_track, mp3): - if mp3: - fname = '{}/{:02}.mp3'.format(dirn, count) - func = metadata.tag_mp3 - else: - fname = '{}/{:02}.flac'.format(dirn, count) - func = metadata.tag_flac + fname = ( + "{}/{:02}.mp3".format(dirn, count) + if mp3 + else "{}/{:02}.flac".format(dirn, count) + ) + func = metadata.tag_mp3 if mp3 else metadata.tag_flac desc = getDesc(parse, meta) req_tqdm(url, fname, desc) func(fname, dirn, meta, album, is_track) @@ -55,46 +57,43 @@ def iterateIDs(client, id, path, quality, album=False): if album: meta = client.get_album_meta(id) - print('\nDownloading: {}\n'.format(meta['title'])) - dirT = (meta['artist']['name'], - meta['title'], - meta['release_date_original'].split('-')[0]) - sanitized_title = sanitize_filename('{} - {} [{}]'.format(*dirT)) + print("\nDownloading: {}\n".format(meta["title"])) + dirT = ( + meta["artist"]["name"], + meta["title"], + meta["release_date_original"].split("-")[0], + ) + sanitized_title = sanitize_filename("{} - {} [{}]".format(*dirT)) dirn = path + sanitized_title mkDir(dirn) - getCover(meta['image']['large'], dirn) - for i in meta['tracks']['items']: - parse = client.get_track_url(i['id'], quality) - url = parse['url'] - - if 'sample' not in parse: - if int(quality) == 5: - downloadItem(dirn, count, parse, i, meta, url, False, True) - else: - downloadItem(dirn, count, parse, i, meta, url, False, False) + getCover(meta["image"]["large"], dirn) + for i in meta["tracks"]["items"]: + parse = client.get_track_url(i["id"], quality) + url = parse["url"] + if "sample" not in parse: + is_mp3 = True if int(quality) == 5 else False + downloadItem(dirn, count, parse, i, meta, url, False, is_mp3) else: - print('Demo. Skipping') - + print("Demo. Skipping") count = count + 1 else: parse = client.get_track_url(id, quality) - url = parse['url'] + url = parse["url"] - if 'sample' not in parse: + if "sample" not in parse: meta = client.get_track_meta(id) - print('\nDownloading: {}\n'.format(meta['title'])) - dirT = (meta['album']['artist']['name'], - meta['title'], - meta['album']['release_date_original'].split('-')[0]) - sanitized_title = sanitize_filename('{} - {} [{}]'.format(*dirT)) + print("\nDownloading: {}\n".format(meta["title"])) + dirT = ( + meta["album"]["artist"]["name"], + meta["title"], + meta["album"]["release_date_original"].split("-")[0], + ) + sanitized_title = sanitize_filename("{} - {} [{}]".format(*dirT)) dirn = path + sanitized_title mkDir(dirn) - getCover(meta['album']['image']['large'], dirn) - if int(quality) == 5: - downloadItem(dirn, count, parse, meta, meta, url, True, True) - else: - downloadItem(dirn, count, parse, meta, meta, url, True, False) + getCover(meta["album"]["image"]["large"], dirn) + is_mp3 = True if int(quality) == 5 else False + downloadItem(dirn, count, parse, meta, meta, url, True, is_mp3) else: - print('Demo. Skipping') - - print('\nCompleted\n') + print("Demo. Skipping") + print("\nCompleted\n") diff --git a/qo_utils/metadata.py b/qo_utils/metadata.py index f3b51fc..45c75ad 100644 --- a/qo_utils/metadata.py +++ b/qo_utils/metadata.py @@ -1,73 +1,74 @@ +import os + from mutagen.flac import FLAC from mutagen.mp3 import EasyMP3 from pathvalidate import sanitize_filename -import os def tag_flac(file, path, d, album, istrack=True): audio = FLAC(file) - audio['TITLE'] = d['title'] # TRACK TITLE - audio['TRACKNUMBER'] = str(d['track_number']) # TRACK NUMBER + audio["TITLE"] = d["title"] # TRACK TITLE + audio["TRACKNUMBER"] = str(d["track_number"]) # TRACK NUMBER try: - audio['COMPOSER'] = d['composer']['name'] # COMPOSER + audio["COMPOSER"] = d["composer"]["name"] # COMPOSER except KeyError: pass try: - audio['ARTIST'] = d['performer']['name'] # TRACK ARTIST + audio["ARTIST"] = d["performer"]["name"] # TRACK ARTIST except KeyError: if istrack: - audio['ARTIST'] = d['album']['artist']['name'] # TRACK ARTIST + audio["ARTIST"] = d["album"]["artist"]["name"] # TRACK ARTIST else: - audio['ARTIST'] = album['artist']['name'] + audio["ARTIST"] = album["artist"]["name"] if istrack: - audio['GENRE'] = ', '.join(d['album']['genres_list']) # GENRE - audio['ALBUMARTIST'] = d['album']['artist']['name'] # ALBUM ARTIST - audio['TRACKTOTAL'] = str(d['album']['tracks_count']) # TRACK TOTAL - audio['ALBUM'] = d['album']['title'] # ALBUM TITLE - audio['YEAR'] = d['album']['release_date_original'].split('-')[0] + audio["GENRE"] = ", ".join(d["album"]["genres_list"]) # GENRE + audio["ALBUMARTIST"] = d["album"]["artist"]["name"] # ALBUM ARTIST + audio["TRACKTOTAL"] = str(d["album"]["tracks_count"]) # TRACK TOTAL + audio["ALBUM"] = d["album"]["title"] # ALBUM TITLE + audio["YEAR"] = d["album"]["release_date_original"].split("-")[0] else: - audio['GENRE'] = ', '.join(album['genres_list']) # GENRE - audio['ALBUMARTIST'] = album['artist']['name'] # ALBUM ARTIST - audio['TRACKTOTAL'] = str(album['tracks_count']) # TRACK TOTAL - audio['ALBUM'] = album['title'] # ALBUM TITLE - audio['YEAR'] = album['release_date_original'].split('-')[0] # YEAR + audio["GENRE"] = ", ".join(album["genres_list"]) # GENRE + audio["ALBUMARTIST"] = album["artist"]["name"] # ALBUM ARTIST + audio["TRACKTOTAL"] = str(album["tracks_count"]) # TRACK TOTAL + audio["ALBUM"] = album["title"] # ALBUM TITLE + audio["YEAR"] = album["release_date_original"].split("-")[0] # YEAR audio.save() - title = sanitize_filename(d['title']) - os.rename(file, '{}/{:02}. {}.flac'.format(path, d['track_number'], title)) + title = sanitize_filename(d["title"]) + os.rename(file, "{}/{:02}. {}.flac".format(path, d["track_number"], title)) def tag_mp3(file, path, d, album, istrack=True): audio = EasyMP3(file) - audio['title'] = d['title'] - audio['tracknumber'] = str(d['track_number']) + audio["title"] = d["title"] + audio["tracknumber"] = str(d["track_number"]) try: - audio['composer'] = d['composer']['name'] + audio["composer"] = d["composer"]["name"] except KeyError: pass try: - audio['artist'] = d['performer']['name'] # TRACK ARTIST + audio["artist"] = d["performer"]["name"] # TRACK ARTIST except KeyError: if istrack: - audio['artist'] = d['album']['artist']['name'] # TRACK ARTIST + audio["artist"] = d["album"]["artist"]["name"] # TRACK ARTIST else: - audio['artist'] = album['artist']['name'] + audio["artist"] = album["artist"]["name"] if istrack: - audio['genre'] = ', '.join(d['album']['genres_list']) # GENRE - audio['albumartist'] = d['album']['artist']['name'] # ALBUM ARTIST - audio['album'] = d['album']['title'] # ALBUM TITLE - audio['date'] = d['album']['release_date_original'].split('-')[0] + audio["genre"] = ", ".join(d["album"]["genres_list"]) # GENRE + audio["albumartist"] = d["album"]["artist"]["name"] # ALBUM ARTIST + audio["album"] = d["album"]["title"] # ALBUM TITLE + audio["date"] = d["album"]["release_date_original"].split("-")[0] else: - audio['GENRE'] = ', '.join(album['genres_list']) # GENRE - audio['albumartist'] = album['artist']['name'] # ALBUM ARTIST - audio['album'] = album['title'] # ALBUM TITLE - audio['date'] = album['release_date_original'].split('-')[0] # YEAR + audio["GENRE"] = ", ".join(album["genres_list"]) # GENRE + audio["albumartist"] = album["artist"]["name"] # ALBUM ARTIST + audio["album"] = album["title"] # ALBUM TITLE + audio["date"] = album["release_date_original"].split("-")[0] # YEAR audio.save() - title = sanitize_filename(d['title']) - os.rename(file, '{}/{:02}. {}.mp3'.format(path, d['track_number'], title)) + title = sanitize_filename(d["title"]) + os.rename(file, "{}/{:02}. {}.mp3".format(path, d["track_number"], title)) diff --git a/qo_utils/search.py b/qo_utils/search.py index 39757a6..aab0e04 100644 --- a/qo_utils/search.py +++ b/qo_utils/search.py @@ -6,33 +6,35 @@ class Search: self.Total = [] self.IDs = [] self.Types = [] - self.Tracks = Qz.search_tracks(query, limit)['tracks']['items'] - self.Albums = Qz.search_albums(query, limit)['albums']['items'] + self.Tracks = Qz.search_tracks(query, limit)["tracks"]["items"] + self.Albums = Qz.search_albums(query, limit)["albums"]["items"] def seconds(self, duration): - return time.strftime("%M:%S", time.gmtime(duration)) - - def isHRes(self, item): - if item: - return 'HI-RES' - else: - return 'Lossless' + return time.strftime("%H:%M:%S", time.gmtime(duration)) def appendInfo(self, i, bool): - self.IDs.append(i['id']) + self.IDs.append(i["id"]) self.Types.append(bool) def itResults(self, iterable): for i in iterable: try: - items = (i['artist']['name'], i['title'], - self.seconds(i['duration']), self.isHRes(i['hires'])) - self.Total.append('[RELEASE] {} - {} - {} [{}]'.format(*items)) + items = ( + i["artist"]["name"], + i["title"], + self.seconds(i["duration"]), + "HI-RES" if i["hires"] else "Lossless", + ) + self.Total.append("[RELEASE] {} - {} - {} [{}]".format(*items)) self.appendInfo(i, True) except KeyError: - items = (i['performer']['name'], i['title'], - self.seconds(i['duration']), self.isHRes(i['hires'])) - self.Total.append('[TRACK] {} - {} - {} [{}]'.format(*items)) + items = ( + i["performer"]["name"], + i["title"], + self.seconds(i["duration"]), + "HI-RES" if i["hires"] else "Lossless", + ) + self.Total.append("[TRACK] {} - {} - {} [{}]".format(*items)) self.appendInfo(i, False) def getResults(self, tracks=False): diff --git a/qo_utils/spoofbuz.py b/qo_utils/spoofbuz.py index e470868..624d9cf 100644 --- a/qo_utils/spoofbuz.py +++ b/qo_utils/spoofbuz.py @@ -1,23 +1,26 @@ -import requests import base64 import re from collections import OrderedDict -class Spoofer: +import requests + +class Spoofer: def __init__(self): self.seed_timezone_regex = r'[a-z]\.initialSeed\("(?P[\w=]+)",window\.utimezone\.(?P[a-z]+)\)' # note: {timezones} should be replaced with every capitalized timezone joined by a | - self.info_extras_regex = r'name:"\w+/(?P{timezones})",info:"(?P[\w=]+)",extras:"(?P[\w=]+)"' + self.info_extras_regex = r'name:"\w+/(?P{timezones})",info:"(?P[\w=]+)",extras:"(?P[\w=]+)"' self.appId_regex = r'{app_id:"(?P\d{9})",app_secret:"\w{32}",base_port:"80",base_url:"https://www\.qobuz\.com",base_method:"/api\.json/0\.2/"},n\.base_url="https://play\.qobuz\.com"' login_page_request = requests.get("https://play.qobuz.com/login") login_page = login_page_request.text - bundle_url_match = re.search(r'', - login_page) + bundle_url_match = re.search( + r'', + login_page, + ) bundle_url = bundle_url_match.group(1) bundle_req = requests.get("https://play.qobuz.com" + bundle_url) self.bundle = bundle_req.text - + def getAppId(self): return re.search(self.appId_regex, self.bundle).group("app_id") @@ -34,7 +37,9 @@ class Spoofer: """ keypairs = list(secrets.items()) secrets.move_to_end(keypairs[1][0], last=False) - info_extras_regex = self.info_extras_regex.format(timezones="|".join([timezone.capitalize() for timezone in secrets])) + info_extras_regex = self.info_extras_regex.format( + timezones="|".join([timezone.capitalize() for timezone in secrets]) + ) info_extras_matches = re.finditer(info_extras_regex, self.bundle) for match in info_extras_matches: timezone, info, extras = match.group("timezone", "info", "extras") @@ -43,4 +48,4 @@ class Spoofer: secrets[secret_pair] = base64.standard_b64decode( "".join(secrets[secret_pair])[:-44] ).decode("utf-8") - return secrets \ No newline at end of file + return secrets diff --git a/qopy/__init__.py b/qopy/__init__.py index 35ca4f3..bab2a20 100644 --- a/qopy/__init__.py +++ b/qopy/__init__.py @@ -1 +1 @@ -from .qopy import Client \ No newline at end of file +from .qopy import Client diff --git a/qopy/exceptions.py b/qopy/exceptions.py index 3d8c1ac..4b56846 100644 --- a/qopy/exceptions.py +++ b/qopy/exceptions.py @@ -1,11 +1,14 @@ class AuthenticationError(Exception): - pass + pass + class IneligibleError(Exception): - pass + pass + class InvalidAppIdError(Exception): - pass + pass + class InvalidAppSecretError(Exception): - pass + pass diff --git a/qopy/qopy.py b/qopy/qopy.py index 9b04b10..63120c1 100644 --- a/qopy/qopy.py +++ b/qopy/qopy.py @@ -2,111 +2,118 @@ # of qopy, originally written by Sorrow446. All credits to the # original author. -import time import hashlib -import requests -from qo_utils import spoofbuz +import time -from qopy.exceptions import AuthenticationError, IneligibleError, InvalidAppSecretError, InvalidAppIdError +import requests + +from qo_utils import spoofbuz +from qopy.exceptions import ( + AuthenticationError, + IneligibleError, + InvalidAppIdError, + InvalidAppSecretError, +) class Client: def __init__(self, email, pwd): - print('Getting tokens...') + print("Getting tokens...") self.spoofer = spoofbuz.Spoofer() self.id = self.spoofer.getAppId() self.session = requests.Session() - self.session.headers.update({ - 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0', - "X-App-Id": self.id}) - self.base = 'https://www.qobuz.com/api.json/0.2/' + self.session.headers.update( + { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0", + "X-App-Id": self.id, + } + ) + self.base = "https://www.qobuz.com/api.json/0.2/" self.auth(email, pwd) self.cfg_setup() def api_call(self, epoint, **kwargs): if epoint == "user/login?": - params={ - "email": kwargs['email'], - "password": kwargs['pwd'], - "app_id": self.id} + params = { + "email": kwargs["email"], + "password": kwargs["pwd"], + "app_id": self.id, + } elif epoint == "track/get?": - params={ - "track_id": kwargs['id']} + params = {"track_id": kwargs["id"]} elif epoint == "album/get?": - params={ - "album_id": kwargs['id']} + params = {"album_id": kwargs["id"]} elif epoint == "track/search?": - params={ - "query": kwargs['query'], - "limit": kwargs['limit']} + params = {"query": kwargs["query"], "limit": kwargs["limit"]} elif epoint == "album/search?": - params={ - "query": kwargs['query'], - "limit": kwargs['limit']} + params = {"query": kwargs["query"], "limit": kwargs["limit"]} elif epoint == "userLibrary/getAlbumsList?": unix = time.time() - r_sig = "userLibrarygetAlbumsList" + str(unix) + kwargs['sec'] - r_sig_hashed = hashlib.md5(r_sig.encode('utf-8')).hexdigest() - params={ + r_sig = "userLibrarygetAlbumsList" + str(unix) + kwargs["sec"] + r_sig_hashed = hashlib.md5(r_sig.encode("utf-8")).hexdigest() + params = { "app_id": self.id, "user_auth_token": self.uat, "request_ts": unix, - "request_sig": r_sig_hashed} + "request_sig": r_sig_hashed, + } elif epoint == "track/getFileUrl?": unix = time.time() - track_id = kwargs['id'] - fmt_id = kwargs['fmt_id'] - r_sig = "trackgetFileUrlformat_id{}intentstreamtrack_id{}{}{}".format(fmt_id, track_id, unix, self.sec) - r_sig_hashed = hashlib.md5(r_sig.encode('utf-8')).hexdigest() - params={ + track_id = kwargs["id"] + fmt_id = kwargs["fmt_id"] + r_sig = "trackgetFileUrlformat_id{}intentstreamtrack_id{}{}{}".format( + fmt_id, track_id, unix, self.sec + ) + r_sig_hashed = hashlib.md5(r_sig.encode("utf-8")).hexdigest() + params = { "request_ts": unix, "request_sig": r_sig_hashed, "track_id": track_id, "format_id": fmt_id, - "intent": 'stream'} + "intent": "stream", + } r = self.session.get(self.base + epoint, params=params) # Do ref header. if epoint == "user/login?": if r.status_code == 401: - raise AuthenticationError('Invalid credentials.') + raise AuthenticationError("Invalid credentials.") elif r.status_code == 400: - raise InvalidAppIdError('Invalid app id.') + raise InvalidAppIdError("Invalid app id.") else: - print('Logged: OK') + print("Logged: OK") elif epoint in ["track/getFileUrl?", "userLibrary/getAlbumsList?"]: if r.status_code == 400: - raise InvalidAppSecretError('Invalid app secret.') + raise InvalidAppSecretError("Invalid app secret.") r.raise_for_status() return r.json() def auth(self, email, pwd): - usr_info = self.api_call('user/login?', email=email, pwd=pwd) - if not usr_info['user']['credential']['parameters']: + usr_info = self.api_call("user/login?", email=email, pwd=pwd) + if not usr_info["user"]["credential"]["parameters"]: raise IneligibleError("Free accounts are not eligible to download tracks.") - self.uat = usr_info['user_auth_token'] - self.session.headers.update({ - "X-User-Auth-Token": self.uat}) - self.label = usr_info['user']['credential']['parameters']['short_label'] - print('Membership: {}'.format(self.label)) + self.uat = usr_info["user_auth_token"] + self.session.headers.update({"X-User-Auth-Token": self.uat}) + self.label = usr_info["user"]["credential"]["parameters"]["short_label"] + print("Membership: {}".format(self.label)) def get_album_meta(self, id): - return self.api_call('album/get?', id=id) + return self.api_call("album/get?", id=id) def get_track_meta(self, id): - return self.api_call('track/get?', id=id) + return self.api_call("track/get?", id=id) def get_track_url(self, id, fmt_id): - return self.api_call('track/getFileUrl?', id=id, fmt_id=fmt_id) + return self.api_call("track/getFileUrl?", id=id, fmt_id=fmt_id) def search_albums(self, query, limit): - return self.api_call('album/search?', query=query, limit=limit) + return self.api_call("album/search?", query=query, limit=limit) def search_tracks(self, query, limit): - return self.api_call('track/search?', query=query, limit=limit) + return self.api_call("track/search?", query=query, limit=limit) def test_secret(self, sec): try: - r = self.api_call('userLibrary/getAlbumsList?', sec=sec) + r = self.api_call("userLibrary/getAlbumsList?", sec=sec) return True except InvalidAppSecretError: return False