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.master
parent
3019b26c46
commit
cf9d43fed2
|
@ -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'],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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='')
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=43.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
|
@ -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
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
universal = 0
|
||||
|
||||
[metadata]
|
||||
license_file = LICENSE
|
||||
license_files = LICENSE
|
||||
|
|
27
setup.py
27
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',
|
||||
},
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue