Added debug option, corrected an argument check

`--debug` option is now in, should give you a better idea of what is happening when used and where a possible error may be.
master
Anthony Forsberg 2017-05-25 11:22:45 -04:00
parent c6ae5a99fc
commit c460cdc09c
5 changed files with 74 additions and 19 deletions

View File

@ -9,6 +9,7 @@ Arguments:
Options:
-h --help Show this screen.
-v --version Show version.
-d --debug Verbose logging.
--artist=<artist> The artist's slug (from the URL)
--track=<track> The track's slug (from the URL)
--album=<album> The album's slug (from the URL)
@ -17,9 +18,9 @@ Options:
--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
-n --no-art Skip grabbing album art.
-e --embed-lyrics Embed track lyrics (If available)
-g --group Use album/track Label as iTunes grouping
-g --group Use album/track Label as iTunes grouping.
-r --embed-art Embed album art (If available)
-y --no-slugify Disable slugification of track, album, and artist names.
"""
@ -46,6 +47,7 @@ Iheanyi:
import os
import ast
import logging
from docopt import docopt
@ -57,6 +59,9 @@ from bandcamp_dl.__init__ import __version__
def main():
arguments = docopt(__doc__, version='bandcamp-dl {}'.format(__version__))
if arguments['--debug']:
logging.basicConfig(level=logging.DEBUG)
bandcamp = Bandcamp()
basedir = arguments['--base-dir'] or os.getcwd()
@ -78,18 +83,27 @@ def main():
else:
url = arguments['URL']
logging.debug("\n\tURL: {}".format(url))
if arguments['--no-art']:
album = bandcamp.parse(url, False, arguments['--embed-lyrics'])
album = bandcamp.parse(url, False, arguments['--embed-lyrics'], arguments['--debug'])
else:
album = bandcamp.parse(url, True, arguments['--embed-lyrics'])
album = bandcamp.parse(url, True, arguments['--embed-lyrics'], arguments['--debug'])
logging.debug(" Album data:\n\t{}".format(album))
if arguments['--full-album'] and not album['full']:
print("Full album not available. Skipping...")
elif arguments['URL']:
elif arguments['URL'] or arguments['--artist']:
logging.debug("Preparing download process..")
bandcamp_downloader = BandcampDownloader(arguments['--template'], basedir, arguments['--overwrite'],
arguments['--embed-lyrics'], arguments['--group'],
arguments['--embed-art'], arguments['--no-slugify'], url)
arguments['--embed-art'], arguments['--no-slugify'],
arguments['--debug'], url)
logging.debug("Initiating download process..")
bandcamp_downloader.start(album)
else:
logging.debug(" /!\ Something went horribly wrong /!\ ")
if __name__ == '__main__':
main()

View File

@ -1,5 +1,6 @@
from datetime import datetime as dt
import json
import logging
import requests
from bs4 import BeautifulSoup
@ -13,14 +14,18 @@ class Bandcamp:
def __init__(self):
self.headers = {'User-Agent': 'bandcamp-dl/{} (https://github.com/iheanyi/bandcamp-dl)'.format(__version__)}
def parse(self, url: str, art: bool=True, lyrics: bool=False) -> dict or 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
:param url: album/track url
:param art: if True download album art
:param lyrics: if True fetch track lyrics
:param debugging: if True then verbose output
:return: album metadata
"""
if debugging:
logging.basicConfig(level=logging.DEBUG)
try:
response = requests.get(url, headers=self.headers)
except requests.exceptions.MissingSchema:
@ -31,11 +36,14 @@ class Bandcamp:
except FeatureNotFound:
self.soup = BeautifulSoup(response.text, "html.parser")
bandcamp_json = BandcampJSON(self.soup).generate()
logging.debug(" Generating BandcampJSON..")
bandcamp_json = BandcampJSON(self.soup, debugging).generate()
album_json = json.loads(bandcamp_json[0])
embed_json = json.loads(bandcamp_json[1])
page_json = json.loads(bandcamp_json[2])
logging.debug(" BandcampJSON generated..")
logging.debug(" Generating Album..")
self.tracks = album_json['trackinfo']
album_release = album_json['album_release_date']
@ -74,9 +82,11 @@ class Bandcamp:
if art:
album['art'] = self.get_album_art()
logging.debug(" Album generated..")
return album
def get_track_lyrics(self, track_url):
logging.debug(" Fetching track lyrics..")
track_page = requests.get(track_url, headers=self.headers)
try:
track_soup = BeautifulSoup(track_page.text, "lxml")
@ -84,8 +94,10 @@ class Bandcamp:
track_soup = BeautifulSoup(track_page.text, "html.parser")
track_lyrics = track_soup.find("div", {"class": "lyricsText"})
if track_lyrics:
logging.debug(" Lyrics retrieved..")
return track_lyrics.text
else:
logging.debug(" Lyrics not found..")
return "lyrics unavailable"
def all_tracks_available(self) -> bool:
@ -105,6 +117,7 @@ class Bandcamp:
:param track: track dict
:return: track metadata dict
"""
logging.debug(" Generating track metadata..")
track_metadata = {
"duration": track['duration'],
"track": str(track['track_num']),
@ -122,6 +135,7 @@ class Bandcamp:
track['lyrics'] = "lyrics unavailable"
track_metadata['lyrics'] = track['lyrics'].replace('\\r\\n', '\n')
logging.debug(" Track metadata generated..")
return track_metadata
@staticmethod

View File

@ -1,5 +1,6 @@
import os
import sys
import logging
import requests
from mutagen.mp3 import MP3, EasyMP3
@ -17,7 +18,7 @@ from bandcamp_dl.__init__ import __version__
class BandcampDownloader:
def __init__(self, template, directory, overwrite, embed_lyrics, grouping, embed_art, no_slugify, urls=None):
def __init__(self, template, directory, overwrite, embed_lyrics, grouping, embed_art, no_slugify, debugging, urls=None):
"""Initialize variables we will need throughout the Class
:param urls: list of urls
@ -39,12 +40,16 @@ class BandcampDownloader:
self.embed_art = embed_art
self.embed_lyrics = embed_lyrics
self.no_slugify = no_slugify
self.debugging = debugging
def start(self, album: dict):
"""Start album download process
:param album: album dict
"""
if self.debugging:
logging.basicConfig(level=logging.DEBUG)
if album['full'] is not True:
choice = input("Track list incomplete, some tracks may be private, download anyway? (yes/no): ").lower()
if choice == "yes" or choice == "y":
@ -62,6 +67,7 @@ class BandcampDownloader:
:param track: track metadata
:return: filepath
"""
logging.debug(" Generating filepath/trackname..")
path = self.template
if self.no_slugify:
@ -80,6 +86,8 @@ class BandcampDownloader:
path = u"{0}/{1}.{2}".format(self.directory, path, "mp3")
logging.debug(" filepath/trackname generated..")
logging.debug("\n\tPath: {}".format(path))
return path
@staticmethod
@ -90,6 +98,8 @@ class BandcampDownloader:
:return: directory path
"""
directory = os.path.dirname(filename)
logging.debug(" Directory:\n\t{}".format(directory))
logging.debug(" Directory doesn't exist, creating..")
if not os.path.exists(directory):
os.makedirs(directory)
@ -120,6 +130,8 @@ class BandcampDownloader:
filename = filepath.rsplit('/', 1)[1]
dirname = self.create_directory(filepath)
logging.debug(" Current file:\n\t{}".format(filepath))
if album['art'] and not os.path.exists(dirname + "/cover.jpg"):
try:
with open(dirname + "/cover.jpg", "wb") as f:
@ -161,11 +173,12 @@ class BandcampDownloader:
for data in r.iter_content(chunk_size=total):
dl += len(data)
f.write(data)
done = int(50 * dl / file_length)
sys.stdout.write(
"\r({}/{}) [{}{}] :: Downloading: {}".format(self.track_num, self.num_tracks,
"=" * done, " " * (50 - done),
filename[:-8]))
if not self.debugging:
done = int(50 * dl / file_length)
sys.stdout.write(
"\r({}/{}) [{}{}] :: Downloading: {}".format(self.track_num, self.num_tracks,
"=" * done, " " * (50 - done),
filename[:-8]))
sys.stdout.flush()
local_size = os.path.getsize(filepath)
# if the local filesize before encoding doesn't match the remote filesize redownload
@ -203,10 +216,13 @@ class BandcampDownloader:
:param filepath: name of mp3 file
:param meta: dict of track metadata
"""
logging.debug(" Encoding process starting..")
filename = filepath.rsplit('/', 1)[1][:-8]
sys.stdout.flush()
sys.stdout.write("\r({}/{}) [{}] :: Encoding: {}".format(self.track_num, self.num_tracks, "=" * 50, filename))
if not self.debugging:
sys.stdout.flush()
sys.stdout.write("\r({}/{}) [{}] :: Encoding: {}".format(self.track_num, self.num_tracks, "=" * 50, filename))
audio = MP3(filepath)
audio.delete()
@ -234,10 +250,14 @@ class BandcampDownloader:
audio["date"] = meta["date"]
audio.save()
logging.debug(" Encoding process finished..")
logging.debug(" Renaming:\n\t{} -to-> {}".format(filepath, filepath[:-4]))
try:
os.rename(filepath, filepath[:-4])
except WindowsError:
os.remove(filepath[:-4])
os.rename(filepath, filepath[:-4])
sys.stdout.write("\r({}/{}) [{}] :: Finished: {}".format(self.track_num, self.num_tracks, "=" * 50, filename))
if not self.debugging:
sys.stdout.write("\r({}/{}) [{}] :: Finished: {}".format(self.track_num, self.num_tracks, "=" * 50, filename))

View File

@ -1,20 +1,25 @@
import re
import logging
import demjson
class BandcampJSON:
def __init__(self, body):
def __init__(self, body, debugging: bool=False):
self.body = body
self.targets = ['TralbumData', 'EmbedData', 'pagedata']
self.json_data = []
if debugging:
logging.basicConfig(level=logging.DEBUG)
def generate(self) -> list:
"""Iterate through targets grabbing needed data"""
for target in self.targets:
if target[:4] == 'page':
self.get_pagedata()
else:
logging.debug(" Grabbing target data..")
self.regex = re.compile(r"(?<=var\s" + target + "\s=\s).*?(?=};)", re.DOTALL)
self.target = target
self.js_to_json()
@ -28,6 +33,7 @@ class BandcampJSON:
def get_js(self):
"""Get <script> element containing the data we need and return the raw JS"""
logging.debug(" Grabbing embedded script..")
self.js_data = self.body.find("script", {"src": False}, text=re.compile(self.target)).string
self.extract_data(self.js_data)
@ -40,6 +46,7 @@ class BandcampJSON:
def js_to_json(self):
"""Convert JavaScript dictionary to JSON"""
logging.debug(" Converting JS to JSON..")
self.get_js()
# Decode with demjson first to reformat keys and lists
decoded_js = demjson.decode(self.js_data)

View File

@ -3,7 +3,7 @@ from codecs import open
from os import path
import sys
appversion = "0.0.8-05"
appversion = "0.0.8-06"
pyversion = int("{}{}".format(sys.version_info[0], sys.version_info[1]))
if not pyversion >= 34: