diff --git a/core/const.py b/core/const.py new file mode 100644 index 0000000..03f7f69 --- /dev/null +++ b/core/const.py @@ -0,0 +1,8 @@ +import logzero + +_log_format = ("%(color)s%(levelname)s:%(end_color)s %(message)s") +formatter = logzero.LogFormatter(fmt=_log_format) + +# options +log = logzero.setup_logger(formatter=formatter) +args = None diff --git a/core/convert.py b/core/convert.py index 5f830a3..d93dbc1 100644 --- a/core/convert.py +++ b/core/convert.py @@ -1,6 +1,6 @@ import subprocess import os -from core.logger import log +from core.const import log """What are the differences and similarities between ffmpeg, libav, and avconv? diff --git a/core/arguments.py b/core/handle.py similarity index 62% rename from core/arguments.py rename to core/handle.py index a4d11d2..d0d751f 100644 --- a/core/arguments.py +++ b/core/handle.py @@ -1,27 +1,38 @@ +import logging import argparse import os import sys -from core.logger import log_leveller, _LOG_LEVELS_STR + +_LOG_LEVELS_STR = ['INFO', 'WARNING', 'ERROR', 'DEBUG'] + +def log_leveller(log_level_str): + loggin_levels = [logging.INFO, logging.WARNING, logging.ERROR, logging.DEBUG] + log_level_str_index = _LOG_LEVELS_STR.index(log_level_str) + loggin_level = loggin_levels[log_level_str_index] + return loggin_level -def get_arguments(): +def get_arguments(to_group=False, raw_args=None): parser = argparse.ArgumentParser( description='Download and convert songs from Spotify, Youtube etc.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument( - '-s', '--song', help='download song by spotify link or name') - group.add_argument( - '-l', '--list', help='download songs from a file') - group.add_argument( - '-p', '--playlist', help='load songs from playlist URL into .txt') - group.add_argument( - '-b', '--album', help='load songs from album URL into .txt') - group.add_argument( - '-u', '--username', - help="load songs from user's playlist into .txt") + if to_group: + group = parser.add_mutually_exclusive_group(required=True) + + group.add_argument( + '-s', '--song', help='download song by spotify link or name') + group.add_argument( + '-l', '--list', help='download songs from a file') + group.add_argument( + '-p', '--playlist', help='load songs from playlist URL into .txt') + group.add_argument( + '-b', '--album', help='load songs from album URL into .txt') + group.add_argument( + '-u', '--username', + help="load songs from user's playlist into .txt") + parser.add_argument( '-m', '--manual', default=False, help='choose the song to download manually', action='store_true') @@ -59,10 +70,7 @@ def get_arguments(): type=str.upper, help='set log verbosity') - parsed = parser.parse_args() + parsed = parser.parse_args(raw_args) parsed.log_level = log_leveller(parsed.log_level) return parsed - - -parsed = get_arguments() diff --git a/core/internals.py b/core/internals.py index 7dacaab..ca7f6ed 100755 --- a/core/internals.py +++ b/core/internals.py @@ -1,5 +1,5 @@ from slugify import SLUG_OK, slugify -from core.logger import log +from core.const import log import os diff --git a/core/logger.py b/core/logger.py deleted file mode 100644 index 558e0a2..0000000 --- a/core/logger.py +++ /dev/null @@ -1,16 +0,0 @@ -import logzero -import logging - -_LOG_LEVELS_STR = ['INFO', 'WARNING', 'ERROR', 'DEBUG'] - -def log_leveller(log_level_str): - loggin_levels = [logging.INFO, logging.WARNING, logging.ERROR, logging.DEBUG] - log_level_str_index = _LOG_LEVELS_STR.index(log_level_str) - loggin_level = loggin_levels[log_level_str_index] - return loggin_level - - -log_format = ("%(color)s%(levelname)s:%(end_color)s %(message)s") -formatter = logzero.LogFormatter(fmt=log_format) -# create a default logger -log = logzero.setup_logger(formatter=formatter) diff --git a/core/metadata.py b/core/metadata.py index 829fea6..2e93f8b 100755 --- a/core/metadata.py +++ b/core/metadata.py @@ -1,7 +1,7 @@ from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover -from core.logger import log +from core.const import log import urllib.request diff --git a/core/spotify_tools.py b/core/spotify_tools.py index 0e0151a..5d4d9e5 100644 --- a/core/spotify_tools.py +++ b/core/spotify_tools.py @@ -4,9 +4,7 @@ from titlecase import titlecase import pprint from core import internals -from core import logger - -log = logger.log +from core.const import log def generate_token(): diff --git a/core/youtube_tools.py b/core/youtube_tools.py index afe7d30..70798a3 100644 --- a/core/youtube_tools.py +++ b/core/youtube_tools.py @@ -1,13 +1,12 @@ import pafy from core import internals -from core import arguments -from core.logger import log +from core import const import os import pprint -args = arguments.parsed +log = const.log def go_pafy(raw_song, meta_tags=None): @@ -36,15 +35,15 @@ def get_youtube_title(content, number=None): def download_song(file_name, content): """ Download the audio file from YouTube. """ - if args.input_ext in (".webm", ".m4a"): - link = content.getbestaudio(preftype=args.input_ext[1:]) + if const.args.input_ext in (".webm", ".m4a"): + link = content.getbestaudio(preftype=const.args.input_ext[1:]) else: return False if link: log.debug('Downloading from URL: ' + link.url) - filepath = '{0}{1}'.format(os.path.join(args.folder, file_name), - args.input_ext) + filepath = '{0}{1}'.format(os.path.join(const.args.folder, file_name), + const.args.input_ext) log.debug('Saving to: ' + filepath) link.download(filepath=filepath) return True @@ -63,7 +62,7 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5): 'maxResults' : 50, 'type' : 'video' } - if args.music_videos_only: + if const.args.music_videos_only: query['videoCategoryId'] = '10' if not meta_tags: @@ -97,7 +96,7 @@ def generate_youtube_url(raw_song, meta_tags, tries_remaining=5): log.debug(pprint.pformat(videos)) - if args.manual: + if const.args.manual: log.info(song) log.info('0. Skip downloading this song.\n') # fetch all video links on first page on YouTube diff --git a/spotdl.py b/spotdl.py index 905d243..d47ef67 100755 --- a/spotdl.py +++ b/spotdl.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 # -*- coding: UTF-8 -*- -from core import logger +from core import const +from core import handle from core import metadata from core import convert from core import internals from core import spotify_tools from core import youtube_tools -from core import arguments from slugify import slugify import spotipy import pafy @@ -24,10 +24,10 @@ def check_exists(music_file, raw_song, meta_tags): """ Check if the input song already exists in the given folder. """ log.debug('Cleaning any temp files and checking ' 'if "{}" already exists'.format(music_file)) - songs = os.listdir(args.folder) + songs = os.listdir(const.args.folder) for song in songs: if song.endswith('.temp'): - os.remove(os.path.join(args.folder, song)) + os.remove(os.path.join(const.args.folder, song)) continue # check if any song with similar name is already present in the given folder file_name = internals.sanitize_title(music_file) @@ -36,29 +36,29 @@ def check_exists(music_file, raw_song, meta_tags): if internals.is_spotify(raw_song): # check if the already downloaded song has correct metadata # if not, remove it and download again without prompt - already_tagged = metadata.compare(os.path.join(args.folder, song), + already_tagged = metadata.compare(os.path.join(const.args.folder, song), meta_tags) log.debug('Checking if it is already tagged correctly? {}', already_tagged) if not already_tagged: - os.remove(os.path.join(args.folder, song)) + os.remove(os.path.join(const.args.folder, song)) return False log.warning('"{}" already exists'.format(song)) - if args.overwrite == 'prompt': + if const.args.overwrite == 'prompt': log.info('"{}" has already been downloaded. ' 'Re-download? (y/N): '.format(song)) prompt = input('> ') if prompt.lower() == 'y': - os.remove(os.path.join(args.folder, song)) + os.remove(os.path.join(const.args.folder, song)) return False else: return True - elif args.overwrite == 'force': - os.remove(os.path.join(args.folder, song)) + elif const.args.overwrite == 'force': + os.remove(os.path.join(const.args.folder, song)) log.info('Overwriting "{}"'.format(song)) return False - elif args.overwrite == 'skip': + elif const.args.overwrite == 'skip': log.info('Skipping "{}"'.format(song)) return True return False @@ -157,32 +157,32 @@ def grab_single(raw_song, number=None): if not refined_songname == ' - ': songname = refined_songname - if args.dry_run: + if const.args.dry_run: return file_name = internals.sanitize_title(songname) if not check_exists(file_name, raw_song, meta_tags): if youtube_tools.download_song(file_name, content): - input_song = file_name + args.input_ext - output_song = file_name + args.output_ext + input_song = file_name + const.args.input_ext + output_song = file_name + const.args.output_ext print('') try: - convert.song(input_song, output_song, args.folder, - avconv=args.avconv) + convert.song(input_song, output_song, const.args.folder, + avconv=const.args.avconv) except FileNotFoundError: - encoder = 'avconv' if args.avconv else 'ffmpeg' + encoder = 'avconv' if const.args.avconv else 'ffmpeg' log.warning('Could not find {0}, skipping conversion'.format(encoder)) - args.output_ext = args.input_ext - output_song = file_name + args.output_ext + const.args.output_ext = const.args.input_ext + output_song = file_name + const.args.output_ext - if not args.input_ext == args.output_ext: - os.remove(os.path.join(args.folder, input_song)) + if not const.args.input_ext == const.args.output_ext: + os.remove(os.path.join(const.args.folder, input_song)) - if not args.no_metadata: + if not const.args.no_metadata: if metadata: - metadata.embed(os.path.join(args.folder, output_song), meta_tags) + metadata.embed(os.path.join(const.args.folder, output_song), meta_tags) else: log.warning('Could not find metadata') @@ -196,27 +196,27 @@ token = spotify_tools.generate_token() spotify = spotipy.Spotify(auth=token) if __name__ == '__main__': - args = arguments.parsed - internals.filter_path(args.folder) + const.args = handle.get_arguments() + internals.filter_path(const.args.folder) - logger.log = logger.logzero.setup_logger(formatter=logger.formatter, - level=args.log_level) - log = logger.log + const.log = const.logzero.setup_logger(formatter=const.formatter, + level=const.args.log_level) + log = const.log log.debug('Python version: {}'.format(sys.version)) log.debug('Platform: {}'.format(platform.platform())) - log.debug(pprint.pformat(args.__dict__)) + log.debug(pprint.pformat(const.args.__dict__)) try: - if args.song: - grab_single(raw_song=args.song) - elif args.list: - grab_list(text_file=args.list) - elif args.playlist: - grab_playlist(playlist=args.playlist) - elif args.album: - spotify_tools.grab_album(album=args.album) - elif args.username: - spotify_tools.feed_playlist(username=args.username) + if const.args.song: + grab_single(raw_song=const.args.song) + elif const.args.list: + grab_list(text_file=const.args.list) + elif const.args.playlist: + grab_playlist(playlist=const.args.playlist) + elif const.args.album: + spotify_tools.grab_album(album=const.args.album) + elif const.args.username: + spotify_tools.feed_playlist(username=const.args.username) # Actually we don't necessarily need this, but yeah... # Explicit is better than implicit! diff --git a/test/test_simple.py b/test/test_simple.py index 8100ed0..6077b93 100644 --- a/test/test_simple.py +++ b/test/test_simple.py @@ -1,32 +1,26 @@ # -*- coding: UTF-8 -*- -from spotdl import logger +from spotdl import const +from spotdl import handle import spotdl + import os +const.args = handle.get_arguments(to_group=False, raw_args='') +const.args.folder = 'test' +const.args.overwrite = 'skip' +const.args.log_level = handle.logging.DEBUG + +spotdl.args = const.args +spotdl.log = const.logzero.setup_logger(formatter=const.formatter, + level=const.args.log_level) + + raw_song = "Tony's Videos VERY SHORT VIDEO 28.10.2016" - -test_args = '-f test -ll debug -m --overwrite skip'.split() - -class TestArgs: - manual = False - input_ext = '.m4a' - output_ext = '.mp3' - folder = 'test' - log_level = logger.logging.DEBUG - overwrite = 'skip' - music_videos_only = False - -setattr(spotdl, "args", TestArgs()) - -spotdl.log = logger.logzero.setup_logger(formatter=logger.formatter, - level=spotdl.args.log_level) - - def test_youtube_url(): expect_url = 'http://youtube.com/watch?v=qOOcy2-tmbk' - url = spotdl.generate_youtube_url(raw_song, meta_tags=None) + url = spotdl.youtube_tools.generate_youtube_url(raw_song, meta_tags=None) assert url == expect_url @@ -34,8 +28,8 @@ def test_youtube_title(): global content global title expect_title = "Tony's Videos VERY SHORT VIDEO 28.10.2016" - content = spotdl.go_pafy(raw_song, meta_tags=None) - title = spotdl.get_youtube_title(content) + content = spotdl.youtube_tools.go_pafy(raw_song, meta_tags=None) + title = spotdl.youtube_tools.get_youtube_title(content) assert title == expect_title def test_check_exists(): @@ -50,7 +44,7 @@ def test_download(): expect_download = True # prerequisites for determining filename file_name = spotdl.internals.sanitize_title(title) - download = spotdl.download_song(file_name, content) + download = spotdl.youtube_tools.download_song(file_name, content) assert download == expect_download @@ -61,9 +55,9 @@ def test_convert(): file_name = spotdl.internals.sanitize_title(title) global input_song global output_song - input_song = file_name + spotdl.args.input_ext - output_song = file_name + spotdl.args.output_ext - convert = spotdl.convert.song(input_song, output_song, spotdl.args.folder) + input_song = file_name + const.args.input_ext + output_song = file_name + const.args.output_ext + convert = spotdl.convert.song(input_song, output_song, const.args.folder) assert convert == expect_convert @@ -73,8 +67,8 @@ def test_metadata(): meta_tags = spotdl.spotify_tools.generate_metadata(raw_song) file_name = spotdl.internals.sanitize_title(title) if meta_tags: - metadata_output = spotdl.metadata.embed(os.path.join(spotdl.args.folder, output_song), meta_tags) - metadata_input = spotdl.metadata.embed(os.path.join(spotdl.args.folder, input_song), meta_tags) + metadata_output = spotdl.metadata.embed(os.path.join(const.args.folder, output_song), meta_tags) + metadata_input = spotdl.metadata.embed(os.path.join(const.args.folder, input_song), meta_tags) else: metadata_input = None metadata_output = None @@ -85,7 +79,7 @@ def test_check_exists2(): expect_check = True # prerequisites for determining filename file_name = spotdl.internals.sanitize_title(title) - os.remove(os.path.join(spotdl.args.folder, input_song)) + os.remove(os.path.join(const.args.folder, input_song)) check = spotdl.check_exists(file_name, raw_song, meta_tags=None) - os.remove(os.path.join(spotdl.args.folder, output_song)) + os.remove(os.path.join(const.args.folder, output_song)) assert check == expect_check diff --git a/test/test_spotify.py b/test/test_spotify.py index 17e10a5..175197e 100644 --- a/test/test_spotify.py +++ b/test/test_spotify.py @@ -1,54 +1,48 @@ # -*- coding: UTF-8 -*- -from spotdl import logger +from spotdl import const +from spotdl import handle import spotdl + import os +const.args = handle.get_arguments(to_group=False, raw_args='') +const.args.folder = 'test' +const.args.overwrite = 'skip' +const.args.log_level = handle.logging.DEBUG + +spotdl.args = const.args +spotdl.log = const.logzero.setup_logger(formatter=const.formatter, + level=const.args.log_level) + + raw_song = 'http://open.spotify.com/track/0JlS7BXXD07hRmevDnbPDU' - -class TestArgs: - manual = False - input_ext = '.m4a' - output_ext = '.mp3' - folder = 'test' - log_level = 'DEBUG' - overwrite = 'skip' - music_videos_only = False - -test_args = TestArgs() -setattr(spotdl, "args", test_args) - -spotdl.log = logger.logzero.setup_logger(formatter=logger.formatter, - level=spotdl.args.log_level) -spotdl.internals.filter_path(spotdl.args.folder) - - def test_spotify_title(): expect_title = 'David André Østby - Intro' global meta_tags meta_tags = spotdl.spotify_tools.generate_metadata(raw_song) - title = spotdl.generate_songname(meta_tags) + title = spotdl.internals.generate_songname(meta_tags) assert title == expect_title -def youtube_url(): - expect_url = 'youtube.com/watch?v=rg1wfcty0BA' - url = spotdl.generate_youtube_url(raw_song, meta_tags) +def test_youtube_url(): + expect_url = 'http://youtube.com/watch?v=rg1wfcty0BA' + url = spotdl.youtube_tools.generate_youtube_url(raw_song, meta_tags) assert url == expect_url -def youtube_title(): +def test_youtube_title(): expect_title = 'Intro - David André Østby' - content = spotdl.go_pafy(raw_song, meta_tags) - title = spotdl.get_youtube_title(content) + content = spotdl.youtube_tools.go_pafy(raw_song, meta_tags) + title = spotdl.youtube_tools.get_youtube_title(content) assert title == expect_title def test_check_exists(): expect_check = False # prerequisites for determining filename - songname = spotdl.generate_songname(meta_tags) + songname = spotdl.internals.generate_songname(meta_tags) global file_name file_name = spotdl.internals.sanitize_title(songname) check = spotdl.check_exists(file_name, raw_song, meta_tags) @@ -58,8 +52,8 @@ def test_check_exists(): def test_download(): expect_download = True # prerequisites for determining filename - content = spotdl.go_pafy(raw_song, meta_tags) - download = spotdl.download_song(file_name, content) + content = spotdl.youtube_tools.go_pafy(raw_song, meta_tags) + download = spotdl.youtube_tools.download_song(file_name, content) assert download == expect_download