mirror of
https://github.com/Wojtek242/qobuz-dl.git
synced 2024-11-22 11:05:25 +01:00
working function that cleans up discographies
also added bit depth and sampling rate to download logging
This commit is contained in:
parent
93f9d8d92f
commit
628e0a67c3
129
qobuz_dl/core.py
129
qobuz_dl/core.py
@ -1,3 +1,7 @@
|
|||||||
|
# ----- Testing ------
|
||||||
|
import json
|
||||||
|
|
||||||
|
# --------------------
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@ -67,6 +71,7 @@ class QobuzDL:
|
|||||||
folder_format="{artist} - {album} ({year}) [{bit_depth}B-"
|
folder_format="{artist} - {album} ({year}) [{bit_depth}B-"
|
||||||
"{sampling_rate}kHz]",
|
"{sampling_rate}kHz]",
|
||||||
track_format="{tracknumber}. {tracktitle}",
|
track_format="{tracknumber}. {tracktitle}",
|
||||||
|
smart_discography=False,
|
||||||
):
|
):
|
||||||
self.directory = self.create_dir(directory)
|
self.directory = self.create_dir(directory)
|
||||||
self.quality = quality
|
self.quality = quality
|
||||||
@ -82,6 +87,7 @@ class QobuzDL:
|
|||||||
self.downloads_db = create_db(downloads_db) if downloads_db else None
|
self.downloads_db = create_db(downloads_db) if downloads_db else None
|
||||||
self.folder_format = folder_format
|
self.folder_format = folder_format
|
||||||
self.track_format = track_format
|
self.track_format = track_format
|
||||||
|
self.smart_discography = smart_discography
|
||||||
|
|
||||||
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)
|
||||||
@ -100,14 +106,14 @@ class QobuzDL:
|
|||||||
return fix
|
return fix
|
||||||
|
|
||||||
def get_url_info(self, url: str) -> Tuple[str, str]:
|
def get_url_info(self, url: str) -> Tuple[str, str]:
|
||||||
'''Returns the type of the url and the id.
|
"""Returns the type of the url and the id.
|
||||||
|
|
||||||
Compatible with urls of the form:
|
Compatible with urls of the form:
|
||||||
https://www.qobuz.com/us-en/{type}/{name}/{id}
|
https://www.qobuz.com/us-en/{type}/{name}/{id}
|
||||||
https://open.qobuz.com/{type}/{id}
|
https://open.qobuz.com/{type}/{id}
|
||||||
https://play.qobuz.com/{type}/{id}
|
https://play.qobuz.com/{type}/{id}
|
||||||
/us-en/{type}/-/{id}
|
/us-en/{type}/-/{id}
|
||||||
'''
|
"""
|
||||||
|
|
||||||
r = re.search(
|
r = re.search(
|
||||||
r"(?:https:\/\/(?:w{3}|open|play)\.qobuz\.com)?(?:\/[a-z]{2}-[a-z]{2})"
|
r"(?:https:\/\/(?:w{3}|open|play)\.qobuz\.com)?(?:\/[a-z]{2}-[a-z]{2})"
|
||||||
@ -178,7 +184,11 @@ class QobuzDL:
|
|||||||
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]
|
||||||
|
items = self.smart_discography_filter(
|
||||||
|
content, True, True,
|
||||||
|
)
|
||||||
logger.info(f"{YELLOW}{len(items)} downloads in queue")
|
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(
|
||||||
@ -416,7 +426,9 @@ class QobuzDL:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for i in track_list:
|
for i in track_list:
|
||||||
track_id = self.get_url_info(self.search_by_type(i, "track", 1, lucky=True)[0])[1]
|
track_id = self.get_url_info(
|
||||||
|
self.search_by_type(i, "track", 1, lucky=True)[0]
|
||||||
|
)[1]
|
||||||
if track_id:
|
if track_id:
|
||||||
self.download_from_id(track_id, False, pl_directory)
|
self.download_from_id(track_id, False, pl_directory)
|
||||||
|
|
||||||
@ -468,3 +480,112 @@ class QobuzDL:
|
|||||||
if len(track_list) > 1:
|
if len(track_list) > 1:
|
||||||
with open(os.path.join(pl_directory, pl_name), "w") as pl:
|
with open(os.path.join(pl_directory, pl_name), "w") as pl:
|
||||||
pl.write("\n\n".join(track_list))
|
pl.write("\n\n".join(track_list))
|
||||||
|
|
||||||
|
def smart_discography_filter(
|
||||||
|
self, contents: list, save_space=False, remove_extras=False
|
||||||
|
) -> list:
|
||||||
|
"""When downloading some artists' discography, there can be a lot
|
||||||
|
of duplicate albums that needlessly use 10's of GB of bandwidth. This
|
||||||
|
filters the duplicates.
|
||||||
|
|
||||||
|
Example (Stevie Wonder):
|
||||||
|
* ...
|
||||||
|
* Songs In The Key of Life [24/192]
|
||||||
|
* Songs In The Key of Life [24/96]
|
||||||
|
* Songs In The Key of Life [16/44.1]
|
||||||
|
* ...
|
||||||
|
|
||||||
|
This function should choose either [24/96] or [24/192].
|
||||||
|
It also skips deluxe albums in favor of the originals, picks remasters
|
||||||
|
in favor of originals, and removes albums by other artists that just
|
||||||
|
feature the requested artist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def print_album(a: dict):
|
||||||
|
print(
|
||||||
|
f"{album['title']} - {album['version']} ({album['maximum_bit_depth']}/{album['maximum_sampling_rate']})"
|
||||||
|
)
|
||||||
|
|
||||||
|
def remastered(s: str) -> bool:
|
||||||
|
"""Case insensitive match to check whether
|
||||||
|
an album is remastered.
|
||||||
|
"""
|
||||||
|
if s is None:
|
||||||
|
return False
|
||||||
|
return re.match(r"(?i)(re)?master(ed)?", s) is not None
|
||||||
|
|
||||||
|
def extra(album: dict) -> bool:
|
||||||
|
assert hasattr(album, "__getitem__"), "param must be dict-like"
|
||||||
|
if 'version' not in album:
|
||||||
|
return False
|
||||||
|
return (
|
||||||
|
re.findall(
|
||||||
|
r"(?i)(anniversary|deluxe|live|collector|demo)",
|
||||||
|
f"{album['title']} {album['version']}",
|
||||||
|
)
|
||||||
|
!= []
|
||||||
|
)
|
||||||
|
|
||||||
|
# remove all albums by other artists
|
||||||
|
artist = contents[0]["name"]
|
||||||
|
items = [item["albums"]["items"] for item in contents][0]
|
||||||
|
artist_f = [] # artist filtered
|
||||||
|
for item in items:
|
||||||
|
if item["artist"]["name"] == artist:
|
||||||
|
artist_f.append(item)
|
||||||
|
|
||||||
|
# use dicts to group duplicate titles together
|
||||||
|
titles_f = dict()
|
||||||
|
for item in artist_f:
|
||||||
|
if (t := item["title"]) not in titles_f:
|
||||||
|
titles_f[t] = []
|
||||||
|
titles_f[t].append(item)
|
||||||
|
|
||||||
|
# pick desired quality out of duplicates
|
||||||
|
# remasters are given preferred status
|
||||||
|
quality_f = []
|
||||||
|
for albums in titles_f.values():
|
||||||
|
# no duplicates for title
|
||||||
|
if len(albums) == 1:
|
||||||
|
quality_f.append(albums[0])
|
||||||
|
continue
|
||||||
|
|
||||||
|
# desired bit depth and sampling rate
|
||||||
|
bit_depth = max(a["maximum_bit_depth"] for a in albums)
|
||||||
|
# having sampling rate > 44.1kHz is a waste of space
|
||||||
|
# https://en.wikipedia.org/wiki/Nyquist–Shannon_sampling_theorem
|
||||||
|
# https://en.wikipedia.org/wiki/44,100_Hz#Human_hearing_and_signal_processing
|
||||||
|
cmp_func = min if save_space else max
|
||||||
|
sampling_rate = cmp_func(
|
||||||
|
a["maximum_sampling_rate"]
|
||||||
|
for a in albums
|
||||||
|
if a["maximum_bit_depth"] == bit_depth
|
||||||
|
)
|
||||||
|
has_remaster = bool([a for a in albums if remastered(a["version"])])
|
||||||
|
|
||||||
|
# check if album has desired bit depth and sampling rate
|
||||||
|
# if there is a remaster in `item`, check if the album is a remaster
|
||||||
|
for album in albums:
|
||||||
|
if (
|
||||||
|
album["maximum_bit_depth"] == bit_depth
|
||||||
|
and album["maximum_sampling_rate"] == sampling_rate
|
||||||
|
):
|
||||||
|
if not has_remaster:
|
||||||
|
quality_f.append(album)
|
||||||
|
elif remastered(album["version"]):
|
||||||
|
quality_f.append(album)
|
||||||
|
|
||||||
|
if remove_extras:
|
||||||
|
final = []
|
||||||
|
# this filters those huge albums with outtakes, live performances etc.
|
||||||
|
for album in quality_f:
|
||||||
|
if not extra(album):
|
||||||
|
final.append(album)
|
||||||
|
else:
|
||||||
|
final = quality_f
|
||||||
|
|
||||||
|
return final
|
||||||
|
# key = lambda a: a["title"]
|
||||||
|
# final.sort(key=key)
|
||||||
|
# for album in final:
|
||||||
|
# print_album(album)
|
||||||
|
@ -247,7 +247,7 @@ def download_id_by_type(
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"\n{YELLOW}Downloading: {album_title}\n" f"Quality: {file_format}\n"
|
f"\n{YELLOW}Downloading: {album_title}\nQuality: {file_format} ({bit_depth}/{sampling_rate})\n"
|
||||||
)
|
)
|
||||||
album_attr = {
|
album_attr = {
|
||||||
"artist": meta["artist"]["name"],
|
"artist": meta["artist"]["name"],
|
||||||
|
Loading…
Reference in New Issue
Block a user