0.0.8 Update

Added --track for downloading individual tracks/singles
Added --embed-art to forcibly embed album art if available
master
Anthony Forsberg 2017-04-15 09:30:34 -04:00
parent 9db2aba929
commit 12327e119b
7 changed files with 86 additions and 118 deletions

View File

@ -17,6 +17,7 @@ Version 0.0.6
- [Enhancement] Individual track downloads work now.
- [Bugfix] Fixed imports, now working when installed via pip.
- [Note] Last version to officially support Python 2.7.x
- [Bugfix] Fixed an encoding issue with accented characters in the filepath. (Thanks `oaubert <https://github.com/oaubert>`_)
Version 0.0.7
-------------
@ -28,3 +29,11 @@ Version 0.0.7
- [Dependency] Slimit is no longer required.
- [Dependency] Ply is no longer required.
- [Dependency] demjson is now required.
- [Bugfix] Downloading singles is now fixed.
- [Bugfix] Monkey-patched Requests to fix compatability with Python versions before 3.6.
- [Enhancement] Added a --group option to insert a group tag (iTunes)
Version 0.0.8
-------------
- [Enhancement] --embed-art option to forcibly embed album art (if available)
- [Enhancement] --track option for downloading individual tracks and singles.

View File

@ -21,7 +21,7 @@ From Wheel
3. ``pip install <filename>.whl``
[OSX] From Homebrew
----------
-------------------
``brew install bandcamp-dl``
@ -46,14 +46,18 @@ Details
::
Usage:
bandcamp-dl.py <url>
bandcamp-dl.py [--template=<template>] [--base-dir=<dir>]
[--full-album]
(<url> | --artist=<artist> --album=<album>)
[--overwrite]
[--no-art]
bandcamp-dl.py (-h | --help)
bandcamp-dl.py (--version)
bandcamp-dl [url]
bandcamp-dl [--template=<template>] [--base-dir=<dir>]
[--full-album]
(<url> | --artist=<artist> --album=<album>)
[--overwrite]
[--no-art]
[--embed-lyrics]
[--group]
[--embed-art]
[--debug]
bandcamp-dl (-h | --help)
bandcamp-dl (--version)
Options
=======
@ -61,15 +65,20 @@ Options
::
Options:
-h --help Show this screen.
-v --version Show version.
--artist=<artist> The artist's slug (from the URL)
--album=<album> The album's slug (from the URL)
--template=<template> Output filename template.
[default: %{artist}/%{album}/%{track} - %{title}]
--base-dir=<dir> Base location of which all files are downloaded
-o --overwrite Overwrite tracks that already exist. Default is False.
-n --no-art Skip grabbing album art
-h --help Show this screen.
-v --version Show version.
-a --artist=<artist> The artist's slug (from the URL)
-b --album=<album> The album's slug (from the URL)
-t --template=<template> Output filename template.
[default: %{artist}/%{album}/%{track} - %{title}]
-d --base-dir=<dir> Base location of which all files are downloaded.
-f --full-album Download only if all tracks are available.
-o --overwrite Overwrite tracks that already exist. Default is False.
-n --no-art Skip grabbing album art
-e --embed-lyrics Embed track lyrics (If available)
-g --group Use album/track Label as iTunes grouping
-r --embed-art Embed album art (If available)
-u --debug Log debug information to a file
Filename Template
=================
@ -90,10 +99,7 @@ Bugs
====
Bugs should be reported `here <https://github.com/iheanyi/bandcamp-dl/issues>`_.
Please include the full output of the command when run with ``--verbose``.
The output (including the first lines) contain important debugging information.
Issues without the full output are often not reproducible and therefore
do not get solved in short order, if ever.
Please include the URL and/or options used.
For discussions, join us in `Discord <https://discord.gg/nwdT4MP>`_.
@ -128,24 +134,6 @@ Many feature requests are for features that actually exist already!
Please, absolutely do show off your work in the issue report and detail
how the existing similar options do *not* solve your problem.
Is there enough context in your bug report?
===========================================
People want to solve problems, and often think they do us a favor by
breaking down their larger problems (e.g. wanting to skip already
downloaded files) to a specific request (e.g. requesting us to look
whether the file exists before downloading the info page). However, what
often happens is that they break down the problem into two steps: One
simple, and one impossible (or extremely complicated one).
We are then presented with a very complicated request when the original
problem could be solved far easier, e.g. by recording the downloaded
video IDs in a separate file. To avoid this, you must include the
greater context where it is non-obvious. In particular, every feature
request that does not consist of adding support for a new site should
contain a use case scenario that explains in what situation the missing
feature would be useful.
Does the issue involve one problem, and one problem only?
=========================================================
@ -157,20 +145,10 @@ mark the issue as closed. Typically, reporting a bunch of issues leads
to the ticket lingering since nobody wants to attack that behemoth,
until someone mercifully splits the issue into multiple ones.
In particular, every site support request issue should only pertain to
services at one site (generally under a common domain, but always using
the same backend technology). Do not request support for vimeo user
videos, Whitehouse podcasts, and Google Plus pages in the same issue.
Also, make sure that you don't post bug reports alongside feature
requests. As a rule of thumb, a feature request does not include outputs
of bandcamp-dl that are not immediately related to the feature at hand.
Do not post reports of a network error alongside the request for a new
video service.
Is anyone going to need the feature?
====================================
Only post features that you (or an incapacitated friend you can
Only post features that you (or an incapable friend you can
personally talk to) require. Do not post features because they seem like
a good idea. If they are really useful, they will be requested by
someone who requires them.

View File

@ -4,11 +4,12 @@ Usage:
bandcamp-dl [url]
bandcamp-dl [--template=<template>] [--base-dir=<dir>]
[--full-album]
(<url> | --artist=<artist> --album=<album>)
(<url> | --artist=<artist> --album=<album> | --artist=<artist> --track=<track>)
[--overwrite]
[--no-art]
[--embed-lyrics]
[--group]
[--embed-art]
bandcamp-dl (-h | --help)
bandcamp-dl (--version)
@ -16,6 +17,7 @@ Options:
-h --help Show this screen.
-v --version Show version.
-a --artist=<artist> The artist's slug (from the URL)
-s --track=<track> The track's slug (from the URL)
-b --album=<album> The album's slug (from the URL)
-t --template=<template> Output filename template.
[default: %{artist}/%{album}/%{track} - %{title}]
@ -25,6 +27,7 @@ Options:
-n --no-art Skip grabbing album art
-e --embed-lyrics Embed track lyrics (If available)
-g --group Use album/track Label as iTunes grouping
-r --embed-art Embed album art (If available)
"""
"""
Coded by:
@ -55,25 +58,9 @@ from docopt import docopt
from bandcamp_dl.bandcamp import Bandcamp
from bandcamp_dl.bandcampdownloader import BandcampDownloader
# LOGGING #######################
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler('bandcamp-dl_0.0.7-09-DEBUG.log')
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
# LOGGING #######################
def main():
arguments = docopt(__doc__, version='bandcamp-dl 0.0.7-09')
logger.debug('\n\tArguments: {}\n'.format(arguments))
arguments = docopt(__doc__, version='bandcamp-dl 0.0.8')
bandcamp = Bandcamp()
@ -83,14 +70,16 @@ def main():
if os.path.isfile(session_file):
with open(session_file, "r") as f:
arguments = ast.literal_eval(f.readline())
elif arguments['<url>'] is None:
elif arguments['<url>'] is None and arguments['--artist'] is None:
print(__doc__)
else:
with open(session_file, "w") as f:
f.write("".join(str(arguments).split('\n')))
if arguments['--artist'] and arguments['--album']:
url = Bandcamp.generate_album_url(arguments['--artist'], arguments['--album'])
url = Bandcamp.generate_album_url(arguments['--artist'], arguments['--album'], "album")
elif arguments['--artist'] and arguments['--track']:
url = Bandcamp.generate_album_url(arguments['--artist'], arguments['--track'], "track")
else:
url = arguments['<url>']
@ -105,8 +94,10 @@ def main():
print("Full album not available. Skipping...")
else:
bandcamp_downloader = BandcampDownloader(url, arguments['--template'], basedir, arguments['--overwrite'],
arguments['--embed-lyrics'], arguments['--group'])
arguments['--embed-lyrics'], arguments['--group'],
arguments['--embed-art'], arguments['--debug'])
bandcamp_downloader.start(album)
if __name__ == '__main__':
main()

