From 4884c1bd4195b09bdd61631d92ce0b7c3a7dba82 Mon Sep 17 00:00:00 2001 From: yosh Date: Wed, 18 Dec 2024 13:51:46 -0500 Subject: [PATCH] rework/cleanup argument handling and fix custom domains --- free_bandcamp_downloader/__main__.py | 126 ++++++++++++++++----------- 1 file changed, 77 insertions(+), 49 deletions(-) diff --git a/free_bandcamp_downloader/__main__.py b/free_bandcamp_downloader/__main__.py index 678eded..92d1ec8 100644 --- a/free_bandcamp_downloader/__main__.py +++ b/free_bandcamp_downloader/__main__.py @@ -1,32 +1,37 @@ """Download free albums and tracks from Bandcamp Usage: - bcdl-free (-a | -l )[--force][--no-unzip][-d | --dir ][-e | --email ] - [-z | --zipcode ][-c | --country ][-f | --format ] - [--cookies ][--identity ][--debug] - bcdl-free setdefault [-d | --dir ][-e | --email ][-z | --zipcode ] - [-c | --country ][-f | --format ] + bcdl-free [--debug] [--force] [--no-unzip] [-al] + [-d ] [-e ] [-z ] [-c ] [-f ] + [--cookies ] [--identity ] URL... + bcdl-free setdefault [-d ] [-e ] [-z ] + [-c ] [-f ] bcdl-free defaults bcdl-free clear - bcdl-free (-h | --help) - bcdl-free --version + bcdl-free -h | --help | --version + +Arguments: + URL URL to download. Can be a link to a label or release page + +Subcommands: + defaults list default configuration options + setdefaults set default configuration options + clear clear default configuration options + Options: - -h --help Show this screen - --version Show version - -a Download the album at URL - -l Download all free albums of the label at URL - --force Download even if album has been downloaded before - --no-unzip Don't unzip downloaded albums - setdefault Set default options - defaults List the default options - clear Clear download history - -d --dir Set download directory - -c --country Set country - -z --zipcode Set zipcode - -e --email Set email (set to 'auto' to automatically download from a disposable email) - -f --format Set format - --cookies Path to cookies.txt file so albums in your collection can be downloaded - --identity Value of identity cookie so albums in your collection can be downloaded - --debug Set loglevel to debug + -h --help Show this screen + --version Show version + --force Download even if album has been downloaded before + --no-unzip Don't unzip downloaded albums + --debug Set loglevel to debug + -a -l Dummy options, for backwards compatibility + -d --dir Set download directory + -c --country Set country + -z --zipcode Set zipcode + -e --email Set email (set to 'auto' to automatically download from a disposable email) + -f --format Set format + --cookies Path to cookies.txt file so albums in your collection can be downloaded + --identity Value of identity cookie so albums in your collection can be downloaded + Formats: - FLAC - V0MP3 @@ -314,13 +319,15 @@ class BCFreeDownloader: raise BCFreeDownloadError(f"{url} has no audio. Skipping...") head_data = soup.head.find("script", {"type": "application/ld+json"}, recursive=False).string + head_data = json.loads(head_data) + head_id = head_data.get("@id") + # fallback if a track link was provided # track releases have this inAlbum key even if they're standalone head_data = head_data.get("inAlbum", head_data)["albumRelease"] - # iterate through every additionalProperty of every albumRelease until we get - # the one that corresponds to the track release (t) or album release (a) - # physical releases are p, and discography offers are b, so this should be fine - head_data = next(obj for obj in head_data if obj["@id"] == url) + # find the albumRelease object that matches the overall album @id + # this will ensure that strictly what is provided as a link is downloaded + head_data = next(obj for obj in head_data if obj["@id"] == head_id) if "offers" not in head_data: raise BCFreeDownloadError(f"{url} has no digital download. Skipping...") @@ -478,15 +485,17 @@ def main(): config = get_config(data_dir, config_dir) options = BCFreeDownloaderOptions() arguments = docopt(__doc__, version=__version__) + if arguments["--debug"]: logger.setLevel(logging.DEBUG) - if arguments["-a"] or arguments["-l"] or arguments["setdefault"]: - # set options + + # set options if needed + if arguments["URL"] or arguments["setdefault"]: for field in dataclasses.fields(options): option = field.name arg = f"--{option}" if arguments[arg]: - setattr(options, option, arguments[arg][0]) + setattr(options, option, arguments[arg]) else: setattr(options, option, config.get(option)) if not getattr(options, option): @@ -499,7 +508,26 @@ def main(): f'{options["format"]} is not a valid format. See "bcdl-free -h" for valid formats' ) sys.exit(1) - if arguments["-a"] or arguments["-l"]: + + if arguments["setdefault"]: + # write arguments to config + for field in dataclasses.fields(options): + option = field.name + arg = f"--{option}" + if arguments[arg]: + config.set(option, arguments[arg]) + sys.exit(0) + + if arguments["clear"]: + with open(config.get("download_history_file"), "w"): + pass + sys.exit(0) + + if arguments["defaults"]: + print(str(config)) + sys.exit(0) + + if arguments["URL"]: # init downloader downloader = BCFreeDownloader( options, @@ -509,25 +537,25 @@ def main(): arguments["--cookies"], arguments["--identity"], ) - if arguments["-a"]: - downloader.download_album(arguments["-a"], arguments["--force"]) - elif arguments["-l"]: - downloader.download_label(arguments["-l"], arguments["--force"]) + + # only matches if there's a /album/iden or /track/iden part + regex_isalbum = re.compile(r'/(album|track)/[a-z0-9-]+') + regex_extractlabel = re.compile(r'(?P