From 5add1fd0f8ca0cfb73929a5902314e86f69882c7 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Tue, 11 Jul 2017 20:32:26 +0530 Subject: [PATCH] Drop python 2 compatibility (#107) --- .travis.yml | 1 - ISSUE_TEMPLATE.md | 2 +- README.md | 2 +- core/convert.py | 4 --- core/metadata.py | 25 +++++++---------- core/misc.py | 42 +++++------------------------ spotdl.py | 66 ++++++++++++++++++++------------------------- test/test_single.py | 6 +---- 8 files changed, 47 insertions(+), 101 deletions(-) diff --git a/.travis.yml b/.travis.yml index f11a1a6..d87652f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "2.7" - "3.4" - "3.5" - "3.6" diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 0514b16..bb83018 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -17,7 +17,7 @@ Please follow the guide below - [ ] Other #### System information -- Your `python` version: `python 2.7` +- Your `python` version: `python 3.x` - Your operating system: `Ubuntu 16.04` ### Description diff --git a/README.md b/README.md index 0d67539..9984e63 100755 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ That's how your Music library will look like! -- This version supports Python 3, Python 2 compatibility was dropped. If you need to use Python 2 though, check out the `python2` branch. +- **This version supports Python 3**, Python 2 compatibility was dropped because of the way it deals with unicode. If you need to use Python 2 though, check out the `python2` branch. - Note: `play` and `lyrics` commands have been deprecated in the current brach since they were not of much use and created unnecessary clutter. You can still get them back by using `old` branch though. diff --git a/core/convert.py b/core/convert.py index 2e7e41d..7363ddb 100644 --- a/core/convert.py +++ b/core/convert.py @@ -1,14 +1,10 @@ import subprocess import os -import sys def song(input_song, output_song, avconv=False, verbose=False): """Do the audio format conversion.""" if not input_song == output_song: - if sys.version_info < (3, 0): - input_song = input_song.encode('utf-8') - output_song = output_song.encode('utf-8') print('Converting {0} to {1}'.format( input_song, output_song.split('.')[-1])) if avconv: diff --git a/core/metadata.py b/core/metadata.py index 4c88e1a..7df0a4d 100755 --- a/core/metadata.py +++ b/core/metadata.py @@ -1,26 +1,21 @@ from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover -import sys -# urllib2 is urllib.request in python3 -try: - import urllib2 -except ImportError: - import urllib.request as urllib2 +import urllib.request -def compare(file, metadata): - """Check if the input file title matches the expected title.""" +def compare(music_file, metadata): + """Check if the input music file title matches the expected title.""" already_tagged = False try: - if file.endswith('.mp3'): - audiofile = EasyID3('Music/' + file) + if music_file.endswith('.mp3'): + audiofile = EasyID3('Music/' + music_file) # fetch track title metadata already_tagged = audiofile['title'][0] == metadata['name'] - elif file.endswith('.m4a'): + elif music_file.endswith('.m4a'): tags = {'title': '\xa9nam'} - audiofile = MP4('Music/' + file) + audiofile = MP4('Music/' + music_file) # fetch track title metadata already_tagged = audiofile[tags['title']] == metadata['name'] except (KeyError, TypeError): @@ -30,8 +25,6 @@ def compare(file, metadata): def embed(music_file, meta_tags): """Embed metadata.""" - if sys.version_info < (3, 0): - music_file = music_file.encode('utf-8') if meta_tags is None: print('Could not find meta-tags') return None @@ -74,7 +67,7 @@ def embed_mp3(music_file, meta_tags): audiofile['copyright'] = meta_tags['copyright'] audiofile.save(v2_version=3) audiofile = ID3('Music/' + music_file) - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) + albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url']) audiofile["APIC"] = APIC(encoding=3, mime='image/jpeg', type=3, desc=u'Cover', data=albumart.read()) albumart.close() @@ -117,7 +110,7 @@ def embed_m4a(music_file, meta_tags): audiofile[tags['genre']] = meta_tags['genre'] if meta_tags['copyright']: audiofile[tags['copyright']] = meta_tags['copyright'] - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) + albumart = urllib.request.urlopen(meta_tags['album']['images'][0]['url']) audiofile[tags['albumart']] = [MP4Cover( albumart.read(), imageformat=MP4Cover.FORMAT_JPEG)] albumart.close() diff --git a/core/misc.py b/core/misc.py index ecbbc93..68d4e72 100755 --- a/core/misc.py +++ b/core/misc.py @@ -1,20 +1,15 @@ -import argparse import sys import os -from slugify import slugify +import argparse import spotipy.oauth2 as oauth2 - -try: - from urllib2 import quote -except ImportError: - from urllib.request import quote - +from urllib.request import quote +from slugify import slugify def input_link(links): """Let the user input a number.""" while True: try: - the_chosen_one = int(user_input('>> Choose your number: ')) + the_chosen_one = int(input('>> Choose your number: ')) if 1 <= the_chosen_one <= len(links): return links[the_chosen_one - 1] elif the_chosen_one == 0: @@ -25,14 +20,6 @@ def input_link(links): print('Choose a valid number!') -def user_input(string=''): - """Take input correctly for both Python 2 & 3.""" - if sys.version_info > (3, 0): - return input(string) - else: - return raw_input(string) - - def trim_song(file): """Remove the first song from file.""" with open(file, 'r') as file_in: @@ -88,14 +75,11 @@ def is_spotify(raw_song): def generate_filename(title): """Generate filename of the song to be downloaded.""" - # IMO python2 sucks dealing with unicode - title = fix_encoding(title) - title = fix_decoding(title) title = title.replace(' ', '_') # slugify removes any special characters filename = slugify(title, ok='-_()[]{}', lower=False) - return fix_encoding(filename) + return filename def generate_token(): @@ -109,26 +93,12 @@ def generate_token(): def generate_search_url(song): """Generate YouTube search URL for the given song.""" - # urllib2.quote() encodes URL with special characters + # urllib.request.quote() encodes URL with special characters url = u"https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q={0}".format( quote(song)) return url -def fix_encoding(query): - """Fix encoding issues in Python 2.""" - if sys.version_info < (3, 0): - query = query.encode('utf-8') - return query - - -def fix_decoding(query): - """Fix decoding issues in Python 2.""" - if sys.version_info < (3, 0): - query = query.decode('utf-8') - return query - - def filter_path(path): os.chdir(sys.path[0]) if not os.path.exists(path): diff --git a/spotdl.py b/spotdl.py index e02a798..5dd4d8e 100755 --- a/spotdl.py +++ b/spotdl.py @@ -9,22 +9,17 @@ from titlecase import titlecase from slugify import slugify import spotipy import pafy +import urllib.request import sys import os -# urllib2 is urllib.request in python3 -try: - import urllib2 -except ImportError: - import urllib.request as urllib2 - def generate_songname(raw_song): """Generate a string of the format '[artist] - [song]' for the given song.""" if misc.is_spotify(raw_song): tags = generate_metadata(raw_song) raw_song = u'{0} - {1}'.format(tags['artists'][0]['name'], tags['name']) - return misc.fix_encoding(raw_song) + return raw_song def generate_metadata(raw_song): @@ -63,7 +58,7 @@ def generate_youtube_url(raw_song): """Search for the song on YouTube and generate an URL to its video.""" song = generate_songname(raw_song) search_url = misc.generate_search_url(song) - item = urllib2.urlopen(search_url).read() + item = urllib.request.urlopen(search_url).read() # item = unicode(item, 'utf-8') items_parse = BeautifulSoup(item, "html.parser") check = 1 @@ -112,7 +107,7 @@ def go_pafy(raw_song): def get_youtube_title(content, number=None): """Get the YouTube video's title.""" - title = misc.fix_encoding(content.title) + title = content.title if number is None: return title else: @@ -131,7 +126,7 @@ def feed_playlist(username): # is None. Skip these. Also see Issue #91. if playlist['name'] is not None: print(u'{0:>5}.| {1:<30} | ({2} tracks)'.format( - check, misc.fix_encoding(playlist['name']), + check, playlist['name'], playlist['tracks']['total'])) links.append(playlist) check += 1 @@ -145,11 +140,11 @@ def feed_playlist(username): results = spotify.user_playlist( playlist['owner']['id'], playlist['id'], fields='tracks,next') print('') - file = u'{0}.txt'.format(slugify(playlist['name'], ok='-_()[]{}')) - print(u'Feeding {0} tracks to {1}'.format(playlist['tracks']['total'], file)) + text_file = u'{0}.txt'.format(slugify(playlist['name'], ok='-_()[]{}')) + print(u'Feeding {0} tracks to {1}'.format(playlist['tracks']['total'], text_file)) tracks = results['tracks'] - with open(file, 'a') as file_out: + with open(text_file, 'a') as file_out: while True: for item in tracks['items']: track = item['track'] @@ -186,21 +181,20 @@ def download_song(content): def check_exists(music_file, raw_song, islist=True): """Check if the input song already exists in the 'Music' folder.""" - files = os.listdir('Music') - for file in files: - if file.endswith('.temp'): - os.remove(u'Music/{0}'.format(file)) + songs = os.listdir('Music') + for song in songs: + if song.endswith('.temp'): + os.remove(u'Music/{0}'.format(song)) continue - # check if any file with similar name is already present in Music/ - dfile = misc.fix_decoding(file) - umfile = misc.fix_decoding(misc.generate_filename(music_file)) - if dfile.startswith(umfile): + # check if any song with similar name is already present in Music/ + umfile = misc.generate_filename(music_file) + if song.startswith(umfile): # check if the already downloaded song has correct metadata - already_tagged = metadata.compare(file, generate_metadata(raw_song)) + already_tagged = metadata.compare(song, generate_metadata(raw_song)) # if not, remove it and download again without prompt if misc.is_spotify(raw_song) and not already_tagged: - os.remove('Music/{0}'.format(file)) + os.remove('Music/{0}'.format(song)) return False # do not prompt and skip the current song @@ -209,22 +203,21 @@ def check_exists(music_file, raw_song, islist=True): return True # if downloading only single song, prompt to re-download else: - prompt = misc.user_input( - 'Song with same name has already been downloaded. ' - 'Re-download? (y/n): ').lower() + prompt = input('Song with same name has already been downloaded. ' + 'Re-download? (y/n): ').lower() if prompt == 'y': - os.remove('Music/{0}'.format(file)) + os.remove('Music/{0}'.format(song)) return False else: return True return False -def grab_list(file): +def grab_list(text_file): """Download all songs from the list.""" - with open(file, 'r') as listed: + with open(text_file, 'r') as listed: lines = (listed.read()).splitlines() - # ignore blank lines in file (if any) + # ignore blank lines in text_file (if any) try: lines.remove('') except ValueError: @@ -244,12 +237,12 @@ def grab_list(file): spotify = spotipy.Spotify(auth=new_token) grab_single(raw_song, number=number) # detect network problems - except (urllib2.URLError, TypeError, IOError): + except (urllib.request.URLError, TypeError, IOError): lines.append(raw_song) # remove the downloaded song from .txt - misc.trim_song(file) + misc.trim_song(text_file) # and append it to the last line in .txt - with open(file, 'a') as myfile: + with open(text_file, 'a') as myfile: myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') continue @@ -257,7 +250,7 @@ def grab_list(file): misc.grace_quit() finally: print('') - misc.trim_song(file) + misc.trim_song(text_file) number += 1 @@ -276,7 +269,6 @@ def grab_single(raw_song, number=None): # generate file name of the song to download music_file = misc.generate_filename(content.title) - music_file = misc.fix_decoding(music_file) if not check_exists(music_file, raw_song, islist=islist): if download_song(content): print('') @@ -285,7 +277,7 @@ def grab_single(raw_song, number=None): convert.song(input_song, output_song, avconv=args.avconv, verbose=args.verbose) if not args.input_ext == args.output_ext: - os.remove('Music/{0}'.format(misc.fix_encoding(input_song))) + os.remove('Music/{0}'.format(input_song)) meta_tags = generate_metadata(raw_song) if not args.no_metadata: metadata.embed(output_song, meta_tags) @@ -313,6 +305,6 @@ if __name__ == '__main__': if args.song: grab_single(raw_song=args.song) elif args.list: - grab_list(file=args.list) + grab_list(text_file=args.list) elif args.username: feed_playlist(username=args.username) diff --git a/test/test_single.py b/test/test_single.py index 6753c0e..dd73e4f 100644 --- a/test/test_single.py +++ b/test/test_single.py @@ -32,7 +32,6 @@ def test_check_exists(): expect_check = False content = spotdl.go_pafy(raw_song) music_file = spotdl.misc.generate_filename(content.title) - music_file = spotdl.misc.fix_decoding(music_file) check = spotdl.check_exists(music_file, raw_song, islist=True) assert check == expect_check @@ -49,7 +48,6 @@ def test_convert(): expect_convert = 0 content = spotdl.go_pafy(raw_song) music_file = spotdl.misc.generate_filename(content.title) - music_file = spotdl.misc.fix_decoding(music_file) input_song = music_file + spotdl.args.input_ext output_song = music_file + spotdl.args.output_ext convert = spotdl.convert.song(input_song, output_song) @@ -60,7 +58,6 @@ def test_metadata(): expect_metadata = True content = spotdl.go_pafy(raw_song) music_file = spotdl.misc.generate_filename(content.title) - music_file = spotdl.misc.fix_decoding(music_file) meta_tags = spotdl.generate_metadata(raw_song) output_song = music_file + spotdl.args.output_ext metadata_output = spotdl.metadata.embed(output_song, meta_tags) @@ -73,8 +70,7 @@ def test_check_exists2(): expect_check = True content = spotdl.go_pafy(raw_song) music_file = spotdl.misc.generate_filename(content.title) - music_file = spotdl.misc.fix_decoding(music_file) input_song = music_file + spotdl.args.input_ext - os.remove('Music/' + spotdl.misc.fix_encoding(input_song)) + os.remove('Music/' + input_song) check = spotdl.check_exists(music_file, raw_song, islist=True) assert check == expect_check