View File

@ -7,22 +7,11 @@ from bs4 import FeatureNotFound
from bandcamp_dl.bandcampjson import BandcampJSON
# LOGGING #######################
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler('bandcamp-dl_0.0.7-09-DEBUG.log')
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
# LOGGING #######################
class Bandcamp:
def __init__(self, debug=False):
self.debug = debug
def parse(self, url: str, art: bool=True) -> dict or None:
"""Requests the page, cherry picks album info
@ -40,8 +29,6 @@ class Bandcamp:
except FeatureNotFound:
self.soup = BeautifulSoup(response.text, "html.parser")
logger.debug('\n\tBeautifulSoup: {}\n'.format(self.soup))
bandcamp_json = BandcampJSON(self.soup).generate()
album_json = json.loads(bandcamp_json[0])
embed_json = json.loads(bandcamp_json[1])
@ -121,14 +108,15 @@ class Bandcamp:
return track_metadata
@staticmethod
def generate_album_url(artist: str, album: str) -> str:
def generate_album_url(artist: str, slug: str, page_type: str) -> str:
"""Generate an album url based on the artist and album name
:param artist: artist name
:param album: album name
:return: album url as str
:param slug: Slug of album/track
:param page_type: Type of page album/track
:return: url as str
"""
return "http://{0}.bandcamp.com/album/{1}".format(artist, album)
return "http://{0}.bandcamp.com/{1}/{2}".format(artist, page_type, slug)
def get_album_art(self) -> str:
"""Find and retrieve album art url from page

View File

@ -6,6 +6,7 @@ from mutagen.mp3 import MP3, EasyMP3
from mutagen.id3._frames import TIT1
from mutagen.id3._frames import TIT2
from mutagen.id3._frames import USLT
from mutagen.id3._frames import APIC
from slugify import slugify
if not sys.version_info[:2] == (3, 6):
@ -14,7 +15,8 @@ if not sys.version_info[:2] == (3, 6):
class BandcampDownloader:
def __init__(self, urls=None, template=None, directory=None, overwrite=False, lyrics=None, grouping=None):
def __init__(self, urls=None, template=None, directory=None, overwrite=False, lyrics=None, grouping=None,
embed_art=None, debug=False):
"""Initialize variables we will need throughout the Class
:param urls: list of urls
@ -22,7 +24,7 @@ class BandcampDownloader:
:param directory: download location
:param overwrite: if True overwrite existing files
"""
self.headers = {'user_agent': 'bandcamp-dl/0.0.7-09 (https://github.com/iheanyi/bandcamp-dl)'}
self.headers = {'user_agent': 'bandcamp-dl/0.0.8 (https://github.com/iheanyi/bandcamp-dl)'}
self.session = requests.Session()
if type(urls) is str:
@ -34,6 +36,8 @@ class BandcampDownloader:
self.overwrite = overwrite
self.lyrics = lyrics
self.grouping = grouping
self.embed_art = embed_art
self.debug = debug
def start(self, album: dict):
"""Start album download process
@ -60,10 +64,12 @@ class BandcampDownloader:
path = self.template
path = path.replace("%{artist}", slugify(track['artist']))
path = path.replace("%{album}", slugify(track['album']))
if track['track'] == "None":
path = path.replace("%{track}", "Single")
else:
path = path.replace("%{track}", str(track['track']).zfill(2))
path = path.replace("%{title}", slugify(track['title']))
path = u"{0}/{1}.{2}".format(self.directory, path, "mp3")
@ -108,6 +114,16 @@ class BandcampDownloader:
filename = filepath.rsplit('/', 1)[1]
dirname = self.create_directory(filepath)
if album['art'] and not os.path.exists(dirname + "/cover.jpg"):
try:
with open(dirname + "/cover.jpg", "wb") as f:
r = self.session.get(album['art'])
f.write(r.content)
self.album_art = dirname + "/cover.jpg"
except Exception as e:
print(e)
print("Couldn't download album art.")
attempts = 0
skip = False
@ -165,17 +181,14 @@ class BandcampDownloader:
return False
if skip is not True:
self.write_id3_tags(filepath, track_meta)
if album['art']:
try:
with open(dirname + "/cover.jpg", "wb") as f:
r = self.session.get(album['art'])
f.write(r.content)
except Exception as e:
print(e)
print("Couldn't download album art.")
if os.path.isfile("not.finished"):
os.remove("not.finished")
# Remove album art image as it is embedded
if self.embed_art:
os.remove(self.album_art)
return True
def write_id3_tags(self, filepath: str, meta: dict):
@ -199,6 +212,10 @@ class BandcampDownloader:
audio["TIT1"] = TIT1(encoding=3, text=meta["label"])
if self.lyrics:
audio["USLT"] = USLT(encoding=3, lang='eng', desc='', text=meta['lyrics'])
if self.embed_art:
with open(self.album_art, 'rb') as cover_img:
cover_bytes = cover_img.read()
audio["APIC"] = APIC(encoding=3, mime='image/jpeg', type=3, desc='Cover', data=cover_bytes)
audio.save()
audio = EasyMP3(filepath)

View File

@ -2,20 +2,6 @@ import re
import demjson
# LOGGING #######################
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler('bandcamp-dl_0.0.7-09-DEBUG.log')
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
# LOGGING #######################
class BandcampJSON:
def __init__(self, body):
@ -32,7 +18,6 @@ class BandcampJSON:
self.regex = re.compile(r"(?<=var\s" + target + "\s=\s).*?(?=};)", re.DOTALL)
self.target = target
self.js_to_json()
logger.debug('\n\tPreliminary JSON data: {}\n'.format(self.json_data))
return self.json_data
def get_pagedata(self):

View File

@ -10,7 +10,7 @@ here = path.abspath(path.dirname(__file__))
setup(
name='bandcamp-downloader',
version='0.0.7-09',
version='0.0.8',
description='bandcamp-dl downloads albums and tracks from Bandcamp for you',
long_description=open('README.rst').read(),
url='https://github.com/iheanyi/bandcamp-dl',