From cf9d43fed2854826189caf11a0e1c45d18117849 Mon Sep 17 00:00:00 2001 From: AnthonyF Date: Thu, 9 Dec 2021 21:18:42 -0500 Subject: [PATCH] Housekeeping + Minor bugfixes Fixed: #186, #188, #189 Cleaned up lingering `.format()`s and replaced with appropriate f-strings. Updated project structure to slightly more modern standards. --- bandcamp_dl/__main__.py | 23 ++++++++++---------- bandcamp_dl/bandcamp.py | 22 ++++++++++++------- bandcamp_dl/bandcampdownloader.py | 33 +++++++++++++++-------------- bandcamp_dl/bandcampjson.py | 9 ++++---- bandcamp_dl/deps.txt | 14 ++++++------ bandcamp_dl/utils/clean_print.py | 3 +-- bandcamp_dl/utils/requests_patch.py | 2 +- pyproject.toml | 3 +++ requirements.txt | 8 +++---- setup.cfg | 2 +- setup.py | 27 +++++++++++++++-------- 11 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 pyproject.toml diff --git a/bandcamp_dl/__main__.py b/bandcamp_dl/__main__.py index e0051b9..bae9c81 100644 --- a/bandcamp_dl/__main__.py +++ b/bandcamp_dl/__main__.py @@ -65,15 +65,15 @@ from bandcamp_dl.__init__ import __version__ def main(): - arguments = docopt(__doc__, version='bandcamp-dl {}'.format(__version__)) + arguments = docopt(__doc__, version=f'bandcamp-dl {__version__}') if arguments['--debug']: logging.basicConfig(level=logging.DEBUG) bandcamp = Bandcamp() - basedir = arguments['--base-dir'] or os.getcwd() - session_file = "{}/{}.not.finished".format(basedir, __version__) + basedir = arguments['--base-dir'] or os.path.expanduser('~') + session_file = f"{basedir}/{__version__}.not.finished" if os.path.isfile(session_file) and arguments['URL'] is None: with open(session_file, "r") as f: @@ -92,24 +92,25 @@ def main(): exit() else: urls = arguments['URL'] + + album_list = [] for url in urls: logging.debug("\n\tURL: {}".format(url)) - # url is now a list of URLs. So lets make an albumList and append each parsed album to it. - albumList = [] - for url in urls: - albumList.append(bandcamp.parse(url, not arguments['--no-art'], arguments['--embed-lyrics'], arguments['--debug'])) + album_list.append( + bandcamp.parse(url, not arguments['--no-art'], arguments['--embed-lyrics'], arguments['--debug'])) + # url is now a list of URLs. So lets make an album_list and append each parsed album to it. - for album in albumList: + for album in album_list: logging.debug(" Album data:\n\t{}".format(album)) - for album in albumList: + for album in album_list: if arguments['--full-album'] and not album['full']: print("Full album not available. Skipping ", album['title'], " ...") - albumList.remove(album) # Remove not-full albums BUT continue with the rest of the albums. + album_list.remove(album) # Remove not-full albums BUT continue with the rest of the albums. if arguments['URL'] or arguments['--artist']: logging.debug("Preparing download process..") - for album in albumList: + for album in album_list: bandcamp_downloader = BandcampDownloader(arguments['--template'], basedir, arguments['--overwrite'], arguments['--embed-lyrics'], arguments['--group'], arguments['--embed-art'], arguments['--no-slugify'], diff --git a/bandcamp_dl/bandcamp.py b/bandcamp_dl/bandcamp.py index b1713a3..2c4dcef 100644 --- a/bandcamp_dl/bandcamp.py +++ b/bandcamp_dl/bandcamp.py @@ -12,10 +12,12 @@ from bandcamp_dl.__init__ import __version__ class Bandcamp: def __init__(self): - self.headers = {'User-Agent': 'bandcamp-dl/{} (https://github.com/iheanyi/bandcamp-dl)'.format(__version__)} + self.headers = {'User-Agent': f'bandcamp-dl/{__version__} (https://github.com/iheanyi/bandcamp-dl)'} + self.soup = None + self.tracks = None - def parse(self, url: str, art: bool=True, lyrics: bool=False, debugging: bool=False) -> dict or None: - """Requests the page, cherry picks album info + def parse(self, url: str, art: bool = True, lyrics: bool = False, debugging: bool = False) -> dict or None: + """Requests the page, cherry-picks album info :param url: album/track url :param art: if True download album art @@ -56,7 +58,7 @@ class Bandcamp: album_title = page_json['trackinfo'][0]['title'] try: - label = page_json['item_sellers']['{}'.format(page_json['current']['selling_band_id'])]['name'] + label = page_json['item_sellers'][f'{page_json["current"]["selling_band_id"]}']['name'] except KeyError: label = None @@ -71,10 +73,14 @@ class Bandcamp: "url": url } - artist_url = page_json['url'].rpartition('/album/')[0] + if "track" in page_json['url']: + artist_url = page_json['url'].rpartition('/track/')[0] + else: + artist_url = page_json['url'].rpartition('/album/')[0] + for track in self.tracks: if lyrics: - track['lyrics'] = self.get_track_lyrics("{}{}#lyrics".format(artist_url, track['title_link'])) + track['lyrics'] = self.get_track_lyrics(f"{artist_url}{track['title_link']}#lyrics") if track['file'] is not None: track = self.get_track_metadata(track) album['tracks'].append(track) @@ -84,7 +90,7 @@ class Bandcamp: album['art'] = self.get_album_art() logging.debug(" Album generated..") - logging.debug(" Album URL: {}".format(album['url'])) + logging.debug(f" Album URL: {album['url']}") return album @@ -153,7 +159,7 @@ class Bandcamp: :param page_type: Type of page album/track :return: url as str """ - return "http://{0}.bandcamp.com/{1}/{2}".format(artist, page_type, slug) + return f"http://{artist}.bandcamp.com/{page_type}/{slug}" def get_album_art(self) -> str: """Find and retrieve album art url from page diff --git a/bandcamp_dl/bandcampdownloader.py b/bandcamp_dl/bandcampdownloader.py index b618c8c..8d1ead7 100644 --- a/bandcamp_dl/bandcampdownloader.py +++ b/bandcamp_dl/bandcampdownloader.py @@ -29,7 +29,7 @@ class BandcampDownloader: :param directory: download location :param overwrite: if True overwrite existing files """ - self.headers = {'User-Agent': 'bandcamp-dl/{} (https://github.com/iheanyi/bandcamp-dl)'.format(__version__)} + self.headers = {'User-Agent': f'bandcamp-dl/{__version__} (https://github.com/iheanyi/bandcamp-dl)'} self.session = requests.Session() if type(urls) is str: @@ -102,10 +102,12 @@ class BandcampDownloader: else: path = path.replace("%{track}", str(track['track']).zfill(2)) - path = u"{0}/{1}.{2}".format(self.directory, path, "mp3") + # Double check that the old issue in Python 2 with unicode strings isn't a problem with f-strings + # Otherwise find an alternative to u'STRING' + path = f"{self.directory}/{path}.mp3" logging.debug(" filepath/trackname generated..") - logging.debug("\n\tPath: {}".format(path)) + logging.debug(f"\n\tPath: {path}") return path @staticmethod @@ -116,7 +118,7 @@ class BandcampDownloader: :return: directory path """ directory = os.path.dirname(filename) - logging.debug(" Directory:\n\t{}".format(directory)) + logging.debug(f" Directory:\n\t{directory}") logging.debug(" Directory doesn't exist, creating..") if not os.path.exists(directory): os.makedirs(directory) @@ -144,11 +146,12 @@ class BandcampDownloader: self.num_tracks = len(album['tracks']) self.track_num = track_index + 1 - filepath = self.template_to_path(track_meta, self.ascii_only, self.ok_chars, self.space_char, self.keep_space, self.keep_upper) + ".tmp" + filepath = self.template_to_path(track_meta, self.ascii_only, self.ok_chars, self.space_char, + self.keep_space, self.keep_upper) + ".tmp" filename = filepath.rsplit('/', 1)[1] dirname = self.create_directory(filepath) - logging.debug(" Current file:\n\t{}".format(filepath)) + logging.debug(f" Current file:\n\t{filepath}") if album['art'] and not os.path.exists(dirname + "/cover.jpg"): try: @@ -180,7 +183,7 @@ class BandcampDownloader: # break out of the try/except and move on to the next file break elif os.path.exists(filepath[:-4]) and self.overwrite is not True: - print("File: {} already exists and is complete, skipping..".format(filename[:-4])) + print(f"File: {filename[:-4]} already exists and is complete, skipping..") skip = True break with open(filepath, "wb") as f: @@ -194,13 +197,11 @@ class BandcampDownloader: if not self.debugging: done = int(50 * dl / file_length) print_clean( - "\r({}/{}) [{}{}] :: Downloading: {}".format(self.track_num, self.num_tracks, - "=" * done, " " * (50 - done), - filename[:-8])) + f'\r({self.track_num}/{self.num_tracks}) [{"=" * done}{" " * (50 - done)}] :: Downloading: {filename[:-8]}') local_size = os.path.getsize(filepath) # if the local filesize before encoding doesn't match the remote filesize redownload if local_size != file_length and attempts != 3: - print("{} is incomplete, retrying..".format(filename)) + print(f"{filename} is incomplete, retrying..") continue # if the maximum number of retry attempts is reached give up and move on elif attempts == 3: @@ -218,8 +219,8 @@ class BandcampDownloader: if skip is False: self.write_id3_tags(filepath, track_meta) - if os.path.isfile("{}/{}.not.finished".format(self.directory, __version__)): - os.remove("{}/{}.not.finished".format(self.directory, __version__)) + if os.path.isfile(f"{self.directory}/{__version__}.not.finished"): + os.remove(f"{self.directory}/{__version__}.not.finished") # Remove album art image as it is embedded if self.embed_art: @@ -238,7 +239,7 @@ class BandcampDownloader: filename = filepath.rsplit('/', 1)[1][:-8] if not self.debugging: - print_clean("\r({}/{}) [{}] :: Encoding: {}".format(self.track_num, self.num_tracks, "=" * 50, filename)) + print_clean(f'\r({self.track_num}/{self.num_tracks}) [{"=" * 50}] :: Encoding: {filename}') audio = MP3(filepath) audio.delete() @@ -267,7 +268,7 @@ class BandcampDownloader: audio.save() logging.debug(" Encoding process finished..") - logging.debug(" Renaming:\n\t{} -to-> {}".format(filepath, filepath[:-4])) + logging.debug(f" Renaming:\n\t{filepath} -to-> {filepath[:-4]}") try: os.rename(filepath, filepath[:-4]) @@ -276,4 +277,4 @@ class BandcampDownloader: os.rename(filepath, filepath[:-4]) if not self.debugging: - print_clean("\r({}/{}) [{}] :: Finished: {}".format(self.track_num, self.num_tracks, "=" * 50, filename)) + print_clean(f'\r({self.track_num}/{self.num_tracks}) [{"=" * 50}] :: Finished: {filename}') diff --git a/bandcamp_dl/bandcampjson.py b/bandcamp_dl/bandcampjson.py index 425d83d..4822561 100644 --- a/bandcamp_dl/bandcampjson.py +++ b/bandcamp_dl/bandcampjson.py @@ -1,10 +1,10 @@ import logging -import demjson +import demjson3 class BandcampJSON: - def __init__(self, body, debugging: bool=False): + def __init__(self, body, debugging: bool = False): self.body = body self.json_data = [] @@ -41,6 +41,7 @@ class BandcampJSON: """Convert JavaScript dictionary to JSON""" logging.debug(" Converting JS to JSON..") # Decode with demjson first to reformat keys and lists - decoded_js = demjson.decode(js_data) + decoded_js = demjson3.decode(js_data) # Encode to make valid JSON, add to list of JSON strings - return demjson.encode(decoded_js) + encoded_json = demjson3.encode(decoded_js) + return demjson3.encode(decoded_js) diff --git a/bandcamp_dl/deps.txt b/bandcamp_dl/deps.txt index e8fcc5d..15fe87e 100644 --- a/bandcamp_dl/deps.txt +++ b/bandcamp_dl/deps.txt @@ -1,8 +1,8 @@ -beautifulsoup4==4.6.0 -demjson==2.2.4 +beautifulsoup4==4.10.0 +demjson3==3.0.5 docopt==0.6.2 -mutagen==1.38 -requests==2.18.4 -unicode-slugify==0.1.3 -mock==2.0.0 -chardet==3.0.4 +mutagen==1.45.1 +requests==2.26.0 +unicode-slugify==0.1.5 +mock==4.0.3 +chardet==4.0.0 diff --git a/bandcamp_dl/utils/clean_print.py b/bandcamp_dl/utils/clean_print.py index fe36acb..b826b94 100644 --- a/bandcamp_dl/utils/clean_print.py +++ b/bandcamp_dl/utils/clean_print.py @@ -3,5 +3,4 @@ import shutil def print_clean(msg): terminal_size = shutil.get_terminal_size() - msg_length = len(msg) - print("{}{}".format(msg, " " * (int(terminal_size[0]) - msg_length)), end='') + print(f'{msg}{" " * (int(terminal_size[0]) - len(msg))}', end='') diff --git a/bandcamp_dl/utils/requests_patch.py b/bandcamp_dl/utils/requests_patch.py index 1fdf90a..c9bfee6 100644 --- a/bandcamp_dl/utils/requests_patch.py +++ b/bandcamp_dl/utils/requests_patch.py @@ -27,7 +27,7 @@ def parse_headers(fp, _class=http.client.HTTPMessage): raise http.client.LineTooLong("header line") headers.append(line) if len(headers) > http.client._MAXHEADERS: - raise HTTPException("got more than {} headers".format(http.client._MAXHEADERS)) + raise HTTPException(f"got more than {http.client._MAXHEADERS} headers") if line in (b'\r\n', b'\n', b''): break diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9babed1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=43.0.0", "wheel"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7b1e169..72c916b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ --index-url https://pypi.python.org/simple/ -beautifulsoup4==4.9.3 -demjson==2.2.4 +beautifulsoup4==4.10.0 +demjson3==3.0.5 docopt==0.6.2 mutagen==1.45.1 -requests==2.25.1 -unicode-slugify==0.1.3 +requests==2.26.0 +unicode-slugify==0.1.5 mock==4.0.3 chardet==4.0.0 diff --git a/setup.cfg b/setup.cfg index 60162cd..5ee896c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,4 +2,4 @@ universal = 0 [metadata] -license_file = LICENSE +license_files = LICENSE diff --git a/setup.py b/setup.py index c7f311e..40921b8 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,19 @@ from setuptools import setup, find_packages -from codecs import open -from os import path -import sys +import pathlib -appversion = "0.0.10" +appversion = "0.0.11-dev" -here = path.abspath(path.dirname(__file__)) +here = pathlib.Path(__file__).parent.resolve() -with open(here + '/bandcamp_dl/__init__.py', 'w') as initpy: - initpy.write('__version__ = "{}"'.format(appversion)) +with open(f'{here}/bandcamp_dl/__init__.py', 'w') as initpy: + initpy.write(f'__version__ = "{appversion}"') setup( name='bandcamp-downloader', version=appversion, description='bandcamp-dl downloads albums and tracks from Bandcamp for you', long_description=open('README.rst').read(), + long_description_content_type='text/x-rst', url='https://github.com/iheanyi/bandcamp-dl', author='Iheanyi Ekechukwu', author_email='iekechukwu@gmail.com', @@ -28,13 +27,19 @@ setup( 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3 :: Only', ], keywords=['bandcamp', 'downloader', 'music', 'cli', 'albums', 'dl'], packages=find_packages(exclude=['tests']), - python_requires='~=3.4', + python_requires='>=3.4', install_requires=[ 'beautifulsoup4', - 'demjson', + 'lxml', + 'demjson3', 'docopt', 'mutagen', 'requests', @@ -53,4 +58,8 @@ setup( 'bandcamp-dl=bandcamp_dl.__main__:main', ], }, + project_urls={ + 'Bug Reports': 'https://github.com/iheanyi/bandcamp-dl/issues', + 'Source': 'https://github.com/iheanyi/bandcamp-dl', + }, )