From f2acf30aa4ba7ab91b82aba4561f8850cae7eb05 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 00:26:53 +0530 Subject: [PATCH 01/56] Split code --- .gitignore | 7 +- core/misc.py | 80 ++++++++++++++++++++ spotdl.py | 208 +++++++++++++++------------------------------------ 3 files changed, 143 insertions(+), 152 deletions(-) create mode 100644 core/misc.py diff --git a/.gitignore b/.gitignore index 74459fb..c1792d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -Music/ -last_albumart.jpg -list.txt +/core/*.pyc +/Music/ +/last_albumart.jpg +/list.txt diff --git a/core/misc.py b/core/misc.py new file mode 100644 index 0000000..60b5c64 --- /dev/null +++ b/core/misc.py @@ -0,0 +1,80 @@ +import argparse +import sys +import os + +def getInputLink(links): + while True: + try: + the_chosen_one = int(raw_input('>> Choose your number: ')) + if the_chosen_one >= 1 and the_chosen_one <= len(links): + return links[the_chosen_one - 1] + elif the_chosen_one == 0: + return None + else: + print('Choose a valid number!') + except ValueError: + print('Choose a valid number!') + +def trimSong(file): + with open(file, 'r') as fin: + data = fin.read().splitlines(True) + with open(file, 'w') as fout: + fout.writelines(data[1:]) + +def getArgs(): + 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('-u', '--username', + help="load user's playlists into .txt") + + parser.add_argument('-n', '--no-convert', default=False, + help='skip the conversion process and meta-tags', action='store_true') + parser.add_argument('-m', '--manual', default=False, + help='choose the song to download manually', action='store_true') + parser.add_argument('-f', '--ffmpeg', default=False, + help='Use ffmpeg instead of libav for conversion. If not set defaults to libav', + action='store_true') + parser.add_argument('-v', '--verbose', default=False, + help='show debug output', action='store_true') + parser.add_argument('-i', '--input_ext', default='.m4a', + help='prefered input format .m4a or .webm (Opus)') + parser.add_argument('-o', '--output_ext', default='.mp3', + help='prefered output extension .mp3 or .m4a (AAC)') + + return parser.parse_args() + + +def isSpotify(raw_song): + if (len(raw_song) == 22 and raw_song.replace(" ", "%20") == raw_song) or (raw_song.find('spotify') > -1): + return True + else: + return False + +def generateSearchURL(song): + URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + \ + song.replace(" ", "%20") + return URL + +def fixEncoding(query): + if sys.version_info > (3, 0): + return query + else: + return query.encode('utf-8') + +def cleanTemp(): + for temp in os.listdir('Music/'): + if temp.endswith('.m4a.temp'): + os.remove('Music/' + temp) + +def graceQuit(): + print('') + print('') + print('Exitting..') + exit() diff --git a/spotdl.py b/spotdl.py index 84fdba4..25ca18f 100644 --- a/spotdl.py +++ b/spotdl.py @@ -1,10 +1,17 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- -# Usual import stuff +from core.misc import getInputLink +from core.misc import trimSong +from core.misc import getArgs +from core.misc import isSpotify +from core.misc import generateSearchURL +from core.misc import fixEncoding +from core.misc import cleanTemp +from core.misc import graceQuit from bs4 import BeautifulSoup from shutil import copyfileobj -from sys import path, version_info +import sys from slugify import slugify from titlecase import titlecase from mutagen.mp4 import MP4, MP4Cover @@ -14,35 +21,6 @@ import eyed3 import requests import pafy import os -import argparse - - -def getInputLink(links): - #for i in range(len(links)): - # links[i] = str(i + 1) + '. ' + links[i] - while True: - try: - the_chosen_one = int(raw_input('>> Choose your number: ')) - if the_chosen_one >= 1 and the_chosen_one <= len(links): - return links[the_chosen_one - 1] - elif the_chosen_one == 0: - return None - else: - print('Choose a valid number!') - except ValueError: - print('Choose a valid number!') - -# Check if input song is Spotify URL or just a song name - - -def isSpotify(raw_song): - if (len(raw_song) == 22 and raw_song.replace(" ", "%20") == raw_song) or (raw_song.find('spotify') > -1): - return True - else: - return False - -# [Artist] - [Song Name] - def generateSongName(raw_song): if isSpotify(raw_song): @@ -54,24 +32,28 @@ def generateSongName(raw_song): def generateMetaTags(raw_song): try: if isSpotify(raw_song): - return spotify.track(raw_song) + meta_tags = spotify.track(raw_song) else: - return spotify.search(raw_song, limit=1)['tracks']['items'][0] + meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] + artist_id = spotify.artist(meta_tags['artists'][0]['id']) + + try: + meta_tags['genre'] = titlecase(artist_id['genres'][0]) + except IndexError: + meta_tags['genre'] = None + + meta_tags['release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] + return meta_tags + except BaseException: return None -def generateSearchURL(song): - URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + \ - song.replace(" ", "%20") - return URL - - def generateYouTubeURL(raw_song): song = generateSongName(raw_song) searchURL = generateSearchURL(song) - items = requests.get(searchURL).text - items_parse = BeautifulSoup(items, "html.parser") + item = requests.get(searchURL).text + items_parse = BeautifulSoup(item, "html.parser") check = 1 if args.manual: links = [] @@ -166,7 +148,7 @@ def downloadSong(content): link.download(filepath='Music/' + music_file + input_ext) -def convertWithAvconv(music_file): +def convertWithAvconv(music_file, input_ext, output_ext, verbose): if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe' else: @@ -182,7 +164,7 @@ def convertWithAvconv(music_file): os.remove('Music/' + music_file + '.m4a') -def convertWithFfmpeg(music_file): +def convertWithFfmpeg(music_file, input_ext, output_ext, verbose): # What are the differences and similarities between ffmpeg, libav, and avconv? # https://stackoverflow.com/questions/9477115 # ffmeg encoders high to lower quality @@ -191,7 +173,8 @@ def convertWithFfmpeg(music_file): # on MacOS brew install ffmpeg --with-fdk-aac will do just that. Other OS? # https://trac.ffmpeg.org/wiki/Encode/AAC # - if args.verbose: + print(music_file, input_ext, output_ext, verbose) + if verbose: ffmpeg_pre = 'ffmpeg -y ' else: ffmpeg_pre = 'ffmpeg -hide_banner -nostats -v panic -y ' @@ -214,7 +197,7 @@ def convertWithFfmpeg(music_file): print('Unknown formats. Unable to convert.', input_ext, output_ext) return - if args.verbose: + if verbose: print(ffmpeg_pre + '-i "Music/' + music_file + input_ext + '" ' + ffmpeg_params + @@ -254,14 +237,17 @@ def checkExists(music_file, raw_song, islist): return True # Remove song from file once downloaded - - -def trimSong(file): - with open(file, 'r') as fin: - data = fin.read().splitlines(True) - with open(file, 'w') as fout: - fout.writelines(data[1:]) - +def fixSong(music_file, meta_tags, output_ext): + if meta_tags is None: + print('Could not find meta-tags') + elif output_ext == '.m4a': + print('Fixing meta-tags') + fixSongM4A(music_file, meta_tags) + elif output_ext == '.mp3': + print('Fixing meta-tags') + fixSongMP3(music_file, meta_tags) + else: + print('Cannot embed meta-tags into given output extension') def fixSongMP3(music_file, meta_tags): audiofile = eyed3.load("Music/" + music_file + '.mp3') @@ -269,23 +255,15 @@ def fixSongMP3(music_file, meta_tags): audiofile.tag.album_artist = meta_tags['artists'][0]['name'] audiofile.tag.album = meta_tags['album']['name'] audiofile.tag.title = meta_tags['name'] - artist = spotify.artist(meta_tags['artists'][0]['id']) - try: - audiofile.tag.genre = titlecase(artist['genres'][0]) - except IndexError: - pass + audiofile.tag.genre = meta_tags['genre'] audiofile.tag.track_num = meta_tags['track_number'] audiofile.tag.disc_num = meta_tags['disc_number'] - audiofile.tag.release_date = spotify.album( - meta_tags['album']['id'])['release_date'] - albumart = ( - requests.get( - meta_tags['album']['images'][0]['url'], - stream=True)).raw + audiofile.tag.release_date = meta_tags['release_date'] + albumart = requests.get(meta_tags['album']['images'][0]['url'], stream=True).raw with open('last_albumart.jpg', 'wb') as out_file: copyfileobj(albumart, out_file) - albumart = open("last_albumart.jpg", "rb").read() - audiofile.tag.images.set(3, albumart, "image/jpeg") + with open('last_albumart.jpg', 'rb') as albumart: + audiofile.tag.images.set(3, albumart.read(), 'image/jpeg') audiofile.tag.save(version=(2, 3, 0)) @@ -310,47 +288,25 @@ def fixSongM4A(music_file, meta_tags): audiofile[tags['artist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] - artist = spotify.artist(meta_tags['artists'][0]['id']) - try: - audiofile[tags['genre']] = titlecase(artist['genres'][0]) - except IndexError: - pass - album = spotify.album(meta_tags['album']['id']) - audiofile[tags['year']] = album['release_date'] + audiofile[tags['genre']] = meta_tags['genre'] + audiofile[tags['year']] = meta_tags['release_date'] audiofile[tags['track']] = [(meta_tags['track_number'], 0)] audiofile[tags['disk']] = [(meta_tags['disc_number'], 0)] - albumart = ( - requests.get(meta_tags['album']['images'][0]['url'], stream=True)).raw + albumart = requests.get(meta_tags['album']['images'][0]['url'], stream=True).raw with open('last_albumart.jpg', 'wb') as out_file: copyfileobj(albumart, out_file) - with open("last_albumart.jpg", "rb") as f: - audiofile["covr"] = [ - MP4Cover( - f.read(), - imageformat=MP4Cover.FORMAT_JPEG)] + with open('last_albumart.jpg', 'rb') as albumart: + audiofile["covr"] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] audiofile.save() - -def convertSong(music_file): +def convertSong(music_file, input_ext, output_ext, ffmpeg, verbose): print('Converting ' + music_file + input_ext + ' to ' + output_ext[1:]) - if args.ffmpeg: - convertWithFfmpeg(music_file) + if ffmpeg: + convertWithFfmpeg(music_file, input_ext, output_ext, verbose) else: - convertWithAvconv(music_file) + convertWithAvconv(music_file, input_ext, output_ext, verbose) -def fixSong(music_file, meta_tags): - if meta_tags is None: - print('Could not find meta-tags') - elif output_ext == '.m4a': - print('Fixing meta-tags') - fixSongM4A(music_file, meta_tags) - elif output_ext == '.mp3': - print('Fixing meta-tags') - fixSongMP3(music_file, meta_tags) - else: - print('Cannot embed meta-tags into given output extension') - # Logic behind preparing the song to download to finishing meta-tags @@ -368,20 +324,13 @@ def grabSingle(raw_song, number=None): downloadSong(content) print('') if not args.no_convert: - convertSong(music_file) + convertSong(music_file, input_ext, output_ext, args.ffmpeg, args.verbose) meta_tags = generateMetaTags(raw_song) - fixSong(music_file, meta_tags) + fixSong(music_file, meta_tags, output_ext) # Fix python2 encoding issues -def fixEncoding(query): - if version_info > (3, 0): - return query - else: - return query.encode('utf-8') - - def grabList(file): lines = open(file, 'r').read() lines = lines.splitlines() @@ -410,56 +359,17 @@ def grabList(file): print('Failed to download song. Will retry after other songs.') -def getArgs(argv=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('-u', '--username', - help="load user's playlists into .txt") - - parser.add_argument('-n', '--no-convert', default=False, - help='skip the conversion process and meta-tags', action='store_true') - parser.add_argument('-m', '--manual', default=False, - help='choose the song to download manually', action='store_true') - parser.add_argument('-f', '--ffmpeg', default=False, - help='Use ffmpeg instead of libav for conversion. If not set defaults to libav', - action='store_true') - parser.add_argument('-v', '--verbose', default=False, - help='show debug output', action='store_true') - parser.add_argument('-i', '--input_ext', default='.m4a', - help='prefered input format .m4a or .webm (Opus)') - parser.add_argument('-o', '--output_ext', default='.mp3', - help='prefered output extension .mp3 or .m4a (AAC)') - - return parser.parse_args(argv) - - -def graceQuit(): - print('') - print('') - print('Exitting..') - exit() - - if __name__ == '__main__': # Python 3 compatibility - if version_info > (3, 0): + if sys.version_info > (3, 0): raw_input = input - os.chdir(path[0]) + os.chdir(sys.path[0]) if not os.path.exists("Music"): os.makedirs("Music") - for temp in os.listdir('Music/'): - if temp.endswith('.m4a.temp'): - os.remove('Music/' + temp) + cleanTemp() # Please respect this user token :) oauth2 = oauth2.SpotifyClientCredentials( @@ -470,7 +380,7 @@ if __name__ == '__main__': # Set up arguments args = getArgs() - + print(args) if not args.verbose: eyed3.log.setLevel("ERROR") From 192225898655d8f4a7a3a32a4ba24c1b6663d754 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Thu, 15 Jun 2017 00:28:54 +0530 Subject: [PATCH 02/56] Add __init__.py --- core/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 core/__init__.py diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/core/__init__.py @@ -0,0 +1 @@ + From 447e011d4f7f6da82d33ed1a685de17b41ed67cf Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 01:03:22 +0530 Subject: [PATCH 03/56] Made some minor fixes based on input output extensions --- spotdl.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/spotdl.py b/spotdl.py index 25ca18f..00bfcd4 100644 --- a/spotdl.py +++ b/spotdl.py @@ -157,11 +157,11 @@ def convertWithAvconv(music_file, input_ext, output_ext, verbose): avconv_path + ' -loglevel 0 -i "' + 'Music/' + music_file + - '.m4a" -ab 192k "' + + input_ext + '" -ab 192k "' + 'Music/' + music_file + - '.mp3"') - os.remove('Music/' + music_file + '.m4a') + output_ext + '"') + os.remove('Music/' + music_file + input_ext) def convertWithFfmpeg(music_file, input_ext, output_ext, verbose): @@ -242,7 +242,7 @@ def fixSong(music_file, meta_tags, output_ext): print('Could not find meta-tags') elif output_ext == '.m4a': print('Fixing meta-tags') - fixSongM4A(music_file, meta_tags) + fixSongM4A(music_file, meta_tags, output_ext) elif output_ext == '.mp3': print('Fixing meta-tags') fixSongMP3(music_file, meta_tags) @@ -255,7 +255,8 @@ def fixSongMP3(music_file, meta_tags): audiofile.tag.album_artist = meta_tags['artists'][0]['name'] audiofile.tag.album = meta_tags['album']['name'] audiofile.tag.title = meta_tags['name'] - audiofile.tag.genre = meta_tags['genre'] + if meta_tags['genre']: + audiofile.tag.genre = meta_tags['genre'] audiofile.tag.track_num = meta_tags['track_number'] audiofile.tag.disc_num = meta_tags['disc_number'] audiofile.tag.release_date = meta_tags['release_date'] @@ -267,7 +268,7 @@ def fixSongMP3(music_file, meta_tags): audiofile.tag.save(version=(2, 3, 0)) -def fixSongM4A(music_file, meta_tags): +def fixSongM4A(music_file, meta_tags, output_ext): # eyed serves only mp3 not aac so using mutagen # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html @@ -288,7 +289,8 @@ def fixSongM4A(music_file, meta_tags): audiofile[tags['artist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] - audiofile[tags['genre']] = meta_tags['genre'] + if meta_tags['genre']: + audiofile[tags['genre']] = meta_tags['genre'] audiofile[tags['year']] = meta_tags['release_date'] audiofile[tags['track']] = [(meta_tags['track_number'], 0)] audiofile[tags['disk']] = [(meta_tags['disc_number'], 0)] @@ -300,11 +302,15 @@ def fixSongM4A(music_file, meta_tags): audiofile.save() def convertSong(music_file, input_ext, output_ext, ffmpeg, verbose): - print('Converting ' + music_file + input_ext + ' to ' + output_ext[1:]) - if ffmpeg: - convertWithFfmpeg(music_file, input_ext, output_ext, verbose) + print(music_file, input_ext, output_ext, ffmpeg, verbose) + if not input_ext == output_ext: + print('Converting ' + music_file + input_ext + ' to ' + output_ext[1:]) + if ffmpeg: + convertWithFfmpeg(music_file, input_ext, output_ext, verbose) + else: + convertWithAvconv(music_file, input_ext, output_ext, verbose) else: - convertWithAvconv(music_file, input_ext, output_ext, verbose) + print('Skipping conversion since input_ext = output_ext') # Logic behind preparing the song to download to finishing meta-tags @@ -324,9 +330,9 @@ def grabSingle(raw_song, number=None): downloadSong(content) print('') if not args.no_convert: - convertSong(music_file, input_ext, output_ext, args.ffmpeg, args.verbose) + convertSong(music_file, args.input_ext, args.output_ext, args.ffmpeg, args.verbose) meta_tags = generateMetaTags(raw_song) - fixSong(music_file, meta_tags, output_ext) + fixSong(music_file, meta_tags, args.output_ext) # Fix python2 encoding issues From f515cc61acc00d98b51bd24d2025a8ec532c4ead Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 03:47:34 +0530 Subject: [PATCH 04/56] Shift completely to urllib2 and mutagen --- core/misc.py | 5 --- spotdl.py | 118 +++++++++++++++++++++------------------------------ 2 files changed, 49 insertions(+), 74 deletions(-) diff --git a/core/misc.py b/core/misc.py index 60b5c64..f207a6b 100644 --- a/core/misc.py +++ b/core/misc.py @@ -68,11 +68,6 @@ def fixEncoding(query): else: return query.encode('utf-8') -def cleanTemp(): - for temp in os.listdir('Music/'): - if temp.endswith('.m4a.temp'): - os.remove('Music/' + temp) - def graceQuit(): print('') print('') diff --git a/spotdl.py b/spotdl.py index 00bfcd4..244e146 100644 --- a/spotdl.py +++ b/spotdl.py @@ -7,18 +7,18 @@ from core.misc import getArgs from core.misc import isSpotify from core.misc import generateSearchURL from core.misc import fixEncoding -from core.misc import cleanTemp from core.misc import graceQuit from bs4 import BeautifulSoup from shutil import copyfileobj import sys from slugify import slugify from titlecase import titlecase +from mutagen.id3 import ID3, APIC +from mutagen.easyid3 import EasyID3 from mutagen.mp4 import MP4, MP4Cover import spotipy import spotipy.oauth2 as oauth2 -import eyed3 -import requests +import urllib2 import pafy import os @@ -28,7 +28,6 @@ def generateSongName(raw_song): raw_song = tags['artists'][0]['name'] + ' - ' + tags['name'] return raw_song - def generateMetaTags(raw_song): try: if isSpotify(raw_song): @@ -48,11 +47,10 @@ def generateMetaTags(raw_song): except BaseException: return None - def generateYouTubeURL(raw_song): song = generateSongName(raw_song) searchURL = generateSearchURL(song) - item = requests.get(searchURL).text + item = urllib2.urlopen(searchURL).read() items_parse = BeautifulSoup(item, "html.parser") check = 1 if args.manual: @@ -80,7 +78,6 @@ def generateYouTubeURL(raw_song): full_link = "youtube.com" + result return full_link - def goPafy(raw_song): trackURL = generateYouTubeURL(raw_song) if trackURL is None: @@ -88,7 +85,6 @@ def goPafy(raw_song): else: return pafy.new(trackURL) - def getYouTubeTitle(content, number): title = content.title if number is None: @@ -96,7 +92,6 @@ def getYouTubeTitle(content, number): else: return str(number) + '. ' + title - def feedTracks(file, tracks): with open(file, 'a') as fout: for item in tracks['items']: @@ -106,7 +101,6 @@ def feedTracks(file, tracks): except KeyError: pass - def feedPlaylist(username): playlists = spotify.user_playlists(username) links = [] @@ -128,26 +122,22 @@ def feedPlaylist(username): feedTracks(file, tracks) # Generate name for the song to be downloaded - - def generateFileName(content): title = (content.title).replace(' ', '_') title = slugify(title, ok='-_()[]{}', lower=False) return fixEncoding(title) - -def downloadSong(content): +def downloadSong(content, input_ext): music_file = generateFileName(content) if input_ext == '.webm': link = content.getbestaudio(preftype='webm') if link is not None: link.download(filepath='Music/' + music_file + input_ext) else: - link = content.getbestaudio(preftype="m4a") + link = content.getbestaudio(preftype='m4a') if link is not None: link.download(filepath='Music/' + music_file + input_ext) - def convertWithAvconv(music_file, input_ext, output_ext, verbose): if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe' @@ -163,7 +153,6 @@ def convertWithAvconv(music_file, input_ext, output_ext, verbose): output_ext + '"') os.remove('Music/' + music_file + input_ext) - def convertWithFfmpeg(music_file, input_ext, output_ext, verbose): # What are the differences and similarities between ffmpeg, libav, and avconv? # https://stackoverflow.com/questions/9477115 @@ -210,28 +199,28 @@ def convertWithFfmpeg(music_file, input_ext, output_ext, verbose): '"Music/' + music_file + output_ext + '" ') os.remove('Music/' + music_file + input_ext) - def checkExists(music_file, raw_song, islist): - if os.path.exists("Music/" + music_file + input_ext + ".temp"): - os.remove("Music/" + music_file + input_ext + ".temp") - if args.no_convert: - extension = input_ext - else: - extension = output_ext - if os.path.isfile("Music/" + music_file + extension): - if extension == '.mp3': - audiofile = eyed3.load("Music/" + music_file + extension) - if isSpotify(raw_song) and not audiofile.tag.title == ( - generateMetaTags(raw_song))['name']: - os.remove("Music/" + music_file + extension) - return False + files = os.listdir("Music") + for file in files: + if file.endswith(".temp"): + os.remove("Music/" + file) + continue + + if file.startswith(music_file): + #audiofile = eyed3.load("Music/" + music_file + output_ext) + #if isSpotify(raw_song) and not audiofile.tag.title == ( + # generateMetaTags(raw_song))['name']: + # os.remove("Music/" + music_file + output_ext) + # return False + os.remove("Music/" + file) + return False if islist: return True else: prompt = raw_input( 'Song with same name has already been downloaded. Re-download? (y/n): ').lower() if prompt == "y": - os.remove("Music/" + music_file + extension) + os.remove("Music/" + file) return False else: return True @@ -245,28 +234,27 @@ def fixSong(music_file, meta_tags, output_ext): fixSongM4A(music_file, meta_tags, output_ext) elif output_ext == '.mp3': print('Fixing meta-tags') - fixSongMP3(music_file, meta_tags) + fixSongMP3(music_file, meta_tags, output_ext) else: print('Cannot embed meta-tags into given output extension') -def fixSongMP3(music_file, meta_tags): - audiofile = eyed3.load("Music/" + music_file + '.mp3') - audiofile.tag.artist = meta_tags['artists'][0]['name'] - audiofile.tag.album_artist = meta_tags['artists'][0]['name'] - audiofile.tag.album = meta_tags['album']['name'] - audiofile.tag.title = meta_tags['name'] - if meta_tags['genre']: - audiofile.tag.genre = meta_tags['genre'] - audiofile.tag.track_num = meta_tags['track_number'] - audiofile.tag.disc_num = meta_tags['disc_number'] - audiofile.tag.release_date = meta_tags['release_date'] - albumart = requests.get(meta_tags['album']['images'][0]['url'], stream=True).raw - with open('last_albumart.jpg', 'wb') as out_file: - copyfileobj(albumart, out_file) - with open('last_albumart.jpg', 'rb') as albumart: - audiofile.tag.images.set(3, albumart.read(), 'image/jpeg') - audiofile.tag.save(version=(2, 3, 0)) - +def fixSongMP3(music_file, meta_tags, output_ext): + audiofile = EasyID3('Music/' + music_file + output_ext) + #audiofile = ID3('Music/' + music_file + output_ext) + audiofile['artist'] = "Artist" + audiofile['albumartist'] = "Artist" + audiofile['album'] = "Album" + audiofile['title'] = "Title" + audiofile['genre'] = "genre" + audiofile['tracknumber'] = "1" + audiofile['discnumber'] = "2" + audiofile['date'] = "2000" + audiofile.save(v2_version=3) + audiofile = ID3('Music/' + music_file + output_ext) + albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']).read() + audiofile["APIC"] = APIC(encoding=3, mime='image/jpeg', type=3, desc=u'Cover', data=albumart) + albumart.close() + audiofile.save(v2_version=3) def fixSongM4A(music_file, meta_tags, output_ext): # eyed serves only mp3 not aac so using mutagen @@ -294,11 +282,9 @@ def fixSongM4A(music_file, meta_tags, output_ext): audiofile[tags['year']] = meta_tags['release_date'] audiofile[tags['track']] = [(meta_tags['track_number'], 0)] audiofile[tags['disk']] = [(meta_tags['disc_number'], 0)] - albumart = requests.get(meta_tags['album']['images'][0]['url'], stream=True).raw - with open('last_albumart.jpg', 'wb') as out_file: - copyfileobj(albumart, out_file) - with open('last_albumart.jpg', 'rb') as albumart: - audiofile["covr"] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] + albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']).read() + audiofile["covr"] = [ MP4Cover(albumart, imageformat=MP4Cover.FORMAT_JPEG) ] + albumart.close() audiofile.save() def convertSong(music_file, input_ext, output_ext, ffmpeg, verbose): @@ -314,8 +300,6 @@ def convertSong(music_file, input_ext, output_ext, ffmpeg, verbose): # Logic behind preparing the song to download to finishing meta-tags - - def grabSingle(raw_song, number=None): if number: islist = True @@ -327,7 +311,7 @@ def grabSingle(raw_song, number=None): print(getYouTubeTitle(content, number)) music_file = generateFileName(content) if not checkExists(music_file, raw_song, islist=islist): - downloadSong(content) + downloadSong(content, args.input_ext) print('') if not args.no_convert: convertSong(music_file, args.input_ext, args.output_ext, args.ffmpeg, args.verbose) @@ -335,8 +319,6 @@ def grabSingle(raw_song, number=None): fixSong(music_file, meta_tags, args.output_ext) # Fix python2 encoding issues - - def grabList(file): lines = open(file, 'r').read() lines = lines.splitlines() @@ -375,8 +357,6 @@ if __name__ == '__main__': if not os.path.exists("Music"): os.makedirs("Music") - cleanTemp() - # Please respect this user token :) oauth2 = oauth2.SpotifyClientCredentials( client_id='4fe3fecfe5334023a1472516cc99d805', @@ -390,12 +370,12 @@ if __name__ == '__main__': if not args.verbose: eyed3.log.setLevel("ERROR") - if args.ffmpeg: - input_ext = args.input_ext - output_ext = args.output_ext - else: - input_ext = '.m4a' - output_ext = '.mp3' + #if args.ffmpeg: + # input_ext = args.input_ext + # output_ext = args.output_ext + #else: + # input_ext = '.m4a' + # output_ext = '.mp3' if args.song: grabSingle(raw_song=args.song) From 958f9db417acac4195040093662170e47750afa0 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 03:58:53 +0530 Subject: [PATCH 05/56] Generalize MP3 metadata --- spotdl.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/spotdl.py b/spotdl.py index 244e146..9c5aabb 100644 --- a/spotdl.py +++ b/spotdl.py @@ -207,7 +207,8 @@ def checkExists(music_file, raw_song, islist): continue if file.startswith(music_file): - #audiofile = eyed3.load("Music/" + music_file + output_ext) + # FIXME + #audiofile = mutagen.load("Music/" + music_file + output_ext) #if isSpotify(raw_song) and not audiofile.tag.title == ( # generateMetaTags(raw_song))['name']: # os.remove("Music/" + music_file + output_ext) @@ -240,19 +241,19 @@ def fixSong(music_file, meta_tags, output_ext): def fixSongMP3(music_file, meta_tags, output_ext): audiofile = EasyID3('Music/' + music_file + output_ext) - #audiofile = ID3('Music/' + music_file + output_ext) - audiofile['artist'] = "Artist" - audiofile['albumartist'] = "Artist" - audiofile['album'] = "Album" - audiofile['title'] = "Title" - audiofile['genre'] = "genre" - audiofile['tracknumber'] = "1" - audiofile['discnumber'] = "2" - audiofile['date'] = "2000" + audiofile['artist'] = meta_tags['artists'][0]['name'] + audiofile['albumartist'] = meta_tags['artists'][0]['name'] + audiofile['album'] = meta_tags['album']['name'] + audiofile['title'] = meta_tags['name'] + if meta_tags['genre']: + audiofile['genre'] = meta_tags['genre'] + audiofile['tracknumber'] = [meta_tags['track_number'], 0] + audiofile['discnumber'] = [meta_tags['disc_number'], 0] + audiofile['date'] = meta_tags['release_date'] audiofile.save(v2_version=3) audiofile = ID3('Music/' + music_file + output_ext) - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']).read() - audiofile["APIC"] = APIC(encoding=3, mime='image/jpeg', type=3, desc=u'Cover', data=albumart) + albumart = urllib2.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() audiofile.save(v2_version=3) @@ -273,6 +274,7 @@ def fixSongM4A(music_file, meta_tags, output_ext): 'disk': 'disk', 'cpil': 'cpil', 'tempo': 'tmpo'} + audiofile = MP4('Music/' + music_file + output_ext) audiofile[tags['artist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] @@ -282,8 +284,8 @@ def fixSongM4A(music_file, meta_tags, output_ext): audiofile[tags['year']] = meta_tags['release_date'] audiofile[tags['track']] = [(meta_tags['track_number'], 0)] audiofile[tags['disk']] = [(meta_tags['disc_number'], 0)] - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']).read() - audiofile["covr"] = [ MP4Cover(albumart, imageformat=MP4Cover.FORMAT_JPEG) ] + albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) + audiofile["covr"] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() audiofile.save() @@ -367,8 +369,6 @@ if __name__ == '__main__': # Set up arguments args = getArgs() print(args) - if not args.verbose: - eyed3.log.setLevel("ERROR") #if args.ffmpeg: # input_ext = args.input_ext From 9bfebd476a99e4134569e0896b1d7d53d9c91607 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Thu, 15 Jun 2017 04:01:02 +0530 Subject: [PATCH 06/56] Remove requests and eyeD3 --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 903eeb1..ef67238 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,8 @@ pathlib >= 1.0.1 -requests >= 2.17.3 BeautifulSoup4 >= 0.4.13 youtube_dl >= 2017.5.1 pafy >= 0.5.3.1 spotipy >= 2.4.4 -eyeD3 >= 0.8 mutagen >= 1.37 unicode-slugify >= 0.1.3 titlecase >= 0.10.0 From 1c629051f4ce3cef4c35219742881be07b4c06ab Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Thu, 15 Jun 2017 04:05:24 +0530 Subject: [PATCH 07/56] Load albumart from URL directly into memory --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c1792d6..9ed708d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /core/*.pyc /Music/ -/last_albumart.jpg -/list.txt +/*.txt From 4e4d7320b13f1e15c5b6b8d5bdc324d4cde0e68a Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 12:10:32 +0530 Subject: [PATCH 08/56] Rename to functions to snakecase --- core/misc.py | 16 +++---- spotdl.py | 121 +++++++++++++++++++++++++-------------------------- 2 files changed, 67 insertions(+), 70 deletions(-) diff --git a/core/misc.py b/core/misc.py index f207a6b..cd0785a 100644 --- a/core/misc.py +++ b/core/misc.py @@ -2,7 +2,7 @@ import argparse import sys import os -def getInputLink(links): +def input_link(links): while True: try: the_chosen_one = int(raw_input('>> Choose your number: ')) @@ -15,13 +15,13 @@ def getInputLink(links): except ValueError: print('Choose a valid number!') -def trimSong(file): +def trim_song(file): with open(file, 'r') as fin: data = fin.read().splitlines(True) with open(file, 'w') as fout: fout.writelines(data[1:]) -def getArgs(): +def get_arguments(): parser = argparse.ArgumentParser(description='Download and convert songs \ from Spotify, Youtube etc.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -33,7 +33,6 @@ def getArgs(): help='download songs from a file') group.add_argument('-u', '--username', help="load user's playlists into .txt") - parser.add_argument('-n', '--no-convert', default=False, help='skip the conversion process and meta-tags', action='store_true') parser.add_argument('-m', '--manual', default=False, @@ -50,25 +49,24 @@ def getArgs(): return parser.parse_args() - -def isSpotify(raw_song): +def is_spotify(raw_song): if (len(raw_song) == 22 and raw_song.replace(" ", "%20") == raw_song) or (raw_song.find('spotify') > -1): return True else: return False -def generateSearchURL(song): +def generate_search_URL(song): URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + \ song.replace(" ", "%20") return URL -def fixEncoding(query): +def fix_encoding(query): if sys.version_info > (3, 0): return query else: return query.encode('utf-8') -def graceQuit(): +def grace_quit(): print('') print('') print('Exitting..') diff --git a/spotdl.py b/spotdl.py index 9c5aabb..655c3e8 100644 --- a/spotdl.py +++ b/spotdl.py @@ -1,15 +1,14 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- -from core.misc import getInputLink -from core.misc import trimSong -from core.misc import getArgs -from core.misc import isSpotify -from core.misc import generateSearchURL -from core.misc import fixEncoding -from core.misc import graceQuit +from core.misc import input_link +from core.misc import trim_song +from core.misc import get_arguments +from core.misc import is_spotify +from core.misc import generate_search_URL +from core.misc import fix_encoding +from core.misc import grace_quit from bs4 import BeautifulSoup -from shutil import copyfileobj import sys from slugify import slugify from titlecase import titlecase @@ -22,15 +21,15 @@ import urllib2 import pafy import os -def generateSongName(raw_song): - if isSpotify(raw_song): - tags = generateMetaTags(raw_song) +def generate_song_name(raw_song): + if is_spotify(raw_song): + tags = generate_metadata(raw_song) raw_song = tags['artists'][0]['name'] + ' - ' + tags['name'] return raw_song -def generateMetaTags(raw_song): +def generate_metadata(raw_song): try: - if isSpotify(raw_song): + if is_spotify(raw_song): meta_tags = spotify.track(raw_song) else: meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] @@ -47,9 +46,9 @@ def generateMetaTags(raw_song): except BaseException: return None -def generateYouTubeURL(raw_song): - song = generateSongName(raw_song) - searchURL = generateSearchURL(song) +def generate_YouTube_URL(raw_song): + song = generate_song_name(raw_song) + searchURL = generate_search_URL(song) item = urllib2.urlopen(searchURL).read() items_parse = BeautifulSoup(item, "html.parser") check = 1 @@ -64,7 +63,7 @@ def generateYouTubeURL(raw_song): links.append(x.find('a')['href']) check += 1 print('') - result = getInputLink(links) + result = input_link(links) if result is None: return None else: @@ -78,21 +77,21 @@ def generateYouTubeURL(raw_song): full_link = "youtube.com" + result return full_link -def goPafy(raw_song): - trackURL = generateYouTubeURL(raw_song) +def go_pafy(raw_song): + trackURL = generate_YouTube_URL(raw_song) if trackURL is None: return None else: return pafy.new(trackURL) -def getYouTubeTitle(content, number): +def get_YouTube_title(content, number): title = content.title if number is None: return title else: return str(number) + '. ' + title -def feedTracks(file, tracks): +def feed_tracks(file, tracks): with open(file, 'a') as fout: for item in tracks['items']: track = item['track'] @@ -101,34 +100,34 @@ def feedTracks(file, tracks): except KeyError: pass -def feedPlaylist(username): +def feed_playlist(username): playlists = spotify.user_playlists(username) links = [] check = 1 for playlist in playlists['items']: - print(str(check) + '. ' + fixEncoding(playlist['name']) + ' (' + str(playlist['tracks']['total']) + ' tracks)') + print(str(check) + '. ' + fix_encoding(playlist['name']) + ' (' + str(playlist['tracks']['total']) + ' tracks)') links.append(playlist) check += 1 print('') - playlist = getInputLink(links) + playlist = input_link(links) results = spotify.user_playlist(playlist['owner']['id'], playlist['id'], fields="tracks,next") print('') file = slugify(playlist['name'], ok='-_()[]{}') + '.txt' print('Feeding ' + str(playlist['tracks']['total']) + ' tracks to ' + file) tracks = results['tracks'] - feedTracks(file, tracks) + feed_tracks(file, tracks) while tracks['next']: tracks = spotify.next(tracks) - feedTracks(file, tracks) + feed_tracks(file, tracks) # Generate name for the song to be downloaded -def generateFileName(content): +def generate_filename(content): title = (content.title).replace(' ', '_') title = slugify(title, ok='-_()[]{}', lower=False) - return fixEncoding(title) + return fix_encoding(title) -def downloadSong(content, input_ext): - music_file = generateFileName(content) +def download_song(content, input_ext): + music_file = generate_filename(content) if input_ext == '.webm': link = content.getbestaudio(preftype='webm') if link is not None: @@ -138,7 +137,7 @@ def downloadSong(content, input_ext): if link is not None: link.download(filepath='Music/' + music_file + input_ext) -def convertWithAvconv(music_file, input_ext, output_ext, verbose): +def convert_with_avconv(music_file, input_ext, output_ext, verbose): if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe' else: @@ -153,7 +152,7 @@ def convertWithAvconv(music_file, input_ext, output_ext, verbose): output_ext + '"') os.remove('Music/' + music_file + input_ext) -def convertWithFfmpeg(music_file, input_ext, output_ext, verbose): +def convert_with_FFmpeg(music_file, input_ext, output_ext, verbose): # What are the differences and similarities between ffmpeg, libav, and avconv? # https://stackoverflow.com/questions/9477115 # ffmeg encoders high to lower quality @@ -199,7 +198,7 @@ def convertWithFfmpeg(music_file, input_ext, output_ext, verbose): '"Music/' + music_file + output_ext + '" ') os.remove('Music/' + music_file + input_ext) -def checkExists(music_file, raw_song, islist): +def check_exists(music_file, raw_song, islist): files = os.listdir("Music") for file in files: if file.endswith(".temp"): @@ -209,8 +208,8 @@ def checkExists(music_file, raw_song, islist): if file.startswith(music_file): # FIXME #audiofile = mutagen.load("Music/" + music_file + output_ext) - #if isSpotify(raw_song) and not audiofile.tag.title == ( - # generateMetaTags(raw_song))['name']: + #if is_spotify(raw_song) and not audiofile.tag.title == ( + # generate_metadata(raw_song))['name']: # os.remove("Music/" + music_file + output_ext) # return False os.remove("Music/" + file) @@ -227,19 +226,19 @@ def checkExists(music_file, raw_song, islist): return True # Remove song from file once downloaded -def fixSong(music_file, meta_tags, output_ext): +def fix_metadata(music_file, meta_tags, output_ext): if meta_tags is None: print('Could not find meta-tags') elif output_ext == '.m4a': print('Fixing meta-tags') - fixSongM4A(music_file, meta_tags, output_ext) + fix_metadata_m4a(music_file, meta_tags, output_ext) elif output_ext == '.mp3': print('Fixing meta-tags') - fixSongMP3(music_file, meta_tags, output_ext) + fix_metadata_mp3(music_file, meta_tags, output_ext) else: print('Cannot embed meta-tags into given output extension') -def fixSongMP3(music_file, meta_tags, output_ext): +def fix_metadata_mp3(music_file, meta_tags, output_ext): audiofile = EasyID3('Music/' + music_file + output_ext) audiofile['artist'] = meta_tags['artists'][0]['name'] audiofile['albumartist'] = meta_tags['artists'][0]['name'] @@ -257,7 +256,7 @@ def fixSongMP3(music_file, meta_tags, output_ext): albumart.close() audiofile.save(v2_version=3) -def fixSongM4A(music_file, meta_tags, output_ext): +def fix_metadata_m4a(music_file, meta_tags, output_ext): # eyed serves only mp3 not aac so using mutagen # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html @@ -289,39 +288,39 @@ def fixSongM4A(music_file, meta_tags, output_ext): albumart.close() audiofile.save() -def convertSong(music_file, input_ext, output_ext, ffmpeg, verbose): +def convert_song(music_file, input_ext, output_ext, ffmpeg, verbose): print(music_file, input_ext, output_ext, ffmpeg, verbose) if not input_ext == output_ext: print('Converting ' + music_file + input_ext + ' to ' + output_ext[1:]) if ffmpeg: - convertWithFfmpeg(music_file, input_ext, output_ext, verbose) + convert_with_FFmpeg(music_file, input_ext, output_ext, verbose) else: - convertWithAvconv(music_file, input_ext, output_ext, verbose) + convert_with_avconv(music_file, input_ext, output_ext, verbose) else: print('Skipping conversion since input_ext = output_ext') # Logic behind preparing the song to download to finishing meta-tags -def grabSingle(raw_song, number=None): +def grab_single(raw_song, number=None): if number: islist = True else: islist = False - content = goPafy(raw_song) + content = go_pafy(raw_song) if content is None: return - print(getYouTubeTitle(content, number)) - music_file = generateFileName(content) - if not checkExists(music_file, raw_song, islist=islist): - downloadSong(content, args.input_ext) + print(get_YouTube_title(content, number)) + music_file = generate_filename(content) + if not check_exists(music_file, raw_song, islist=islist): + download_song(content, args.input_ext) print('') if not args.no_convert: - convertSong(music_file, args.input_ext, args.output_ext, args.ffmpeg, args.verbose) - meta_tags = generateMetaTags(raw_song) - fixSong(music_file, meta_tags, args.output_ext) + convert_song(music_file, args.input_ext, args.output_ext, args.ffmpeg, args.verbose) + meta_tags = generate_metadata(raw_song) + fix_metadata(music_file, meta_tags, args.output_ext) # Fix python2 encoding issues -def grabList(file): +def grab_list(file): lines = open(file, 'r').read() lines = lines.splitlines() # Ignore blank lines in file (if any) @@ -335,15 +334,15 @@ def grabList(file): number = 1 for raw_song in lines: try: - grabSingle(raw_song, number=number) - trimSong(file) + grab_single(raw_song, number=number) + trim_song(file) number += 1 print('') except KeyboardInterrupt: - graceQuit() + grace_quit() except ConnectionError: lines.append(raw_song) - trimSong(file) + trim_song(file) with open(file, 'a') as myfile: myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') @@ -367,7 +366,7 @@ if __name__ == '__main__': spotify = spotipy.Spotify(auth=token) # Set up arguments - args = getArgs() + args = get_arguments() print(args) #if args.ffmpeg: @@ -378,8 +377,8 @@ if __name__ == '__main__': # output_ext = '.mp3' if args.song: - grabSingle(raw_song=args.song) + grab_single(raw_song=args.song) elif args.list: - grabList(file=args.list) + grab_list(file=args.list) elif args.username: - feedPlaylist(username=args.username) + feed_playlist(username=args.username) From 11b3a61a223136b655093f3fb32d4b997725c67a Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 12:51:01 +0530 Subject: [PATCH 09/56] Some minor fixes --- core/misc.py | 6 ++-- spotdl.py | 87 +++++++++++++++++++++++++--------------------------- 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/core/misc.py b/core/misc.py index cd0785a..41db32d 100644 --- a/core/misc.py +++ b/core/misc.py @@ -33,12 +33,12 @@ def get_arguments(): help='download songs from a file') group.add_argument('-u', '--username', help="load user's playlists into .txt") - parser.add_argument('-n', '--no-convert', default=False, - help='skip the conversion process and meta-tags', action='store_true') parser.add_argument('-m', '--manual', default=False, help='choose the song to download manually', action='store_true') + parser.add_argument('-nm', '--no-metadata', default=False, + help='do not embed metadata in songs', action='store_true') parser.add_argument('-f', '--ffmpeg', default=False, - help='Use ffmpeg instead of libav for conversion. If not set defaults to libav', + help='Use ffmpeg for conversion otherwise set defaults to libav', action='store_true') parser.add_argument('-v', '--verbose', default=False, help='show debug output', action='store_true') diff --git a/spotdl.py b/spotdl.py index 655c3e8..3b81ea4 100644 --- a/spotdl.py +++ b/spotdl.py @@ -126,18 +126,18 @@ def generate_filename(content): title = slugify(title, ok='-_()[]{}', lower=False) return fix_encoding(title) -def download_song(content, input_ext): +def download_song(content): music_file = generate_filename(content) - if input_ext == '.webm': + if args.input_ext == '.webm': link = content.getbestaudio(preftype='webm') if link is not None: - link.download(filepath='Music/' + music_file + input_ext) + link.download(filepath='Music/' + music_file + args.input_ext) else: link = content.getbestaudio(preftype='m4a') if link is not None: - link.download(filepath='Music/' + music_file + input_ext) + link.download(filepath='Music/' + music_file + args.input_ext) -def convert_with_avconv(music_file, input_ext, output_ext, verbose): +def convert_with_libav(music_file): if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe' else: @@ -146,13 +146,13 @@ def convert_with_avconv(music_file, input_ext, output_ext, verbose): avconv_path + ' -loglevel 0 -i "' + 'Music/' + music_file + - input_ext + '" -ab 192k "' + + args.input_ext + '" -ab 192k "' + 'Music/' + music_file + - output_ext + '"') - os.remove('Music/' + music_file + input_ext) + args.output_ext + '"') + os.remove('Music/' + music_file + args.input_ext) -def convert_with_FFmpeg(music_file, input_ext, output_ext, verbose): +def convert_with_FFmpeg(music_file): # What are the differences and similarities between ffmpeg, libav, and avconv? # https://stackoverflow.com/questions/9477115 # ffmeg encoders high to lower quality @@ -161,31 +161,31 @@ def convert_with_FFmpeg(music_file, input_ext, output_ext, verbose): # on MacOS brew install ffmpeg --with-fdk-aac will do just that. Other OS? # https://trac.ffmpeg.org/wiki/Encode/AAC # - print(music_file, input_ext, output_ext, verbose) - if verbose: + print(music_file) + if args.verbose: ffmpeg_pre = 'ffmpeg -y ' else: ffmpeg_pre = 'ffmpeg -hide_banner -nostats -v panic -y ' - if input_ext == '.m4a': - if output_ext == '.mp3': + if args.input_ext == '.m4a': + if args.output_ext == '.mp3': ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' elif output_ext == '.webm': ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' else: return - elif input_ext == '.webm': - if output_ext == '.mp3': + elif args.input_ext == '.webm': + if args.output_ext == '.mp3': ffmpeg_params = '-ab 192k -ar 44100 -vn ' - elif output_ext == '.m4a': + elif args.output_ext == '.m4a': ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 256k -vn ' else: return else: - print('Unknown formats. Unable to convert.', input_ext, output_ext) + print('Unknown formats. Unable to convert.', args.input_ext, args.output_ext) return - if verbose: + if args.verbose: print(ffmpeg_pre + '-i "Music/' + music_file + input_ext + '" ' + ffmpeg_params + @@ -196,7 +196,7 @@ def convert_with_FFmpeg(music_file, input_ext, output_ext, verbose): '-i "Music/' + music_file + input_ext + '" ' + ffmpeg_params + '"Music/' + music_file + output_ext + '" ') - os.remove('Music/' + music_file + input_ext) + os.remove('Music/' + music_file + args.input_ext) def check_exists(music_file, raw_song, islist): files = os.listdir("Music") @@ -217,8 +217,7 @@ def check_exists(music_file, raw_song, islist): if islist: return True else: - prompt = raw_input( - 'Song with same name has already been downloaded. Re-download? (y/n): ').lower() + prompt = raw_input('Song with same name has already been downloaded. Re-download? (y/n): ').lower() if prompt == "y": os.remove("Music/" + file) return False @@ -226,20 +225,20 @@ def check_exists(music_file, raw_song, islist): return True # Remove song from file once downloaded -def fix_metadata(music_file, meta_tags, output_ext): +def fix_metadata(music_file, meta_tags): if meta_tags is None: print('Could not find meta-tags') - elif output_ext == '.m4a': + elif args.output_ext == '.m4a': print('Fixing meta-tags') - fix_metadata_m4a(music_file, meta_tags, output_ext) - elif output_ext == '.mp3': + fix_metadata_m4a(music_file, meta_tags) + elif args.output_ext == '.mp3': print('Fixing meta-tags') - fix_metadata_mp3(music_file, meta_tags, output_ext) + fix_metadata_mp3(music_file, meta_tags) else: print('Cannot embed meta-tags into given output extension') -def fix_metadata_mp3(music_file, meta_tags, output_ext): - audiofile = EasyID3('Music/' + music_file + output_ext) +def fix_metadata_mp3(music_file, meta_tags): + audiofile = EasyID3('Music/' + music_file + args.output_ext) audiofile['artist'] = meta_tags['artists'][0]['name'] audiofile['albumartist'] = meta_tags['artists'][0]['name'] audiofile['album'] = meta_tags['album']['name'] @@ -250,13 +249,13 @@ def fix_metadata_mp3(music_file, meta_tags, output_ext): audiofile['discnumber'] = [meta_tags['disc_number'], 0] audiofile['date'] = meta_tags['release_date'] audiofile.save(v2_version=3) - audiofile = ID3('Music/' + music_file + output_ext) + audiofile = ID3('Music/' + music_file + args.output_ext) albumart = urllib2.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() audiofile.save(v2_version=3) -def fix_metadata_m4a(music_file, meta_tags, output_ext): +def fix_metadata_m4a(music_file, meta_tags): # eyed serves only mp3 not aac so using mutagen # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html @@ -274,7 +273,7 @@ def fix_metadata_m4a(music_file, meta_tags, output_ext): 'cpil': 'cpil', 'tempo': 'tmpo'} - audiofile = MP4('Music/' + music_file + output_ext) + audiofile = MP4('Music/' + music_file + args.output_ext) audiofile[tags['artist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] @@ -288,17 +287,13 @@ def fix_metadata_m4a(music_file, meta_tags, output_ext): albumart.close() audiofile.save() -def convert_song(music_file, input_ext, output_ext, ffmpeg, verbose): - print(music_file, input_ext, output_ext, ffmpeg, verbose) - if not input_ext == output_ext: - print('Converting ' + music_file + input_ext + ' to ' + output_ext[1:]) - if ffmpeg: - convert_with_FFmpeg(music_file, input_ext, output_ext, verbose) +def convert_song(music_file): + if not args.input_ext == args.output_ext: + print('Converting ' + music_file + args.input_ext + ' to ' + args.output_ext[1:]) + if args.ffmpeg: + convert_with_FFmpeg(music_file) else: - convert_with_avconv(music_file, input_ext, output_ext, verbose) - else: - print('Skipping conversion since input_ext = output_ext') - + convert_with_libav(music_file) # Logic behind preparing the song to download to finishing meta-tags def grab_single(raw_song, number=None): @@ -312,12 +307,12 @@ def grab_single(raw_song, number=None): print(get_YouTube_title(content, number)) music_file = generate_filename(content) if not check_exists(music_file, raw_song, islist=islist): - download_song(content, args.input_ext) + download_song(content) print('') - if not args.no_convert: - convert_song(music_file, args.input_ext, args.output_ext, args.ffmpeg, args.verbose) - meta_tags = generate_metadata(raw_song) - fix_metadata(music_file, meta_tags, args.output_ext) + convert_song(music_file) + meta_tags = generate_metadata(raw_song) + if not args.no_metadata: + fix_metadata(music_file, meta_tags) # Fix python2 encoding issues def grab_list(file): From 8cf7d96fc89fa375173586b6acdbab756c8da777 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 14:23:02 +0530 Subject: [PATCH 10/56] Switching to subprocess.call() from os.system() --- spotdl.py | 91 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/spotdl.py b/spotdl.py index 3b81ea4..3952866 100644 --- a/spotdl.py +++ b/spotdl.py @@ -9,17 +9,18 @@ from core.misc import generate_search_URL from core.misc import fix_encoding from core.misc import grace_quit from bs4 import BeautifulSoup -import sys -from slugify import slugify from titlecase import titlecase -from mutagen.id3 import ID3, APIC +from slugify import slugify from mutagen.easyid3 import EasyID3 +from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover import spotipy import spotipy.oauth2 as oauth2 import urllib2 import pafy +import sys import os +import subprocess def generate_song_name(raw_song): if is_spotify(raw_song): @@ -137,19 +138,37 @@ def download_song(content): if link is not None: link.download(filepath='Music/' + music_file + args.input_ext) +def convert_song(music_file): + if not args.input_ext == args.output_ext: + print('Converting ' + music_file + args.input_ext + ' to ' + args.output_ext[1:]) + if args.ffmpeg: + convert_with_FFmpeg(music_file) + else: + convert_with_libav(music_file) + def convert_with_libav(music_file): if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe' else: avconv_path = 'avconv' - os.system( - avconv_path + ' -loglevel 0 -i "' + - 'Music/' + - music_file + - args.input_ext + '" -ab 192k "' + - 'Music/' + - music_file + - args.output_ext + '"') + + if args.verbose: + level = 'debug' + else: + level = '0' + + print([avconv_path, + '-loglevel', level, + '-i', 'Music/' + music_file + args.input_ext, + '-ab', '192k', + 'Music/' + music_file + args.output_ext]) + + subprocess.call([avconv_path, + '-loglevel', level, + '-i', 'Music/' + music_file + args.input_ext, + '-ab', '192k', + 'Music/' + music_file + args.output_ext]) + os.remove('Music/' + music_file + args.input_ext) def convert_with_FFmpeg(music_file): @@ -161,41 +180,47 @@ def convert_with_FFmpeg(music_file): # on MacOS brew install ffmpeg --with-fdk-aac will do just that. Other OS? # https://trac.ffmpeg.org/wiki/Encode/AAC # - print(music_file) - if args.verbose: - ffmpeg_pre = 'ffmpeg -y ' + if os.name == "nt": + ffmpeg_pre = 'Scripts//ffmpeg.exe ' else: - ffmpeg_pre = 'ffmpeg -hide_banner -nostats -v panic -y ' + ffmpeg_pre = 'ffmpeg ' + + ffmpeg_pre += '-y ' + if not args.verbose: + ffmpeg_pre += '-hide_banner -nostats -v panic ' if args.input_ext == '.m4a': if args.output_ext == '.mp3': - ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' + ffmpeg_params = ' -codec:v copy -codec:a libmp3lame -q:a 2 ' elif output_ext == '.webm': - ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' + ffmpeg_params = ' -c:a libopus -vbr on -b:a 192k -vn ' else: return elif args.input_ext == '.webm': if args.output_ext == '.mp3': - ffmpeg_params = '-ab 192k -ar 44100 -vn ' + ffmpeg_params = ' -ab 192k -ar 44100 -vn ' elif args.output_ext == '.m4a': - ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 256k -vn ' + ffmpeg_params = ' -cutoff 20000 -c:a libfdk_aac -b:a 256k -vn ' else: return else: print('Unknown formats. Unable to convert.', args.input_ext, args.output_ext) return - if args.verbose: - print(ffmpeg_pre + - '-i "Music/' + music_file + input_ext + '" ' + - ffmpeg_params + - '"Music/' + music_file + output_ext + '" ') + command = (ffmpeg_pre + + '-i "Music/' + music_file + args.input_ext + '"' + + ffmpeg_params + + '"Music/' + music_file + args.output_ext + '"').split(' ') + + commandos = (ffmpeg_pre + + '-i "Music/' + music_file + args.input_ext + '" ' + + ffmpeg_params + + '"Music/' + music_file + args.output_ext + '" ') + #print(command) + #print(commandos) + os.system(commandos) + #subprocess.call(command) - os.system( - ffmpeg_pre + - '-i "Music/' + music_file + input_ext + '" ' + - ffmpeg_params + - '"Music/' + music_file + output_ext + '" ') os.remove('Music/' + music_file + args.input_ext) def check_exists(music_file, raw_song, islist): @@ -287,14 +312,6 @@ def fix_metadata_m4a(music_file, meta_tags): albumart.close() audiofile.save() -def convert_song(music_file): - if not args.input_ext == args.output_ext: - print('Converting ' + music_file + args.input_ext + ' to ' + args.output_ext[1:]) - if args.ffmpeg: - convert_with_FFmpeg(music_file) - else: - convert_with_libav(music_file) - # Logic behind preparing the song to download to finishing meta-tags def grab_single(raw_song, number=None): if number: From 8c89da5ccfb0a56b5e09fe65697bbbb519c2b18a Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 14:48:22 +0530 Subject: [PATCH 11/56] Some small fixes --- core/misc.py | 9 +++++++ spotdl.py | 70 ++++++++++++++++++++++------------------------------ 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/core/misc.py b/core/misc.py index 41db32d..a005600 100644 --- a/core/misc.py +++ b/core/misc.py @@ -1,6 +1,7 @@ import argparse import sys import os +import spotipy.oauth2 as oauth2 def input_link(links): while True: @@ -55,6 +56,14 @@ def is_spotify(raw_song): else: return False +def generate_token(): + # Please respect this user token :) + creds = oauth2.SpotifyClientCredentials( + client_id='4fe3fecfe5334023a1472516cc99d805', + client_secret='0f02b7c483c04257984695007a4a8d5c') + token = creds.get_access_token() + return token + def generate_search_URL(song): URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + \ song.replace(" ", "%20") diff --git a/spotdl.py b/spotdl.py index 3952866..645fd42 100644 --- a/spotdl.py +++ b/spotdl.py @@ -5,6 +5,7 @@ from core.misc import input_link from core.misc import trim_song from core.misc import get_arguments from core.misc import is_spotify +from core.misc import generate_token from core.misc import generate_search_URL from core.misc import fix_encoding from core.misc import grace_quit @@ -157,11 +158,11 @@ def convert_with_libav(music_file): else: level = '0' - print([avconv_path, - '-loglevel', level, - '-i', 'Music/' + music_file + args.input_ext, - '-ab', '192k', - 'Music/' + music_file + args.output_ext]) + #print([avconv_path, + # '-loglevel', level, + # '-i', 'Music/' + music_file + args.input_ext, + # '-ab', '192k', + # 'Music/' + music_file + args.output_ext]) subprocess.call([avconv_path, '-loglevel', level, @@ -207,10 +208,10 @@ def convert_with_FFmpeg(music_file): print('Unknown formats. Unable to convert.', args.input_ext, args.output_ext) return - command = (ffmpeg_pre + - '-i "Music/' + music_file + args.input_ext + '"' + - ffmpeg_params + - '"Music/' + music_file + args.output_ext + '"').split(' ') + #command = (ffmpeg_pre + + # '-i "Music/' + music_file + args.input_ext + '"' + + # ffmpeg_params + + # '"Music/' + music_file + args.output_ext + '"').split(' ') commandos = (ffmpeg_pre + '-i "Music/' + music_file + args.input_ext + '" ' + @@ -312,26 +313,7 @@ def fix_metadata_m4a(music_file, meta_tags): albumart.close() audiofile.save() -# Logic behind preparing the song to download to finishing meta-tags -def grab_single(raw_song, number=None): - if number: - islist = True - else: - islist = False - content = go_pafy(raw_song) - if content is None: - return - print(get_YouTube_title(content, number)) - music_file = generate_filename(content) - if not check_exists(music_file, raw_song, islist=islist): - download_song(content) - print('') - convert_song(music_file) - meta_tags = generate_metadata(raw_song) - if not args.no_metadata: - fix_metadata(music_file, meta_tags) -# Fix python2 encoding issues def grab_list(file): lines = open(file, 'r').read() lines = lines.splitlines() @@ -359,6 +341,24 @@ def grab_list(file): myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') +# Logic behind preparing the song to download to finishing meta-tags +def grab_single(raw_song, number=None): + if number: + islist = True + else: + islist = False + content = go_pafy(raw_song) + if content is None: + return + print(get_YouTube_title(content, number)) + music_file = generate_filename(content) + if not check_exists(music_file, raw_song, islist=islist): + download_song(content) + print('') + convert_song(music_file) + meta_tags = generate_metadata(raw_song) + if not args.no_metadata: + fix_metadata(music_file, meta_tags) if __name__ == '__main__': @@ -370,23 +370,11 @@ if __name__ == '__main__': if not os.path.exists("Music"): os.makedirs("Music") - # Please respect this user token :) - oauth2 = oauth2.SpotifyClientCredentials( - client_id='4fe3fecfe5334023a1472516cc99d805', - client_secret='0f02b7c483c04257984695007a4a8d5c') - token = oauth2.get_access_token() + token = generate_token() spotify = spotipy.Spotify(auth=token) # Set up arguments args = get_arguments() - print(args) - - #if args.ffmpeg: - # input_ext = args.input_ext - # output_ext = args.output_ext - #else: - # input_ext = '.m4a' - # output_ext = '.mp3' if args.song: grab_single(raw_song=args.song) From b3160c31ea3b1a6e243421a9a376c0245213412a Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 16:18:02 +0530 Subject: [PATCH 12/56] Make metadata more verbose --- spotdl.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/spotdl.py b/spotdl.py index 645fd42..cd7923b 100644 --- a/spotdl.py +++ b/spotdl.py @@ -43,6 +43,7 @@ def generate_metadata(raw_song): meta_tags['genre'] = None meta_tags['release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] + print(meta_tags) return meta_tags except BaseException: @@ -287,29 +288,31 @@ def fix_metadata_m4a(music_file, meta_tags): # http://mutagen.readthedocs.io/en/latest/api/mp4.html tags = {'album': '\xa9alb', 'artist': '\xa9ART', - 'year': '\xa9day', + 'date': '\xa9day', 'title': '\xa9nam', 'comment': '\xa9cmt', 'group': '\xa9grp', 'writer': '\xa9wrt', 'genre': '\xa9gen', - 'track': 'trkn', - 'aart': 'aART', - 'disk': 'disk', + 'tracknumber': 'trkn', + 'albumartist': 'aART', + 'disknumber': 'disk', 'cpil': 'cpil', + 'albumart': 'covr', 'tempo': 'tmpo'} audiofile = MP4('Music/' + music_file + args.output_ext) audiofile[tags['artist']] = meta_tags['artists'][0]['name'] + audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] if meta_tags['genre']: audiofile[tags['genre']] = meta_tags['genre'] - audiofile[tags['year']] = meta_tags['release_date'] - audiofile[tags['track']] = [(meta_tags['track_number'], 0)] - audiofile[tags['disk']] = [(meta_tags['disc_number'], 0)] + audiofile[tags['tracknumber']] = [(meta_tags['track_number'], 0)] + audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] + audiofile[tags['date']] = meta_tags['release_date'] albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) - audiofile["covr"] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] + audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() audiofile.save() From e86d5f8cdc1c50646fc82e99d7fa08aedb73077f Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 18:47:09 +0530 Subject: [PATCH 13/56] Add moredata fields --- spotdl.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/spotdl.py b/spotdl.py index cd7923b..3e78b47 100644 --- a/spotdl.py +++ b/spotdl.py @@ -38,12 +38,17 @@ def generate_metadata(raw_song): artist_id = spotify.artist(meta_tags['artists'][0]['id']) try: - meta_tags['genre'] = titlecase(artist_id['genres'][0]) + meta_tags[u'genre'] = titlecase(artist_id['genres'][0]) except IndexError: - meta_tags['genre'] = None + meta_tags[u'genre'] = None - meta_tags['release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] - print(meta_tags) + meta_tags[u'release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] + meta_tags[u'copyright'] = spotify.album(meta_tags['album']['id'])['copyrights'][0]['text'] + meta_tags[u'publisher'] = spotify.album(meta_tags['album']['id'])['label'] + meta_tags[u'total_tracks'] = spotify.album(meta_tags['album']['id'])['tracks']['total'] + #import pprint + #pprint.pprint(meta_tags) + #pprint.pprint(spotify.album(meta_tags['album']['id'])) return meta_tags except BaseException: @@ -272,9 +277,15 @@ def fix_metadata_mp3(music_file, meta_tags): audiofile['title'] = meta_tags['name'] if meta_tags['genre']: audiofile['genre'] = meta_tags['genre'] - audiofile['tracknumber'] = [meta_tags['track_number'], 0] + audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] audiofile['discnumber'] = [meta_tags['disc_number'], 0] audiofile['date'] = meta_tags['release_date'] + audiofile['originaldate'] = meta_tags['release_date'] + audiofile['copyright'] = meta_tags['copyright'] + audiofile['author'] = meta_tags['publisher'] + audiofile['arranger'] = meta_tags['publisher'] + audiofile['performer'] = meta_tags['publisher'] + audiofile['encodedby'] = meta_tags['publisher'] audiofile.save(v2_version=3) audiofile = ID3('Music/' + music_file + args.output_ext) albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) @@ -290,6 +301,7 @@ def fix_metadata_m4a(music_file, meta_tags): 'artist': '\xa9ART', 'date': '\xa9day', 'title': '\xa9nam', + 'originaldate': 'purd', 'comment': '\xa9cmt', 'group': '\xa9grp', 'writer': '\xa9wrt', @@ -299,6 +311,7 @@ def fix_metadata_m4a(music_file, meta_tags): 'disknumber': 'disk', 'cpil': 'cpil', 'albumart': 'covr', + 'copyright': 'cprt', 'tempo': 'tmpo'} audiofile = MP4('Music/' + music_file + args.output_ext) @@ -308,9 +321,11 @@ def fix_metadata_m4a(music_file, meta_tags): audiofile[tags['title']] = meta_tags['name'] if meta_tags['genre']: audiofile[tags['genre']] = meta_tags['genre'] - audiofile[tags['tracknumber']] = [(meta_tags['track_number'], 0)] + audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] audiofile[tags['date']] = meta_tags['release_date'] + audiofile[tags['originaldate']] = meta_tags['release_date'] + audiofile[tags['copyright']] = meta_tags['copyright'] albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() From 7f029af5c61a47af7217c1b9fb53ef5baf0ac4cb Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 20:54:57 +0530 Subject: [PATCH 14/56] Minor changes --- spotdl.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/spotdl.py b/spotdl.py index 3e78b47..fc28c7a 100644 --- a/spotdl.py +++ b/spotdl.py @@ -46,8 +46,8 @@ def generate_metadata(raw_song): meta_tags[u'copyright'] = spotify.album(meta_tags['album']['id'])['copyrights'][0]['text'] meta_tags[u'publisher'] = spotify.album(meta_tags['album']['id'])['label'] meta_tags[u'total_tracks'] = spotify.album(meta_tags['album']['id'])['tracks']['total'] - #import pprint - #pprint.pprint(meta_tags) + import pprint + pprint.pprint(meta_tags) #pprint.pprint(spotify.album(meta_tags['album']['id'])) return meta_tags @@ -275,17 +275,21 @@ def fix_metadata_mp3(music_file, meta_tags): audiofile['albumartist'] = meta_tags['artists'][0]['name'] audiofile['album'] = meta_tags['album']['name'] audiofile['title'] = meta_tags['name'] - if meta_tags['genre']: - audiofile['genre'] = meta_tags['genre'] audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] audiofile['discnumber'] = [meta_tags['disc_number'], 0] audiofile['date'] = meta_tags['release_date'] audiofile['originaldate'] = meta_tags['release_date'] audiofile['copyright'] = meta_tags['copyright'] - audiofile['author'] = meta_tags['publisher'] - audiofile['arranger'] = meta_tags['publisher'] - audiofile['performer'] = meta_tags['publisher'] + audiofile['author'] = meta_tags['artists'][0]['name'] + audiofile['lyricist'] = meta_tags['artists'][0]['name'] + audiofile['arranger'] = meta_tags['artists'][0]['name'] + audiofile['performer'] = meta_tags['artists'][0]['name'] audiofile['encodedby'] = meta_tags['publisher'] + audiofile['isrc'] = meta_tags['external_ids']['isrc'] + audiofile['website'] = meta_tags['external_urls']['spotify'] + audiofile['length'] = meta_tags['duration_ms'] / 100 + if meta_tags['genre']: + audiofile['genre'] = meta_tags['genre'] audiofile.save(v2_version=3) audiofile = ID3('Music/' + music_file + args.output_ext) albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) @@ -319,13 +323,13 @@ def fix_metadata_m4a(music_file, meta_tags): audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] - if meta_tags['genre']: - audiofile[tags['genre']] = meta_tags['genre'] audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] audiofile[tags['date']] = meta_tags['release_date'] audiofile[tags['originaldate']] = meta_tags['release_date'] audiofile[tags['copyright']] = meta_tags['copyright'] + if meta_tags['genre']: + audiofile[tags['genre']] = meta_tags['genre'] albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] albumart.close() From f841a381694bc92115f8bd2aa632078f57831615 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Thu, 15 Jun 2017 22:20:27 +0530 Subject: [PATCH 15/56] Add all artists in metadata --- spotdl.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/spotdl.py b/spotdl.py index fc28c7a..bb072ea 100644 --- a/spotdl.py +++ b/spotdl.py @@ -46,8 +46,8 @@ def generate_metadata(raw_song): meta_tags[u'copyright'] = spotify.album(meta_tags['album']['id'])['copyrights'][0]['text'] meta_tags[u'publisher'] = spotify.album(meta_tags['album']['id'])['label'] meta_tags[u'total_tracks'] = spotify.album(meta_tags['album']['id'])['tracks']['total'] - import pprint - pprint.pprint(meta_tags) + #import pprint + #pprint.pprint(meta_tags) #pprint.pprint(spotify.album(meta_tags['album']['id'])) return meta_tags @@ -270,8 +270,11 @@ def fix_metadata(music_file, meta_tags): print('Cannot embed meta-tags into given output extension') def fix_metadata_mp3(music_file, meta_tags): + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) audiofile = EasyID3('Music/' + music_file + args.output_ext) - audiofile['artist'] = meta_tags['artists'][0]['name'] + audiofile['artist'] = ', '.join(artists) audiofile['albumartist'] = meta_tags['artists'][0]['name'] audiofile['album'] = meta_tags['album']['name'] audiofile['title'] = meta_tags['name'] @@ -279,6 +282,7 @@ def fix_metadata_mp3(music_file, meta_tags): audiofile['discnumber'] = [meta_tags['disc_number'], 0] audiofile['date'] = meta_tags['release_date'] audiofile['originaldate'] = meta_tags['release_date'] + audiofile['media'] = meta_tags['type'] audiofile['copyright'] = meta_tags['copyright'] audiofile['author'] = meta_tags['artists'][0]['name'] audiofile['lyricist'] = meta_tags['artists'][0]['name'] @@ -287,7 +291,7 @@ def fix_metadata_mp3(music_file, meta_tags): audiofile['encodedby'] = meta_tags['publisher'] audiofile['isrc'] = meta_tags['external_ids']['isrc'] audiofile['website'] = meta_tags['external_urls']['spotify'] - audiofile['length'] = meta_tags['duration_ms'] / 100 + audiofile['length'] = str(meta_tags['duration_ms'] / 1000) if meta_tags['genre']: audiofile['genre'] = meta_tags['genre'] audiofile.save(v2_version=3) @@ -318,8 +322,11 @@ def fix_metadata_m4a(music_file, meta_tags): 'copyright': 'cprt', 'tempo': 'tmpo'} + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) audiofile = MP4('Music/' + music_file + args.output_ext) - audiofile[tags['artist']] = meta_tags['artists'][0]['name'] + audiofile[tags['artist']] = ', '.join(artists) audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] @@ -375,9 +382,9 @@ def grab_single(raw_song, number=None): print(get_YouTube_title(content, number)) music_file = generate_filename(content) if not check_exists(music_file, raw_song, islist=islist): - download_song(content) + #download_song(content) print('') - convert_song(music_file) + #convert_song(music_file) meta_tags = generate_metadata(raw_song) if not args.no_metadata: fix_metadata(music_file, meta_tags) From 10f57b4ae5886351f1971e04e67da9110d5bace9 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Thu, 15 Jun 2017 23:33:59 +0530 Subject: [PATCH 16/56] Revert debug modifications --- spotdl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spotdl.py b/spotdl.py index bb072ea..3b3477a 100644 --- a/spotdl.py +++ b/spotdl.py @@ -382,9 +382,9 @@ def grab_single(raw_song, number=None): print(get_YouTube_title(content, number)) music_file = generate_filename(content) if not check_exists(music_file, raw_song, islist=islist): - #download_song(content) + download_song(content) print('') - #convert_song(music_file) + convert_song(music_file) meta_tags = generate_metadata(raw_song) if not args.no_metadata: fix_metadata(music_file, meta_tags) From be088f6dc02d598f4486ca337e3cf0bcef6264ff Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 00:24:09 +0530 Subject: [PATCH 17/56] Handle URLError and IOError when network problem --- spotdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotdl.py b/spotdl.py index 3b3477a..5ba9f33 100644 --- a/spotdl.py +++ b/spotdl.py @@ -363,7 +363,7 @@ def grab_list(file): print('') except KeyboardInterrupt: grace_quit() - except ConnectionError: + except (URLError, IOError): lines.append(raw_song) trim_song(file) with open(file, 'a') as myfile: From 74511cf59656fe8e6d763df9159388501e405625 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 02:06:08 +0530 Subject: [PATCH 18/56] Fix check_exists() from giving prompts on non-existent files --- core/misc.py | 6 +++++ spotdl.py | 70 +++++++++++++++++++++++++--------------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/core/misc.py b/core/misc.py index a005600..e698d0e 100644 --- a/core/misc.py +++ b/core/misc.py @@ -1,6 +1,7 @@ import argparse import sys import os +from slugify import slugify import spotipy.oauth2 as oauth2 def input_link(links): @@ -56,6 +57,11 @@ def is_spotify(raw_song): else: return False +def generate_filename(title): + raw_title = title.replace(' ', '_') + filename = slugify(raw_title, ok='-_()[]{}', lower=False) + return fix_encoding(filename) + def generate_token(): # Please respect this user token :) creds = oauth2.SpotifyClientCredentials( diff --git a/spotdl.py b/spotdl.py index 5ba9f33..5fcd45b 100644 --- a/spotdl.py +++ b/spotdl.py @@ -5,16 +5,17 @@ from core.misc import input_link from core.misc import trim_song from core.misc import get_arguments from core.misc import is_spotify +from core.misc import generate_filename from core.misc import generate_token from core.misc import generate_search_URL from core.misc import fix_encoding from core.misc import grace_quit from bs4 import BeautifulSoup from titlecase import titlecase -from slugify import slugify from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover +from slugify import slugify import spotipy import spotipy.oauth2 as oauth2 import urllib2 @@ -23,7 +24,7 @@ import sys import os import subprocess -def generate_song_name(raw_song): +def generate_songname(raw_song): if is_spotify(raw_song): tags = generate_metadata(raw_song) raw_song = tags['artists'][0]['name'] + ' - ' + tags['name'] @@ -51,11 +52,11 @@ def generate_metadata(raw_song): #pprint.pprint(spotify.album(meta_tags['album']['id'])) return meta_tags - except BaseException: + except (urllib2.URLError, IOError): return None def generate_YouTube_URL(raw_song): - song = generate_song_name(raw_song) + song = generate_songname(raw_song) searchURL = generate_search_URL(song) item = urllib2.urlopen(searchURL).read() items_parse = BeautifulSoup(item, "html.parser") @@ -128,14 +129,8 @@ def feed_playlist(username): tracks = spotify.next(tracks) feed_tracks(file, tracks) -# Generate name for the song to be downloaded -def generate_filename(content): - title = (content.title).replace(' ', '_') - title = slugify(title, ok='-_()[]{}', lower=False) - return fix_encoding(title) - def download_song(content): - music_file = generate_filename(content) + music_file = generate_filename(content.title) if args.input_ext == '.webm': link = content.getbestaudio(preftype='webm') if link is not None: @@ -237,39 +232,42 @@ def check_exists(music_file, raw_song, islist): os.remove("Music/" + file) continue - if file.startswith(music_file): - # FIXME - #audiofile = mutagen.load("Music/" + music_file + output_ext) - #if is_spotify(raw_song) and not audiofile.tag.title == ( - # generate_metadata(raw_song))['name']: - # os.remove("Music/" + music_file + output_ext) - # return False - os.remove("Music/" + file) - return False - if islist: - return True - else: - prompt = raw_input('Song with same name has already been downloaded. Re-download? (y/n): ').lower() - if prompt == "y": + if file.startswith(generate_filename(music_file)): + audiofile = EasyID3('Music/' + file) + + try: + already_tagged = audiofile['title'][0] == generate_metadata(raw_song)['name'] + except KeyError: + already_tagged = False + + if is_spotify(raw_song) and not already_tagged: os.remove("Music/" + file) return False - else: + + if islist: return True + else: + prompt = raw_input('Song with same name has already been downloaded. Re-download? (y/n): ').lower() + if prompt == "y": + os.remove("Music/" + file) + return False + else: + return True # Remove song from file once downloaded -def fix_metadata(music_file, meta_tags): +def fix_metadata(music_file, meta_tags, output_ext): if meta_tags is None: print('Could not find meta-tags') - elif args.output_ext == '.m4a': + elif output_ext == '.m4a': print('Fixing meta-tags') - fix_metadata_m4a(music_file, meta_tags) - elif args.output_ext == '.mp3': + fix_metadata_m4a(music_file, meta_tags, output_ext) + elif output_ext == '.mp3': print('Fixing meta-tags') - fix_metadata_mp3(music_file, meta_tags) + fix_metadata_mp3(music_file, meta_tags, output_ext) else: print('Cannot embed meta-tags into given output extension') -def fix_metadata_mp3(music_file, meta_tags): +def fix_metadata_mp3(music_file, meta_tags, output_ext): artists = [] for artist in meta_tags['artists']: artists.append(artist['name']) @@ -301,7 +299,7 @@ def fix_metadata_mp3(music_file, meta_tags): albumart.close() audiofile.save(v2_version=3) -def fix_metadata_m4a(music_file, meta_tags): +def fix_metadata_m4a(music_file, meta_tags, output_ext): # eyed serves only mp3 not aac so using mutagen # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html @@ -363,7 +361,7 @@ def grab_list(file): print('') except KeyboardInterrupt: grace_quit() - except (URLError, IOError): + except (urllib2.URLError, IOError): lines.append(raw_song) trim_song(file) with open(file, 'a') as myfile: @@ -380,14 +378,14 @@ def grab_single(raw_song, number=None): if content is None: return print(get_YouTube_title(content, number)) - music_file = generate_filename(content) + music_file = generate_filename(content.title) if not check_exists(music_file, raw_song, islist=islist): download_song(content) print('') convert_song(music_file) meta_tags = generate_metadata(raw_song) if not args.no_metadata: - fix_metadata(music_file, meta_tags) + fix_metadata(music_file, meta_tags, args.output_ext) if __name__ == '__main__': From f1e5913e6e8243048d4bf2cccf61b1f5a212f292 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 02:37:01 +0530 Subject: [PATCH 19/56] Automatically refresh token when expired when downloading from list --- spotdl.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/spotdl.py b/spotdl.py index 5fcd45b..d403e0f 100644 --- a/spotdl.py +++ b/spotdl.py @@ -16,6 +16,7 @@ from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover from slugify import slugify +import spotipy.oauth2.SpotifyOauthError import spotipy import spotipy.oauth2 as oauth2 import urllib2 @@ -356,9 +357,11 @@ def grab_list(file): for raw_song in lines: try: grab_single(raw_song, number=number) - trim_song(file) - number += 1 - print('') + except spotipy.oauth2.SpotifyOauthError: + token = generate_token() + global spotify + spotify = spotipy.Spotify(auth=token) + grab_single(raw_song, number=number) except KeyboardInterrupt: grace_quit() except (urllib2.URLError, IOError): @@ -367,6 +370,11 @@ def grab_list(file): with open(file, 'a') as myfile: myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') + continue + finally: + print('') + trim_song(file) + number += 1 # Logic behind preparing the song to download to finishing meta-tags def grab_single(raw_song, number=None): From 67f579a03a6be78f40580e076f5eda121021aa96 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 02:50:01 +0530 Subject: [PATCH 20/56] Fix embedding multiple artists --- spotdl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spotdl.py b/spotdl.py index d403e0f..a7b4d37 100644 --- a/spotdl.py +++ b/spotdl.py @@ -273,7 +273,7 @@ def fix_metadata_mp3(music_file, meta_tags, output_ext): for artist in meta_tags['artists']: artists.append(artist['name']) audiofile = EasyID3('Music/' + music_file + args.output_ext) - audiofile['artist'] = ', '.join(artists) + audiofile['artist'] = artists audiofile['albumartist'] = meta_tags['artists'][0]['name'] audiofile['album'] = meta_tags['album']['name'] audiofile['title'] = meta_tags['name'] @@ -325,7 +325,7 @@ def fix_metadata_m4a(music_file, meta_tags, output_ext): for artist in meta_tags['artists']: artists.append(artist['name']) audiofile = MP4('Music/' + music_file + args.output_ext) - audiofile[tags['artist']] = ', '.join(artists) + audiofile[tags['artist']] = artists audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] From 34ae941d637b8ffa9d063aba871a7fd1ba0e6561 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 12:38:28 +0530 Subject: [PATCH 21/56] No need to import spotipy.oauth2.SpotifyOauthError --- spotdl.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spotdl.py b/spotdl.py index a7b4d37..3e84b1d 100644 --- a/spotdl.py +++ b/spotdl.py @@ -16,7 +16,6 @@ from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover from slugify import slugify -import spotipy.oauth2.SpotifyOauthError import spotipy import spotipy.oauth2 as oauth2 import urllib2 @@ -357,7 +356,7 @@ def grab_list(file): for raw_song in lines: try: grab_single(raw_song, number=number) - except spotipy.oauth2.SpotifyOauthError: + except spotipy.oauth2.SpotifyOauthError:: token = generate_token() global spotify spotify = spotipy.Spotify(auth=token) From b1647ee76bd4c0b99f3047f3705c1386e90d488f Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 12:39:36 +0530 Subject: [PATCH 22/56] Fix a syntax error --- spotdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotdl.py b/spotdl.py index 3e84b1d..4c94da2 100644 --- a/spotdl.py +++ b/spotdl.py @@ -356,7 +356,7 @@ def grab_list(file): for raw_song in lines: try: grab_single(raw_song, number=number) - except spotipy.oauth2.SpotifyOauthError:: + except spotipy.oauth2.SpotifyOauthError: token = generate_token() global spotify spotify = spotipy.Spotify(auth=token) From 06853a03d4aee1ff9360bf04ef7356c4c5445632 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 13:32:40 +0530 Subject: [PATCH 23/56] Separate module for embedding metadata --- core/embed_metadata.py | 102 ++++++++++++++++++++++++++++++++++++++++ spotdl.py | 103 +++-------------------------------------- 2 files changed, 108 insertions(+), 97 deletions(-) create mode 100644 core/embed_metadata.py diff --git a/core/embed_metadata.py b/core/embed_metadata.py new file mode 100644 index 0000000..45625a0 --- /dev/null +++ b/core/embed_metadata.py @@ -0,0 +1,102 @@ +import urllib2 +from mutagen.easyid3 import EasyID3 +from mutagen.id3 import ID3, APIC +from mutagen.mp4 import MP4, MP4Cover + +def compare_metadata(file, metadata): + try: + if file.endswith('.mp3'): + audiofile = EasyID3('Music/' + file) + already_tagged = audiofile['title'][0] == metadata['name'] + elif file.endswith('.m4a'): + tag = {'title': '\xa9nam'} + audiofile = MP4('Music/' + file) + already_tagged = audiofile[tags['title']] == metadata['name'] + except KeyError: + already_tagged = False + return already_tagged + +def embed_metadata(music_file, meta_tags, output_ext): + if meta_tags is None: + print('Could not find meta-tags') + elif output_ext == '.m4a': + print('Fixing meta-tags') + metadata_m4a(music_file, meta_tags, output_ext) + elif output_ext == '.mp3': + print('Fixing meta-tags') + metadata_mp3(music_file, meta_tags, output_ext) + else: + print('Cannot embed meta-tags into given output extension') + +def metadata_mp3(music_file, meta_tags, output_ext): + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) + audiofile = EasyID3('Music/' + music_file + output_ext) + audiofile['artist'] = artists + audiofile['albumartist'] = meta_tags['artists'][0]['name'] + audiofile['album'] = meta_tags['album']['name'] + audiofile['title'] = meta_tags['name'] + audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] + audiofile['discnumber'] = [meta_tags['disc_number'], 0] + audiofile['date'] = meta_tags['release_date'] + audiofile['originaldate'] = meta_tags['release_date'] + audiofile['media'] = meta_tags['type'] + audiofile['copyright'] = meta_tags['copyright'] + audiofile['author'] = meta_tags['artists'][0]['name'] + audiofile['lyricist'] = meta_tags['artists'][0]['name'] + audiofile['arranger'] = meta_tags['artists'][0]['name'] + audiofile['performer'] = meta_tags['artists'][0]['name'] + audiofile['encodedby'] = meta_tags['publisher'] + audiofile['isrc'] = meta_tags['external_ids']['isrc'] + audiofile['website'] = meta_tags['external_urls']['spotify'] + audiofile['length'] = str(meta_tags['duration_ms'] / 1000) + if meta_tags['genre']: + audiofile['genre'] = meta_tags['genre'] + audiofile.save(v2_version=3) + audiofile = ID3('Music/' + music_file + output_ext) + albumart = urllib2.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() + audiofile.save(v2_version=3) + +def metadata_m4a(music_file, meta_tags, output_ext): + # eyed serves only mp3 not aac so using mutagen + # Apple has specific tags - see mutagen docs - + # http://mutagen.readthedocs.io/en/latest/api/mp4.html + tags = {'album': '\xa9alb', + 'artist': '\xa9ART', + 'date': '\xa9day', + 'title': '\xa9nam', + 'originaldate': 'purd', + 'comment': '\xa9cmt', + 'group': '\xa9grp', + 'writer': '\xa9wrt', + 'genre': '\xa9gen', + 'tracknumber': 'trkn', + 'albumartist': 'aART', + 'disknumber': 'disk', + 'cpil': 'cpil', + 'albumart': 'covr', + 'copyright': 'cprt', + 'tempo': 'tmpo'} + + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) + audiofile = MP4('Music/' + music_file + output_ext) + audiofile[tags['artist']] = artists + audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] + audiofile[tags['album']] = meta_tags['album']['name'] + audiofile[tags['title']] = meta_tags['name'] + audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] + audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] + audiofile[tags['date']] = meta_tags['release_date'] + audiofile[tags['originaldate']] = meta_tags['release_date'] + audiofile[tags['copyright']] = meta_tags['copyright'] + if meta_tags['genre']: + audiofile[tags['genre']] = meta_tags['genre'] + albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) + audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] + albumart.close() + audiofile.save() diff --git a/spotdl.py b/spotdl.py index 4c94da2..e0ae31a 100644 --- a/spotdl.py +++ b/spotdl.py @@ -1,6 +1,10 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- +from core.embed_metadata import compare_metadata +from core.embed_metadata import embed_metadata +from core.embed_metadata import metadata_mp3 +from core.embed_metadata import metadata_m4a from core.misc import input_link from core.misc import trim_song from core.misc import get_arguments @@ -12,9 +16,6 @@ from core.misc import fix_encoding from core.misc import grace_quit from bs4 import BeautifulSoup from titlecase import titlecase -from mutagen.easyid3 import EasyID3 -from mutagen.id3 import ID3, APIC -from mutagen.mp4 import MP4, MP4Cover from slugify import slugify import spotipy import spotipy.oauth2 as oauth2 @@ -233,17 +234,12 @@ def check_exists(music_file, raw_song, islist): continue if file.startswith(generate_filename(music_file)): - audiofile = EasyID3('Music/' + file) - try: - already_tagged = audiofile['title'][0] == generate_metadata(raw_song)['name'] - except KeyError: - already_tagged = False + already_tagged = compare_metadata(file, generate_metadata(raw_song)) if is_spotify(raw_song) and not already_tagged: os.remove("Music/" + file) return False - if islist: return True else: @@ -254,93 +250,6 @@ def check_exists(music_file, raw_song, islist): else: return True -# Remove song from file once downloaded -def fix_metadata(music_file, meta_tags, output_ext): - if meta_tags is None: - print('Could not find meta-tags') - elif output_ext == '.m4a': - print('Fixing meta-tags') - fix_metadata_m4a(music_file, meta_tags, output_ext) - elif output_ext == '.mp3': - print('Fixing meta-tags') - fix_metadata_mp3(music_file, meta_tags, output_ext) - else: - print('Cannot embed meta-tags into given output extension') - -def fix_metadata_mp3(music_file, meta_tags, output_ext): - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) - audiofile = EasyID3('Music/' + music_file + args.output_ext) - audiofile['artist'] = artists - audiofile['albumartist'] = meta_tags['artists'][0]['name'] - audiofile['album'] = meta_tags['album']['name'] - audiofile['title'] = meta_tags['name'] - audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] - audiofile['discnumber'] = [meta_tags['disc_number'], 0] - audiofile['date'] = meta_tags['release_date'] - audiofile['originaldate'] = meta_tags['release_date'] - audiofile['media'] = meta_tags['type'] - audiofile['copyright'] = meta_tags['copyright'] - audiofile['author'] = meta_tags['artists'][0]['name'] - audiofile['lyricist'] = meta_tags['artists'][0]['name'] - audiofile['arranger'] = meta_tags['artists'][0]['name'] - audiofile['performer'] = meta_tags['artists'][0]['name'] - audiofile['encodedby'] = meta_tags['publisher'] - audiofile['isrc'] = meta_tags['external_ids']['isrc'] - audiofile['website'] = meta_tags['external_urls']['spotify'] - audiofile['length'] = str(meta_tags['duration_ms'] / 1000) - if meta_tags['genre']: - audiofile['genre'] = meta_tags['genre'] - audiofile.save(v2_version=3) - audiofile = ID3('Music/' + music_file + args.output_ext) - albumart = urllib2.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() - audiofile.save(v2_version=3) - -def fix_metadata_m4a(music_file, meta_tags, output_ext): - # eyed serves only mp3 not aac so using mutagen - # Apple has specific tags - see mutagen docs - - # http://mutagen.readthedocs.io/en/latest/api/mp4.html - tags = {'album': '\xa9alb', - 'artist': '\xa9ART', - 'date': '\xa9day', - 'title': '\xa9nam', - 'originaldate': 'purd', - 'comment': '\xa9cmt', - 'group': '\xa9grp', - 'writer': '\xa9wrt', - 'genre': '\xa9gen', - 'tracknumber': 'trkn', - 'albumartist': 'aART', - 'disknumber': 'disk', - 'cpil': 'cpil', - 'albumart': 'covr', - 'copyright': 'cprt', - 'tempo': 'tmpo'} - - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) - audiofile = MP4('Music/' + music_file + args.output_ext) - audiofile[tags['artist']] = artists - audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] - audiofile[tags['album']] = meta_tags['album']['name'] - audiofile[tags['title']] = meta_tags['name'] - audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] - audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] - audiofile[tags['date']] = meta_tags['release_date'] - audiofile[tags['originaldate']] = meta_tags['release_date'] - audiofile[tags['copyright']] = meta_tags['copyright'] - if meta_tags['genre']: - audiofile[tags['genre']] = meta_tags['genre'] - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) - audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] - albumart.close() - audiofile.save() - - def grab_list(file): lines = open(file, 'r').read() lines = lines.splitlines() @@ -392,7 +301,7 @@ def grab_single(raw_song, number=None): convert_song(music_file) meta_tags = generate_metadata(raw_song) if not args.no_metadata: - fix_metadata(music_file, meta_tags, args.output_ext) + embed_metadata(music_file, meta_tags, args.output_ext) if __name__ == '__main__': From e9c247ead9dc3c1745cfe2b0e79d19378ed18625 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 14:35:14 +0530 Subject: [PATCH 24/56] Switch to class for embedding metadata --- core/embed_metadata.py | 102 --------------------------------------- core/metadata.py | 106 +++++++++++++++++++++++++++++++++++++++++ spotdl.py | 17 +++---- 3 files changed, 115 insertions(+), 110 deletions(-) delete mode 100644 core/embed_metadata.py create mode 100644 core/metadata.py diff --git a/core/embed_metadata.py b/core/embed_metadata.py deleted file mode 100644 index 45625a0..0000000 --- a/core/embed_metadata.py +++ /dev/null @@ -1,102 +0,0 @@ -import urllib2 -from mutagen.easyid3 import EasyID3 -from mutagen.id3 import ID3, APIC -from mutagen.mp4 import MP4, MP4Cover - -def compare_metadata(file, metadata): - try: - if file.endswith('.mp3'): - audiofile = EasyID3('Music/' + file) - already_tagged = audiofile['title'][0] == metadata['name'] - elif file.endswith('.m4a'): - tag = {'title': '\xa9nam'} - audiofile = MP4('Music/' + file) - already_tagged = audiofile[tags['title']] == metadata['name'] - except KeyError: - already_tagged = False - return already_tagged - -def embed_metadata(music_file, meta_tags, output_ext): - if meta_tags is None: - print('Could not find meta-tags') - elif output_ext == '.m4a': - print('Fixing meta-tags') - metadata_m4a(music_file, meta_tags, output_ext) - elif output_ext == '.mp3': - print('Fixing meta-tags') - metadata_mp3(music_file, meta_tags, output_ext) - else: - print('Cannot embed meta-tags into given output extension') - -def metadata_mp3(music_file, meta_tags, output_ext): - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) - audiofile = EasyID3('Music/' + music_file + output_ext) - audiofile['artist'] = artists - audiofile['albumartist'] = meta_tags['artists'][0]['name'] - audiofile['album'] = meta_tags['album']['name'] - audiofile['title'] = meta_tags['name'] - audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] - audiofile['discnumber'] = [meta_tags['disc_number'], 0] - audiofile['date'] = meta_tags['release_date'] - audiofile['originaldate'] = meta_tags['release_date'] - audiofile['media'] = meta_tags['type'] - audiofile['copyright'] = meta_tags['copyright'] - audiofile['author'] = meta_tags['artists'][0]['name'] - audiofile['lyricist'] = meta_tags['artists'][0]['name'] - audiofile['arranger'] = meta_tags['artists'][0]['name'] - audiofile['performer'] = meta_tags['artists'][0]['name'] - audiofile['encodedby'] = meta_tags['publisher'] - audiofile['isrc'] = meta_tags['external_ids']['isrc'] - audiofile['website'] = meta_tags['external_urls']['spotify'] - audiofile['length'] = str(meta_tags['duration_ms'] / 1000) - if meta_tags['genre']: - audiofile['genre'] = meta_tags['genre'] - audiofile.save(v2_version=3) - audiofile = ID3('Music/' + music_file + output_ext) - albumart = urllib2.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() - audiofile.save(v2_version=3) - -def metadata_m4a(music_file, meta_tags, output_ext): - # eyed serves only mp3 not aac so using mutagen - # Apple has specific tags - see mutagen docs - - # http://mutagen.readthedocs.io/en/latest/api/mp4.html - tags = {'album': '\xa9alb', - 'artist': '\xa9ART', - 'date': '\xa9day', - 'title': '\xa9nam', - 'originaldate': 'purd', - 'comment': '\xa9cmt', - 'group': '\xa9grp', - 'writer': '\xa9wrt', - 'genre': '\xa9gen', - 'tracknumber': 'trkn', - 'albumartist': 'aART', - 'disknumber': 'disk', - 'cpil': 'cpil', - 'albumart': 'covr', - 'copyright': 'cprt', - 'tempo': 'tmpo'} - - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) - audiofile = MP4('Music/' + music_file + output_ext) - audiofile[tags['artist']] = artists - audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] - audiofile[tags['album']] = meta_tags['album']['name'] - audiofile[tags['title']] = meta_tags['name'] - audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] - audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] - audiofile[tags['date']] = meta_tags['release_date'] - audiofile[tags['originaldate']] = meta_tags['release_date'] - audiofile[tags['copyright']] = meta_tags['copyright'] - if meta_tags['genre']: - audiofile[tags['genre']] = meta_tags['genre'] - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) - audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] - albumart.close() - audiofile.save() diff --git a/core/metadata.py b/core/metadata.py new file mode 100644 index 0000000..f5c77ad --- /dev/null +++ b/core/metadata.py @@ -0,0 +1,106 @@ +import urllib2 +from mutagen.easyid3 import EasyID3 +from mutagen.id3 import ID3, APIC +from mutagen.mp4 import MP4, MP4Cover + + +def compare_metadata(file, metadata): + try: + if file.endswith('.mp3'): + audiofile = EasyID3('Music/' + file) + already_tagged = audiofile['title'][0] == metadata['name'] + elif file.endswith('.m4a'): + tag = {'title': '\xa9nam'} + audiofile = MP4('Music/' + file) + already_tagged = audiofile[tags['title']] == metadata['name'] + except KeyError: + already_tagged = False + return already_tagged + +class embed_metadata(object): + + def __init__(self, music_file, meta_tags, output_ext): + + if meta_tags is None: + print('Could not find meta-tags') + elif output_ext == '.m4a': + print('Fixing meta-tags') + self.metadata_m4a(music_file, meta_tags, output_ext) + elif output_ext == '.mp3': + print('Fixing meta-tags') + self.metadata_mp3(music_file, meta_tags, output_ext) + else: + print('Cannot embed meta-tags into given output extension') + + def metadata_mp3(self, music_file, meta_tags, output_ext): + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) + audiofile = EasyID3('Music/' + music_file + output_ext) + audiofile['artist'] = artists + audiofile['albumartist'] = meta_tags['artists'][0]['name'] + audiofile['album'] = meta_tags['album']['name'] + audiofile['title'] = meta_tags['name'] + audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] + audiofile['discnumber'] = [meta_tags['disc_number'], 0] + audiofile['date'] = meta_tags['release_date'] + audiofile['originaldate'] = meta_tags['release_date'] + audiofile['media'] = meta_tags['type'] + audiofile['copyright'] = meta_tags['copyright'] + audiofile['author'] = meta_tags['artists'][0]['name'] + audiofile['lyricist'] = meta_tags['artists'][0]['name'] + audiofile['arranger'] = meta_tags['artists'][0]['name'] + audiofile['performer'] = meta_tags['artists'][0]['name'] + audiofile['encodedby'] = meta_tags['publisher'] + audiofile['isrc'] = meta_tags['external_ids']['isrc'] + audiofile['website'] = meta_tags['external_urls']['spotify'] + audiofile['length'] = str(meta_tags['duration_ms'] / 1000) + if meta_tags['genre']: + audiofile['genre'] = meta_tags['genre'] + audiofile.save(v2_version=3) + audiofile = ID3('Music/' + music_file + output_ext) + albumart = urllib2.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() + audiofile.save(v2_version=3) + + def metadata_m4a(self, music_file, meta_tags, output_ext): + # eyed serves only mp3 not aac so using mutagen + # Apple has specific tags - see mutagen docs - + # http://mutagen.readthedocs.io/en/latest/api/mp4.html + tags = {'album': '\xa9alb', + 'artist': '\xa9ART', + 'date': '\xa9day', + 'title': '\xa9nam', + 'originaldate': 'purd', + 'comment': '\xa9cmt', + 'group': '\xa9grp', + 'writer': '\xa9wrt', + 'genre': '\xa9gen', + 'tracknumber': 'trkn', + 'albumartist': 'aART', + 'disknumber': 'disk', + 'cpil': 'cpil', + 'albumart': 'covr', + 'copyright': 'cprt', + 'tempo': 'tmpo'} + + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) + audiofile = MP4('Music/' + music_file + output_ext) + audiofile[tags['artist']] = artists + audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] + audiofile[tags['album']] = meta_tags['album']['name'] + audiofile[tags['title']] = meta_tags['name'] + audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] + audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] + audiofile[tags['date']] = meta_tags['release_date'] + audiofile[tags['originaldate']] = meta_tags['release_date'] + audiofile[tags['copyright']] = meta_tags['copyright'] + if meta_tags['genre']: + audiofile[tags['genre']] = meta_tags['genre'] + albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) + audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] + albumart.close() + audiofile.save() diff --git a/spotdl.py b/spotdl.py index e0ae31a..148cd03 100644 --- a/spotdl.py +++ b/spotdl.py @@ -1,10 +1,9 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- -from core.embed_metadata import compare_metadata -from core.embed_metadata import embed_metadata -from core.embed_metadata import metadata_mp3 -from core.embed_metadata import metadata_m4a +#from core.metadata import embed_metadata +#from core.metadata import compare_metadata +from core import metadata from core.misc import input_link from core.misc import trim_song from core.misc import get_arguments @@ -235,15 +234,17 @@ def check_exists(music_file, raw_song, islist): if file.startswith(generate_filename(music_file)): - already_tagged = compare_metadata(file, generate_metadata(raw_song)) + already_tagged = metadata.compare_metadata(file, generate_metadata(raw_song)) if is_spotify(raw_song) and not already_tagged: os.remove("Music/" + file) return False + if islist: return True else: prompt = raw_input('Song with same name has already been downloaded. Re-download? (y/n): ').lower() + if prompt == "y": os.remove("Music/" + file) return False @@ -251,8 +252,8 @@ def check_exists(music_file, raw_song, islist): return True def grab_list(file): - lines = open(file, 'r').read() - lines = lines.splitlines() + with open(file, 'r') as listed: + lines = (listed.read()).splitlines() # Ignore blank lines in file (if any) try: lines.remove('') @@ -301,7 +302,7 @@ def grab_single(raw_song, number=None): convert_song(music_file) meta_tags = generate_metadata(raw_song) if not args.no_metadata: - embed_metadata(music_file, meta_tags, args.output_ext) + metadata.embed_metadata(music_file, meta_tags, args.output_ext) if __name__ == '__main__': From 280ff41633d3e5397f2ff71702d95c900bbdf433 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 14:40:12 +0530 Subject: [PATCH 25/56] Make functions and class names a little verbose --- core/metadata.py | 12 ++++++------ spotdl.py | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/core/metadata.py b/core/metadata.py index f5c77ad..444f9a3 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -4,7 +4,7 @@ from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover -def compare_metadata(file, metadata): +def compare(file, metadata): try: if file.endswith('.mp3'): audiofile = EasyID3('Music/' + file) @@ -17,7 +17,7 @@ def compare_metadata(file, metadata): already_tagged = False return already_tagged -class embed_metadata(object): +class embed(object): def __init__(self, music_file, meta_tags, output_ext): @@ -25,14 +25,14 @@ class embed_metadata(object): print('Could not find meta-tags') elif output_ext == '.m4a': print('Fixing meta-tags') - self.metadata_m4a(music_file, meta_tags, output_ext) + self.embed_m4a(music_file, meta_tags, output_ext) elif output_ext == '.mp3': print('Fixing meta-tags') - self.metadata_mp3(music_file, meta_tags, output_ext) + self.embed_mp3(music_file, meta_tags, output_ext) else: print('Cannot embed meta-tags into given output extension') - def metadata_mp3(self, music_file, meta_tags, output_ext): + def embed_mp3(self, music_file, meta_tags, output_ext): artists = [] for artist in meta_tags['artists']: artists.append(artist['name']) @@ -64,7 +64,7 @@ class embed_metadata(object): albumart.close() audiofile.save(v2_version=3) - def metadata_m4a(self, music_file, meta_tags, output_ext): + def embed_m4a(self, music_file, meta_tags, output_ext): # eyed serves only mp3 not aac so using mutagen # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html diff --git a/spotdl.py b/spotdl.py index 148cd03..28c83af 100644 --- a/spotdl.py +++ b/spotdl.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: UTF-8 -*- -#from core.metadata import embed_metadata -#from core.metadata import compare_metadata from core import metadata from core.misc import input_link from core.misc import trim_song @@ -234,7 +232,7 @@ def check_exists(music_file, raw_song, islist): if file.startswith(generate_filename(music_file)): - already_tagged = metadata.compare_metadata(file, generate_metadata(raw_song)) + already_tagged = metadata.compare(file, generate_metadata(raw_song)) if is_spotify(raw_song) and not already_tagged: os.remove("Music/" + file) @@ -302,7 +300,7 @@ def grab_single(raw_song, number=None): convert_song(music_file) meta_tags = generate_metadata(raw_song) if not args.no_metadata: - metadata.embed_metadata(music_file, meta_tags, args.output_ext) + metadata.embed(music_file, meta_tags, args.output_ext) if __name__ == '__main__': From 5d3c27b622fb2234f7216e57d1ac471d5fb3f65f Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 14:53:02 +0530 Subject: [PATCH 26/56] Import misc --- spotdl.py | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/spotdl.py b/spotdl.py index 28c83af..e52fc20 100644 --- a/spotdl.py +++ b/spotdl.py @@ -2,15 +2,7 @@ # -*- coding: UTF-8 -*- from core import metadata -from core.misc import input_link -from core.misc import trim_song -from core.misc import get_arguments -from core.misc import is_spotify -from core.misc import generate_filename -from core.misc import generate_token -from core.misc import generate_search_URL -from core.misc import fix_encoding -from core.misc import grace_quit +from core import misc from bs4 import BeautifulSoup from titlecase import titlecase from slugify import slugify @@ -23,14 +15,14 @@ import os import subprocess def generate_songname(raw_song): - if is_spotify(raw_song): + if misc.is_spotify(raw_song): tags = generate_metadata(raw_song) raw_song = tags['artists'][0]['name'] + ' - ' + tags['name'] return raw_song def generate_metadata(raw_song): try: - if is_spotify(raw_song): + if misc.is_spotify(raw_song): meta_tags = spotify.track(raw_song) else: meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] @@ -55,7 +47,7 @@ def generate_metadata(raw_song): def generate_YouTube_URL(raw_song): song = generate_songname(raw_song) - searchURL = generate_search_URL(song) + searchURL = misc.generate_search_URL(song) item = urllib2.urlopen(searchURL).read() items_parse = BeautifulSoup(item, "html.parser") check = 1 @@ -70,7 +62,7 @@ def generate_YouTube_URL(raw_song): links.append(x.find('a')['href']) check += 1 print('') - result = input_link(links) + result = misc.input_link(links) if result is None: return None else: @@ -112,11 +104,11 @@ def feed_playlist(username): links = [] check = 1 for playlist in playlists['items']: - print(str(check) + '. ' + fix_encoding(playlist['name']) + ' (' + str(playlist['tracks']['total']) + ' tracks)') + print(str(check) + '. ' + misc.fix_encoding(playlist['name']) + ' (' + str(playlist['tracks']['total']) + ' tracks)') links.append(playlist) check += 1 print('') - playlist = input_link(links) + playlist = misc.input_link(links) results = spotify.user_playlist(playlist['owner']['id'], playlist['id'], fields="tracks,next") print('') file = slugify(playlist['name'], ok='-_()[]{}') + '.txt' @@ -128,7 +120,7 @@ def feed_playlist(username): feed_tracks(file, tracks) def download_song(content): - music_file = generate_filename(content.title) + music_file = misc.generate_filename(content.title) if args.input_ext == '.webm': link = content.getbestaudio(preftype='webm') if link is not None: @@ -230,11 +222,11 @@ def check_exists(music_file, raw_song, islist): os.remove("Music/" + file) continue - if file.startswith(generate_filename(music_file)): + if file.startswith(misc.generate_filename(music_file)): already_tagged = metadata.compare(file, generate_metadata(raw_song)) - if is_spotify(raw_song) and not already_tagged: + if misc.is_spotify(raw_song) and not already_tagged: os.remove("Music/" + file) return False @@ -265,22 +257,22 @@ def grab_list(file): try: grab_single(raw_song, number=number) except spotipy.oauth2.SpotifyOauthError: - token = generate_token() + token = misc.generate_token() global spotify spotify = spotipy.Spotify(auth=token) grab_single(raw_song, number=number) except KeyboardInterrupt: - grace_quit() + misc.grace_quit() except (urllib2.URLError, IOError): lines.append(raw_song) - trim_song(file) + misc.trim_song(file) with open(file, 'a') as myfile: myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') continue finally: print('') - trim_song(file) + misc.trim_song(file) number += 1 # Logic behind preparing the song to download to finishing meta-tags @@ -293,7 +285,7 @@ def grab_single(raw_song, number=None): if content is None: return print(get_YouTube_title(content, number)) - music_file = generate_filename(content.title) + music_file = misc.generate_filename(content.title) if not check_exists(music_file, raw_song, islist=islist): download_song(content) print('') @@ -312,11 +304,11 @@ if __name__ == '__main__': if not os.path.exists("Music"): os.makedirs("Music") - token = generate_token() + token = misc.generate_token() spotify = spotipy.Spotify(auth=token) # Set up arguments - args = get_arguments() + args = misc.get_arguments() if args.song: grab_single(raw_song=args.song) From 70f18067db6d81adc5df87153f6d2286aaabc5a9 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 16:21:31 +0530 Subject: [PATCH 27/56] Capture requests.exception.ConnectionError --- spotdl.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spotdl.py b/spotdl.py index e52fc20..8bd76c9 100644 --- a/spotdl.py +++ b/spotdl.py @@ -261,15 +261,15 @@ def grab_list(file): global spotify spotify = spotipy.Spotify(auth=token) grab_single(raw_song, number=number) - except KeyboardInterrupt: - misc.grace_quit() - except (urllib2.URLError, IOError): + except (urllib2.URLError, requests.exceptions.ConnectionError, IOError): lines.append(raw_song) misc.trim_song(file) with open(file, 'a') as myfile: myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') continue + except KeyboardInterrupt: + misc.grace_quit() finally: print('') misc.trim_song(file) From 24636d2d476f28fbfe61cf71e0f84451746196ce Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 16:35:35 +0530 Subject: [PATCH 28/56] Revert --- spotdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotdl.py b/spotdl.py index 8bd76c9..fe8f5f6 100644 --- a/spotdl.py +++ b/spotdl.py @@ -261,7 +261,7 @@ def grab_list(file): global spotify spotify = spotipy.Spotify(auth=token) grab_single(raw_song, number=number) - except (urllib2.URLError, requests.exceptions.ConnectionError, IOError): + except (urllib2.URLError, IOError): lines.append(raw_song) misc.trim_song(file) with open(file, 'a') as myfile: From 4547ef7e71545f686ce11965e549bd7612b37f77 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 16:39:47 +0530 Subject: [PATCH 29/56] Capture TypeError --- spotdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotdl.py b/spotdl.py index fe8f5f6..47c0ac0 100644 --- a/spotdl.py +++ b/spotdl.py @@ -261,7 +261,7 @@ def grab_list(file): global spotify spotify = spotipy.Spotify(auth=token) grab_single(raw_song, number=number) - except (urllib2.URLError, IOError): + except (urllib2.URLError, TypeError, IOError): lines.append(raw_song) misc.trim_song(file) with open(file, 'a') as myfile: From 5fcdc3c22770310fb72ee9bdf46631bc8792ed58 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 18:04:08 +0530 Subject: [PATCH 30/56] Fix imports for python3 --- core/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 142 bytes core/__pycache__/metadata.cpython-35.pyc | Bin 0 -> 3689 bytes core/__pycache__/misc.cpython-35.pyc | Bin 0 -> 3504 bytes core/metadata.py | 5 ++++- spotdl.py | 6 +++++- 5 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 core/__pycache__/__init__.cpython-35.pyc create mode 100644 core/__pycache__/metadata.cpython-35.pyc create mode 100644 core/__pycache__/misc.cpython-35.pyc diff --git a/core/__pycache__/__init__.cpython-35.pyc b/core/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ce5abb47b44e574a619066bf64d2f5ac0d84cfb GIT binary patch literal 142 zcmWgV<>gv)-Z_$yfq~&M5W@jTzyXMhS%5?e14FPTqu)w~B9JhG_+_o1k)NBYUy!L^ zoLrPyP*SX4P+5|ZpQj&OkYAFSR;lZfU!IqfpO}(bq@SE$l&T*epP83g5+AQuPY`6QdUutOG$}QS*}vNRVmaGGX$61 zmu45Dz#=OTd z=q-lqkZ|cmukjt`Mm7(^tMOX2o-G|SgZgS(<^9}7oY-X>e+8R{Y9-kUY z3uP0f_&db-k90(|XO{WaA@vI?9bO{9MYNAmtB|Nt7vpx1psG=Kze2}VN;juHJ>s5! zU^7fPLNAv}^g8FIgc^>{dU=^Hkz2ZESN>{t#b@VcGmfgf)U88zHObcxy=S8$8m#GB z8I`eA4koYNG)yGxH^LDw?H}I&c+#tBU#6lsh{|2XD_5-3ZW#^>?f+ViA1jq9Sh+h< zEVZA=G8ADM>P9#cQD(qS7%LfyaUY$AQs^pvzae!2j{f`ewcRX{Yk9O*3{;et#acct zce8ZuMV^(>&iLM=>>!P^P)M~l$dp{04zQY!b$yT}d8lL)o{Gy5zYiqr5L#I zz#snQVot;FQQ=qakH&IphwX9EWf!&<_?kpicUku|+^( z`|6x6tj^drjlO1lVcZuzZ|iVrGaJQXCC$o}ohTJ6ym$9FayHC|b5Ei-Q93+}<9OWP z3{&o8R(PsiiPAD#8Ah*Vx{{5`d<2U(rA&(`OM6va%M}cC!}RLo7@bt}^`5KULdH9~ znefT$n{(0N`S77Abkq9l!=E`9R)9`O`~7~DMrFU>MXO+lDs9BI?KGXsR#px>X3*8C z=}A<4SfAwRrwIFyIHTU&GU0Kk`{;LzbX=n&m-F@r!L3l2!}00@z2xkz@-{CbASQ!7 zHazl}I393(;eeBvJ2C26w0?@zEi%#jw$*?&Iyb31L=@4lW%0K0Ho;~r*0G*v&$Q-j z>w?wI+tx+fx@cSHX14HN^wN9L%kM=OExKe!>Dty6%y`)%%NDr`p! zE3}M#<<;J>mN%)Z{^qb6HO6lO_h=8YXj*ab~) zO#3Jq%dn7r(5lp~k?l-H!zc}7iei~ zFda%2G;*nSGL>LbjeN8nN5w9qG96?>itTaG=G0agq92JOaHB#E^vnr1QgNZ{h2`j= zejvAtsFXoVj8s^H4*E$E{ih1*^kSI~%UxX?%2dgq`TBkzmnbvFcTkh|q9h#3LRaCD zZyF2_Mhe^{!e!Jn%C)^*0uizp1T}L9n_UPM(`~P*y)e&lMVZ@4SBp}CYC;TR{MX?( zj<)Y>u4J$?y|l|0lCBNntdKolEx{9L8sZ9zWfmW>xW=Lbq5Wxu&+(I5g!T?f@c@Dx z&$-}Sauy6Z%{v`u3H8q8dl`hq-;Ohn?-Fb}xTF@G>&_2RKb*BBHsQ~F{e;CDaJ+)D ziBkLvx3#^dkQyK&do|hjjc72ffKNo5#0&{Mab`*2?Sw3tCvlO)0*Ol`E|XX!u|%S4 z{;m+RSX?D!uDHhO8qG0FGFR57T)8sgN~9hV5L{_8*>dH9<;t69xU$ZMNG>oYCfYT8 z2`5PIE-0Wo(OjBnn6FMzw2b{Jf?PMqZS+LyBkgx8Gz6j80YH964I01zXH<(5)!maZ z!57B(6aga}1nf0&h?+sbvFcBTrI>B)V5l*(fvI>ZJoAstuwC#CZ^*GvAg&{y_HZ2| zSRZo}ob}dmPG=12e847kOFdIw;+O1;-X~j%<2`wY;pCZvIB7oo@+-v$ZqgLt*ig;s zqfOm~HgNNi5&=Jv5}B40?I0dDKX3L!cZ zEIwqh#^O_mpk>&ANh>US4Yke+evGQmSp1lU$KodtLF>tHzI^uZtNyQ^KmJ1fl;Iyi zpxgUWo!^h=^4+ZBe5&&k^9f49f?N;D{{O%2%9IK^&J`s6JkosGB>p^-om+RlpY$8y zR?p)n4?o8HeckN$lT3_auDARBSEDet8g&&jFx;bfR`1M|pVf+=NooOt*{R@dX;<1a z?G=OdW`o&egm;%rS8=n9Be8DCGiXgwMwmVgS)Rg4zF~LV>@#n>Zaw9Amgc_z-LYi5 ys^qJYEJ|IURLlchd)fA$9F%4X@0+joAK112lK&qm<{;*nE-O`#l$LBzA0OLQ2-fmOPFfSGK2~X4KkpnwXsq2f{8XNFV_3 zE<_Wtp4>k7cXXz2ed)jGJ6`kTzmS*qI}4DJ<$&7N*|TTQp8MHLb90S<{deotf0~T_ z#EyM7;BPS0zcF$ASuA9%JB2OQwI+C*b!`LNEOgk=VO@t=<(UfGud;5Hafk5=`x{u) z7{{_^;2Fm8S2u8-@ma`CgW!qKWktRoMZgFf#*rq zoz{i-5S(qdtb9g==^*HjeTy*oNViiMTNnx=u{TF-++~_E4V^6Z&iox&>@AiyJE$`G zJA$>%-ZI)fundNk1edSu5T(NoY$jijC3dt?7o^truN_v=6?V{I@<(P_)-PMHu=NAR z_jICF-OesOc;IgB#jz6ZI8LP-rNdnz@44?ua!FHmvE29ELoW?F73rb zv6KW$%9lZ+)lxFndvUb1lf)WkY(I`iQ5bt%$R$6PVrdvCzmtsfhPk&#%xAzzoyWv1 z2fwq{HLHnV#h$n3EQwfSQ)g7LIE!JNu?qlhfR)J&Hv7jSE8MV;C1d*tGn*W6WE-PB zfAK>}?HKB7?8sqn9k!1dq!n_--)(lq_yI8JOH=2Te3@G#>EOavC9lK@j8&1s<9S0R zVW6RfPj<+EA_!=8$x0U1EmF)>(R5kp=C z>=S}M3d30pqxmMh3tgZmG>4LiCR##UW*-@gKq=Cw3XD}61xsX;$spv;JKM4lhx@=H zXOJq$sOE93Nh8886j<;F?ddX)D1)$fp79HeUj&6?!4E`niSf&nX(h##k14J){+nYI ziyu>b!uYjg6up2h zc8$kw6l+(YT5)%RhE#>cAw{30==X<-7-XB@;o<^zVcXI>3KzLCTZACpwIz#`6??RLSiSMo?!v#+K+iY*sgA~JG!hRLeoL;P+% zdbHJQ)$+P02Z<+@$mbu)K{^zX{u3=__mj!`ap4d3dxXx@NtksPJL zHDgW21r+LH)POA@`afgK&v0g1zNslu5j92Z4*-gSjEajoM*Z+hs^(PIOie>g#}at; z(BMs#CgM6fs4%%j1#JfvZO7E<+3ojKF@Lt)%(=0$j80x8lPTLTpT7SezLq0vpvh;X zKmz!Js&vHB_UqINDK$^pp%JKF$>I`4sZs#J;3% zkT?6mo1TdLmXb3P9(afs~a${Q(d40KdIH3fry0kwCGJJ?t^2g+*#|gC2wQx zW0;Swt6~`CZUH#ae4x-Tork(m*@)?#(w+6g0O8yVc-LL)i`BmH`(k}{b!~ZN)w{iR zcV+$d9pB&F5_!$d`&Pywj>|XLt6rPSm}nrO{~A!P|}c zN5_wcOHwFQT=h^TUq2ZTqtV zgA$(f_j;gt2GL3+h94WLzN);O6)oik^y8Ye(ZSmf$ zrn=wWXxVaef`We>HCA~OWtgfTo}2wRubHS7$h+$6RLJoO*<}aEw=qgt7bx2Z=VMGi zGP*H%3jk!LM-sa*?R-VM5-jB0c9#*+|BnB0l8`kVp#?+;_1hT;La%hY9hD>tpv)`H!fYu1{J7}aJSqxOGZtT~tf literal 0 HcmV?d00001 diff --git a/core/metadata.py b/core/metadata.py index 444f9a3..0dbd199 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -1,8 +1,11 @@ -import urllib2 from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover +try: + import urllib2 +except ImportError: + import urllib.request as urllib2 def compare(file, metadata): try: diff --git a/spotdl.py b/spotdl.py index 47c0ac0..6855c57 100644 --- a/spotdl.py +++ b/spotdl.py @@ -8,12 +8,16 @@ from titlecase import titlecase from slugify import slugify import spotipy import spotipy.oauth2 as oauth2 -import urllib2 import pafy import sys import os import subprocess +try: + import urllib2 +except ImportError: + import urllib.request as urllib2 + def generate_songname(raw_song): if misc.is_spotify(raw_song): tags = generate_metadata(raw_song) From fe0cc185a6c150faba9ac5e5cda1fd500ab3fa7a Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 18:04:47 +0530 Subject: [PATCH 31/56] Fix imports for python3 --- core/__pycache__/__init__.cpython-35.pyc | Bin 142 -> 0 bytes core/__pycache__/metadata.cpython-35.pyc | Bin 3689 -> 0 bytes core/__pycache__/misc.cpython-35.pyc | Bin 3504 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/__init__.cpython-35.pyc delete mode 100644 core/__pycache__/metadata.cpython-35.pyc delete mode 100644 core/__pycache__/misc.cpython-35.pyc diff --git a/core/__pycache__/__init__.cpython-35.pyc b/core/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index 2ce5abb47b44e574a619066bf64d2f5ac0d84cfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmWgV<>gv)-Z_$yfq~&M5W@jTzyXMhS%5?e14FPTqu)w~B9JhG_+_o1k)NBYUy!L^ zoLrPyP*SX4P+5|ZpQj&OkYAFSR;lZfU!IqfpO}(bq@SE$l&T*epP83g5+AQuPY`6QdUutOG$}QS*}vNRVmaGGX$61 zmu45Dz#=OTd z=q-lqkZ|cmukjt`Mm7(^tMOX2o-G|SgZgS(<^9}7oY-X>e+8R{Y9-kUY z3uP0f_&db-k90(|XO{WaA@vI?9bO{9MYNAmtB|Nt7vpx1psG=Kze2}VN;juHJ>s5! zU^7fPLNAv}^g8FIgc^>{dU=^Hkz2ZESN>{t#b@VcGmfgf)U88zHObcxy=S8$8m#GB z8I`eA4koYNG)yGxH^LDw?H}I&c+#tBU#6lsh{|2XD_5-3ZW#^>?f+ViA1jq9Sh+h< zEVZA=G8ADM>P9#cQD(qS7%LfyaUY$AQs^pvzae!2j{f`ewcRX{Yk9O*3{;et#acct zce8ZuMV^(>&iLM=>>!P^P)M~l$dp{04zQY!b$yT}d8lL)o{Gy5zYiqr5L#I zz#snQVot;FQQ=qakH&IphwX9EWf!&<_?kpicUku|+^( z`|6x6tj^drjlO1lVcZuzZ|iVrGaJQXCC$o}ohTJ6ym$9FayHC|b5Ei-Q93+}<9OWP z3{&o8R(PsiiPAD#8Ah*Vx{{5`d<2U(rA&(`OM6va%M}cC!}RLo7@bt}^`5KULdH9~ znefT$n{(0N`S77Abkq9l!=E`9R)9`O`~7~DMrFU>MXO+lDs9BI?KGXsR#px>X3*8C z=}A<4SfAwRrwIFyIHTU&GU0Kk`{;LzbX=n&m-F@r!L3l2!}00@z2xkz@-{CbASQ!7 zHazl}I393(;eeBvJ2C26w0?@zEi%#jw$*?&Iyb31L=@4lW%0K0Ho;~r*0G*v&$Q-j z>w?wI+tx+fx@cSHX14HN^wN9L%kM=OExKe!>Dty6%y`)%%NDr`p! zE3}M#<<;J>mN%)Z{^qb6HO6lO_h=8YXj*ab~) zO#3Jq%dn7r(5lp~k?l-H!zc}7iei~ zFda%2G;*nSGL>LbjeN8nN5w9qG96?>itTaG=G0agq92JOaHB#E^vnr1QgNZ{h2`j= zejvAtsFXoVj8s^H4*E$E{ih1*^kSI~%UxX?%2dgq`TBkzmnbvFcTkh|q9h#3LRaCD zZyF2_Mhe^{!e!Jn%C)^*0uizp1T}L9n_UPM(`~P*y)e&lMVZ@4SBp}CYC;TR{MX?( zj<)Y>u4J$?y|l|0lCBNntdKolEx{9L8sZ9zWfmW>xW=Lbq5Wxu&+(I5g!T?f@c@Dx z&$-}Sauy6Z%{v`u3H8q8dl`hq-;Ohn?-Fb}xTF@G>&_2RKb*BBHsQ~F{e;CDaJ+)D ziBkLvx3#^dkQyK&do|hjjc72ffKNo5#0&{Mab`*2?Sw3tCvlO)0*Ol`E|XX!u|%S4 z{;m+RSX?D!uDHhO8qG0FGFR57T)8sgN~9hV5L{_8*>dH9<;t69xU$ZMNG>oYCfYT8 z2`5PIE-0Wo(OjBnn6FMzw2b{Jf?PMqZS+LyBkgx8Gz6j80YH964I01zXH<(5)!maZ z!57B(6aga}1nf0&h?+sbvFcBTrI>B)V5l*(fvI>ZJoAstuwC#CZ^*GvAg&{y_HZ2| zSRZo}ob}dmPG=12e847kOFdIw;+O1;-X~j%<2`wY;pCZvIB7oo@+-v$ZqgLt*ig;s zqfOm~HgNNi5&=Jv5}B40?I0dDKX3L!cZ zEIwqh#^O_mpk>&ANh>US4Yke+evGQmSp1lU$KodtLF>tHzI^uZtNyQ^KmJ1fl;Iyi zpxgUWo!^h=^4+ZBe5&&k^9f49f?N;D{{O%2%9IK^&J`s6JkosGB>p^-om+RlpY$8y zR?p)n4?o8HeckN$lT3_auDARBSEDet8g&&jFx;bfR`1M|pVf+=NooOt*{R@dX;<1a z?G=OdW`o&egm;%rS8=n9Be8DCGiXgwMwmVgS)Rg4zF~LV>@#n>Zaw9Amgc_z-LYi5 ys^qJYEJ|IURLlchd)fA$9F%4X@0+joAK112lK&qm<{;*nE-O`#l$LBzA0OLQ2-fmOPFfSGK2~X4KkpnwXsq2f{8XNFV_3 zE<_Wtp4>k7cXXz2ed)jGJ6`kTzmS*qI}4DJ<$&7N*|TTQp8MHLb90S<{deotf0~T_ z#EyM7;BPS0zcF$ASuA9%JB2OQwI+C*b!`LNEOgk=VO@t=<(UfGud;5Hafk5=`x{u) z7{{_^;2Fm8S2u8-@ma`CgW!qKWktRoMZgFf#*rq zoz{i-5S(qdtb9g==^*HjeTy*oNViiMTNnx=u{TF-++~_E4V^6Z&iox&>@AiyJE$`G zJA$>%-ZI)fundNk1edSu5T(NoY$jijC3dt?7o^truN_v=6?V{I@<(P_)-PMHu=NAR z_jICF-OesOc;IgB#jz6ZI8LP-rNdnz@44?ua!FHmvE29ELoW?F73rb zv6KW$%9lZ+)lxFndvUb1lf)WkY(I`iQ5bt%$R$6PVrdvCzmtsfhPk&#%xAzzoyWv1 z2fwq{HLHnV#h$n3EQwfSQ)g7LIE!JNu?qlhfR)J&Hv7jSE8MV;C1d*tGn*W6WE-PB zfAK>}?HKB7?8sqn9k!1dq!n_--)(lq_yI8JOH=2Te3@G#>EOavC9lK@j8&1s<9S0R zVW6RfPj<+EA_!=8$x0U1EmF)>(R5kp=C z>=S}M3d30pqxmMh3tgZmG>4LiCR##UW*-@gKq=Cw3XD}61xsX;$spv;JKM4lhx@=H zXOJq$sOE93Nh8886j<;F?ddX)D1)$fp79HeUj&6?!4E`niSf&nX(h##k14J){+nYI ziyu>b!uYjg6up2h zc8$kw6l+(YT5)%RhE#>cAw{30==X<-7-XB@;o<^zVcXI>3KzLCTZACpwIz#`6??RLSiSMo?!v#+K+iY*sgA~JG!hRLeoL;P+% zdbHJQ)$+P02Z<+@$mbu)K{^zX{u3=__mj!`ap4d3dxXx@NtksPJL zHDgW21r+LH)POA@`afgK&v0g1zNslu5j92Z4*-gSjEajoM*Z+hs^(PIOie>g#}at; z(BMs#CgM6fs4%%j1#JfvZO7E<+3ojKF@Lt)%(=0$j80x8lPTLTpT7SezLq0vpvh;X zKmz!Js&vHB_UqINDK$^pp%JKF$>I`4sZs#J;3% zkT?6mo1TdLmXb3P9(afs~a${Q(d40KdIH3fry0kwCGJJ?t^2g+*#|gC2wQx zW0;Swt6~`CZUH#ae4x-Tork(m*@)?#(w+6g0O8yVc-LL)i`BmH`(k}{b!~ZN)w{iR zcV+$d9pB&F5_!$d`&Pywj>|XLt6rPSm}nrO{~A!P|}c zN5_wcOHwFQT=h^TUq2ZTqtV zgA$(f_j;gt2GL3+h94WLzN);O6)oik^y8Ye(ZSmf$ zrn=wWXxVaef`We>HCA~OWtgfTo}2wRubHS7$h+$6RLJoO*<}aEw=qgt7bx2Z=VMGi zGP*H%3jk!LM-sa*?R-VM5-jB0c9#*+|BnB0l8`kVp#?+;_1hT;La%hY9hD>tpv)`H!fYu1{J7}aJSqxOGZtT~tf From 1071eb3d75e84b6a160a546448baa24db6aceb29 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 18:40:39 +0530 Subject: [PATCH 32/56] Remove necessary exceptions --- spotdl.py | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/spotdl.py b/spotdl.py index 6855c57..f37c13e 100644 --- a/spotdl.py +++ b/spotdl.py @@ -25,29 +25,25 @@ def generate_songname(raw_song): return raw_song def generate_metadata(raw_song): + if misc.is_spotify(raw_song): + meta_tags = spotify.track(raw_song) + else: + meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] + artist_id = spotify.artist(meta_tags['artists'][0]['id']) + try: - if misc.is_spotify(raw_song): - meta_tags = spotify.track(raw_song) - else: - meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] - artist_id = spotify.artist(meta_tags['artists'][0]['id']) + meta_tags[u'genre'] = titlecase(artist_id['genres'][0]) + except IndexError: + meta_tags[u'genre'] = None - try: - meta_tags[u'genre'] = titlecase(artist_id['genres'][0]) - except IndexError: - meta_tags[u'genre'] = None - - meta_tags[u'release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] - meta_tags[u'copyright'] = spotify.album(meta_tags['album']['id'])['copyrights'][0]['text'] - meta_tags[u'publisher'] = spotify.album(meta_tags['album']['id'])['label'] - meta_tags[u'total_tracks'] = spotify.album(meta_tags['album']['id'])['tracks']['total'] - #import pprint - #pprint.pprint(meta_tags) - #pprint.pprint(spotify.album(meta_tags['album']['id'])) - return meta_tags - - except (urllib2.URLError, IOError): - return None + meta_tags[u'release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] + meta_tags[u'copyright'] = spotify.album(meta_tags['album']['id'])['copyrights'][0]['text'] + meta_tags[u'publisher'] = spotify.album(meta_tags['album']['id'])['label'] + meta_tags[u'total_tracks'] = spotify.album(meta_tags['album']['id'])['tracks']['total'] + #import pprint + #pprint.pprint(meta_tags) + #pprint.pprint(spotify.album(meta_tags['album']['id'])) + return meta_tags def generate_YouTube_URL(raw_song): song = generate_songname(raw_song) From 44f34555b41a9a52942be504eda1552025a893c8 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 20:53:30 +0530 Subject: [PATCH 33/56] Use urllib2.quote() to fix encoding errors --- core/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 142 bytes core/__pycache__/metadata.cpython-35.pyc | Bin 0 -> 3689 bytes core/__pycache__/misc.cpython-35.pyc | Bin 0 -> 3597 bytes core/misc.py | 8 ++++++-- spotdl.py | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 core/__pycache__/__init__.cpython-35.pyc create mode 100644 core/__pycache__/metadata.cpython-35.pyc create mode 100644 core/__pycache__/misc.cpython-35.pyc diff --git a/core/__pycache__/__init__.cpython-35.pyc b/core/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bb4315bc13fb2a8ea2fadc8a96b41ec83336fc6 GIT binary patch literal 142 zcmWgV<>h+w);W@qfq~&M5W@jTzyXMhS%5?e14FPTqu)w~B9JhG_+_o1k)NBYUy!L^ zoLrPyP*SX4P+5|ZpQj&OkYAFSR;lZfU!IqfpO}(bq@SE$l&T*epP83g5+AQuPcG+mMzip+VW-XNikk|coyP1aNrZWt>WigAEWLn(9>zu%C$07w7*>Dq3V$hACLD+Vgc%VI4b zm%CZI_9D;9XlH!yQFf5VStz7h8)QnZO$S)b$GSeqk~~zh1y99g2y&{T@ z!uHiUTUed3Z5e&d_`o1@LFig%zL^(v2X9(x?oA9$E!MROukD9jE17wz6{AHG{5B zO;4iIfb~g^eu}UUi8JcWEfXGxdXIjyNXIogayf605Zns&I2^Ao&`ZwVDsS^50%9`Q zW5XkliQ@ss7Y;ayxf7$FMeC?3vb_ zZC$XsdE2^ZTNiEX+{_le2L>y2+x$*cZ4niNiJjl# z#fl8I%hye_+0>2R| z6{bU}{AMoIPNot}s+o_rj!eXh)U_V#YlxE=paan=wDS(rx(j~SnlfDP^L=yt=IPhT%ybv-$G5=i;{3C3tfdr zzG*N#7|C~&2$xaIDA)FK2}Ht8<>ix!ZZKK4BG?W@P-^W2*h>Npgmm2 z2-e5k1ZTZ*=w~W71?WgLx zsfL(?A*p>!QpnDz*!SH*9>wY>#$=GaR;q8SxhnlyDU&?&-4fE~*1K$KSXAABxw^^X zHj58gtg-kM!fzWkVA2Z9ep9Wpf*+&mGZsH&;j#D;gx`Mht1q8D{3`gx^T%JPA2a*| z2y}a2s`LBtT)vxCoKJOrVm?7BSdi->+5i8yQ<+jh*SUhEpGTT6o5Y_-vUBUscawf2 z-0FM${Rf!bSj;h z&WgeMv;J%{!n;eRtGHRlkytn6>9?mSBTS!$EKlJi->^Gw_L;X`x1Vx6OY>iV?pm^4 yRr1wH7NxFFD&_&My=;3=4oWkH_sv%~9@w@1oc|vw<{;*nE-O`#;Eg!W2 literal 0 HcmV?d00001 diff --git a/core/__pycache__/misc.cpython-35.pyc b/core/__pycache__/misc.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ee8d8c382fe5f582b1ede7b58fe81ed9c73f2ed GIT binary patch literal 3597 zcmZuzTXPi074DhYo0gRX;_d(*+hDVfcURU05V0XdK*g1m5)clssnphJx_32bZglss z>=-?fJm+`hFU@28#4kvdUy|?ij5Z*9wB0&=`gHfXpL2e3v3=@4+s~~<#(re)eG7np zi=qCFiQ{Lnkg?$$w%E`*!rN?U8`x%{!y<o2oJ3*VwGd^W}j zjBN}B-q`CyHXAa{n1(Ja_SXCkE%pXWn;q1d{2jsCW^Wkn9#{s$DuN3acZt$r2R4(B zNk6;VVZ279*3*{`tLYj$Xfyc(>FNJl&$0Ck<0+l!)_3yrckZ~``$?jNJ4-U@##yu{ ztaEA(@_wonJ%1A#6>-ogl_-E3p4RR%0zbU zq8;M?_m8uI%J}IoJq}RK(qL!pkR!s_z7j2b>frb=9744}Ev@kGHje}4OopAtC?r9H* zB-2J~B`1l5fGfkOgu=atH};q9vqd z_My=Tgd&ORz*v=1urx7MM$=o{vfzg=fHe_C*bekE$Ce=l>~e|4{h&3GRmK#ZFagES zfx@w12ckI7_=PI!h~grJ+=mn&G5)LfC@%e+;xgk`-lO>V=M*mEpBRcyO?LPU9N0%3 zTxI;4+56n=eKFr7Z6J28?-VDvDE&1^;{3ZKA9cN$yM7YC5>mTl6XpKWH5A&-Cd&)A%041Y1KWC$)4rfKf z^EI@Ih|q}hk(Qaron9Q}i@jc)^dg}>3WNL#Puw`su0Xcp?hzVXl^O>ZW8z{wj#4qn zx4y^2CG0}Cm30&zaue_)rvdhr&~C-8a=}~}?0K(n(W`u+*Ml+lz2qjzCbm-fge{i)cC-0OczGu6cvN#5!8 z$_q!;9qD@h)%=cf%f*(64d4AJT{rj8eb?{Z+wOK7Maz?u)RRgSOZVg?i$tvdOiMYu ze6+q_+C%*wuJiE7&4!me&PNek&YbRxvSa!D=8muh`30(>fYwk*Lej0Jenaf76zq;mkaJQ&Dm- zOcMJ8fHEN?<06kyJ^YfgIi)p|(~#4#1YX@Wc$1}xxWx`?Om0&`+eMeKYx4BM&bxEC z@+Zs9ooj2WDCuP|nY8`n;k*Ch>pHRtnxZKM0>BrPrDKkw-=b1Tp?O>n?LdvHi++QX zs)31FcTt_>uK~?PBe7`Dh_(TM1TmF~ijYl}3H?G@`OC}<=HFU2iW7ATrcmv6n>+Fv z&KB0Fu#*GC!RUJTuYddD5LGyt3i%NHU&p?rYEX2>!RwKT{e%bcr0W!QjY=ejrm7pT zXi;9E06#9(sfmb%^t2cqQQX6gm2fN81xv1B?PHjao~vUR;fR4_#fQ3=dF7!hR25?C zQ-%wE7{HxJ0Ux>>W3fIK{#b0TuWzibt$SZ@++5rI`iAdsZSh;Ho5RJ5SP5SWJ#<&c zt805V{f%4e{_4is=FP2J8#lH#S66R(8{RFx=|k*B;$?b&ZFr$d$=l|+ZNx8L4ZKhd zPa0qLeWBDyCkG;?wg&fe1)b6QxO$f6>M`37A+c&>Vm5jI0{mazz-n%M(vO2-^m7FO z+r&0#?bGPN0c5O)yNoo`zMa@(b=ljQhJTAiNxd=*l(%&C}W(S8V8=th=mfXU6F2e`0 zc@;aAjnRxE)=rZ@?~+fKkqIfP=IEW_hfe49OpklFz~XJJOa}_{IBKSf4ze&MKXjYp zq-YqY6=kn2pHtnXs}DcyAH3hGA!? zV0rt&>p*KnO}`IrNtZ=U0QDXt)ZI$cqO~UynPP-8uAc*0^=NG<5Gtgw>8{AjL{0et zVXMmMTrycBo%Kuil~)9RY8&ty%LUrZbtXEW9^YA}!f sBCD-$qJ(FmxI^YppJJj$x?`QRmeHV{L34f9?ksj1ol6+?P79;)Kg%6WmH+?% literal 0 HcmV?d00001 diff --git a/core/misc.py b/core/misc.py index e698d0e..29e5aba 100644 --- a/core/misc.py +++ b/core/misc.py @@ -4,6 +4,11 @@ import os from slugify import slugify import spotipy.oauth2 as oauth2 +try: + from urllib2 import quote +except: + from urllib.request import quote + def input_link(links): while True: try: @@ -71,8 +76,7 @@ def generate_token(): return token def generate_search_URL(song): - URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + \ - song.replace(" ", "%20") + URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + quote(song) return URL def fix_encoding(query): diff --git a/spotdl.py b/spotdl.py index f37c13e..1ffe103 100644 --- a/spotdl.py +++ b/spotdl.py @@ -22,7 +22,7 @@ def generate_songname(raw_song): if misc.is_spotify(raw_song): tags = generate_metadata(raw_song) raw_song = tags['artists'][0]['name'] + ' - ' + tags['name'] - return raw_song + return misc.fix_encoding(raw_song) def generate_metadata(raw_song): if misc.is_spotify(raw_song): From 1aa7639beeeb3e5285e1c0b5a4b9d89b0f7c033e Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 20:54:48 +0530 Subject: [PATCH 34/56] Use urllib2.quote() to fix encoding errors --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9ed708d..d284bb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -/core/*.pyc +*.pyc +/core/__pycache__ /Music/ /*.txt From dd4a0c576164474d75503641ca0d1583a062032f Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 20:55:14 +0530 Subject: [PATCH 35/56] Use urllib2.quote() to fix encoding errors --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d284bb0..1159f8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.pyc -/core/__pycache__ +/core/__pycache__/ /Music/ /*.txt From 42d45db998e3343fb6536d61fd4de803ba567bb7 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 20:55:33 +0530 Subject: [PATCH 36/56] Use urllib2.quote() to fix encoding errors --- core/__pycache__/__init__.cpython-35.pyc | Bin 142 -> 0 bytes core/__pycache__/metadata.cpython-35.pyc | Bin 3689 -> 0 bytes core/__pycache__/misc.cpython-35.pyc | Bin 3597 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/__init__.cpython-35.pyc delete mode 100644 core/__pycache__/metadata.cpython-35.pyc delete mode 100644 core/__pycache__/misc.cpython-35.pyc diff --git a/core/__pycache__/__init__.cpython-35.pyc b/core/__pycache__/__init__.cpython-35.pyc deleted file mode 100644 index 9bb4315bc13fb2a8ea2fadc8a96b41ec83336fc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmWgV<>h+w);W@qfq~&M5W@jTzyXMhS%5?e14FPTqu)w~B9JhG_+_o1k)NBYUy!L^ zoLrPyP*SX4P+5|ZpQj&OkYAFSR;lZfU!IqfpO}(bq@SE$l&T*epP83g5+AQuPcG+mMzip+VW-XNikk|coyP1aNrZWt>WigAEWLn(9>zu%C$07w7*>Dq3V$hACLD+Vgc%VI4b zm%CZI_9D;9XlH!yQFf5VStz7h8)QnZO$S)b$GSeqk~~zh1y99g2y&{T@ z!uHiUTUed3Z5e&d_`o1@LFig%zL^(v2X9(x?oA9$E!MROukD9jE17wz6{AHG{5B zO;4iIfb~g^eu}UUi8JcWEfXGxdXIjyNXIogayf605Zns&I2^Ao&`ZwVDsS^50%9`Q zW5XkliQ@ss7Y;ayxf7$FMeC?3vb_ zZC$XsdE2^ZTNiEX+{_le2L>y2+x$*cZ4niNiJjl# z#fl8I%hye_+0>2R| z6{bU}{AMoIPNot}s+o_rj!eXh)U_V#YlxE=paan=wDS(rx(j~SnlfDP^L=yt=IPhT%ybv-$G5=i;{3C3tfdr zzG*N#7|C~&2$xaIDA)FK2}Ht8<>ix!ZZKK4BG?W@P-^W2*h>Npgmm2 z2-e5k1ZTZ*=w~W71?WgLx zsfL(?A*p>!QpnDz*!SH*9>wY>#$=GaR;q8SxhnlyDU&?&-4fE~*1K$KSXAABxw^^X zHj58gtg-kM!fzWkVA2Z9ep9Wpf*+&mGZsH&;j#D;gx`Mht1q8D{3`gx^T%JPA2a*| z2y}a2s`LBtT)vxCoKJOrVm?7BSdi->+5i8yQ<+jh*SUhEpGTT6o5Y_-vUBUscawf2 z-0FM${Rf!bSj;h z&WgeMv;J%{!n;eRtGHRlkytn6>9?mSBTS!$EKlJi->^Gw_L;X`x1Vx6OY>iV?pm^4 yRr1wH7NxFFD&_&My=;3=4oWkH_sv%~9@w@1oc|vw<{;*nE-O`#;Eg!W2 diff --git a/core/__pycache__/misc.cpython-35.pyc b/core/__pycache__/misc.cpython-35.pyc deleted file mode 100644 index 7ee8d8c382fe5f582b1ede7b58fe81ed9c73f2ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3597 zcmZuzTXPi074DhYo0gRX;_d(*+hDVfcURU05V0XdK*g1m5)clssnphJx_32bZglss z>=-?fJm+`hFU@28#4kvdUy|?ij5Z*9wB0&=`gHfXpL2e3v3=@4+s~~<#(re)eG7np zi=qCFiQ{Lnkg?$$w%E`*!rN?U8`x%{!y<o2oJ3*VwGd^W}j zjBN}B-q`CyHXAa{n1(Ja_SXCkE%pXWn;q1d{2jsCW^Wkn9#{s$DuN3acZt$r2R4(B zNk6;VVZ279*3*{`tLYj$Xfyc(>FNJl&$0Ck<0+l!)_3yrckZ~``$?jNJ4-U@##yu{ ztaEA(@_wonJ%1A#6>-ogl_-E3p4RR%0zbU zq8;M?_m8uI%J}IoJq}RK(qL!pkR!s_z7j2b>frb=9744}Ev@kGHje}4OopAtC?r9H* zB-2J~B`1l5fGfkOgu=atH};q9vqd z_My=Tgd&ORz*v=1urx7MM$=o{vfzg=fHe_C*bekE$Ce=l>~e|4{h&3GRmK#ZFagES zfx@w12ckI7_=PI!h~grJ+=mn&G5)LfC@%e+;xgk`-lO>V=M*mEpBRcyO?LPU9N0%3 zTxI;4+56n=eKFr7Z6J28?-VDvDE&1^;{3ZKA9cN$yM7YC5>mTl6XpKWH5A&-Cd&)A%041Y1KWC$)4rfKf z^EI@Ih|q}hk(Qaron9Q}i@jc)^dg}>3WNL#Puw`su0Xcp?hzVXl^O>ZW8z{wj#4qn zx4y^2CG0}Cm30&zaue_)rvdhr&~C-8a=}~}?0K(n(W`u+*Ml+lz2qjzCbm-fge{i)cC-0OczGu6cvN#5!8 z$_q!;9qD@h)%=cf%f*(64d4AJT{rj8eb?{Z+wOK7Maz?u)RRgSOZVg?i$tvdOiMYu ze6+q_+C%*wuJiE7&4!me&PNek&YbRxvSa!D=8muh`30(>fYwk*Lej0Jenaf76zq;mkaJQ&Dm- zOcMJ8fHEN?<06kyJ^YfgIi)p|(~#4#1YX@Wc$1}xxWx`?Om0&`+eMeKYx4BM&bxEC z@+Zs9ooj2WDCuP|nY8`n;k*Ch>pHRtnxZKM0>BrPrDKkw-=b1Tp?O>n?LdvHi++QX zs)31FcTt_>uK~?PBe7`Dh_(TM1TmF~ijYl}3H?G@`OC}<=HFU2iW7ATrcmv6n>+Fv z&KB0Fu#*GC!RUJTuYddD5LGyt3i%NHU&p?rYEX2>!RwKT{e%bcr0W!QjY=ejrm7pT zXi;9E06#9(sfmb%^t2cqQQX6gm2fN81xv1B?PHjao~vUR;fR4_#fQ3=dF7!hR25?C zQ-%wE7{HxJ0Ux>>W3fIK{#b0TuWzibt$SZ@++5rI`iAdsZSh;Ho5RJ5SP5SWJ#<&c zt805V{f%4e{_4is=FP2J8#lH#S66R(8{RFx=|k*B;$?b&ZFr$d$=l|+ZNx8L4ZKhd zPa0qLeWBDyCkG;?wg&fe1)b6QxO$f6>M`37A+c&>Vm5jI0{mazz-n%M(vO2-^m7FO z+r&0#?bGPN0c5O)yNoo`zMa@(b=ljQhJTAiNxd=*l(%&C}W(S8V8=th=mfXU6F2e`0 zc@;aAjnRxE)=rZ@?~+fKkqIfP=IEW_hfe49OpklFz~XJJOa}_{IBKSf4ze&MKXjYp zq-YqY6=kn2pHtnXs}DcyAH3hGA!? zV0rt&>p*KnO}`IrNtZ=U0QDXt)ZI$cqO~UynPP-8uAc*0^=NG<5Gtgw>8{AjL{0et zVXMmMTrycBo%Kuil~)9RY8&ty%LUrZbtXEW9^YA}!f sBCD-$qJ(FmxI^YppJJj$x?`QRmeHV{L34f9?ksj1ol6+?P79;)Kg%6WmH+?% From c9e341e2ef3aafd385d8df6043d6b0d593225a30 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 21:12:03 +0530 Subject: [PATCH 37/56] Change back to functions for consistency --- core/metadata.py | 165 +++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 84 deletions(-) diff --git a/core/metadata.py b/core/metadata.py index 0dbd199..13c3346 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -20,90 +20,87 @@ def compare(file, metadata): already_tagged = False return already_tagged -class embed(object): +def embed(music_file, meta_tags, output_ext): + if meta_tags is None: + print('Could not find meta-tags') + elif output_ext == '.m4a': + print('Fixing meta-tags') + embed_m4a(music_file, meta_tags, output_ext) + elif output_ext == '.mp3': + print('Fixing meta-tags') + embed_mp3(music_file, meta_tags, output_ext) + else: + print('Cannot embed meta-tags into given output extension') - def __init__(self, music_file, meta_tags, output_ext): +def embed_mp3(music_file, meta_tags, output_ext): + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) + audiofile = EasyID3('Music/' + music_file + output_ext) + audiofile['artist'] = artists + audiofile['albumartist'] = meta_tags['artists'][0]['name'] + audiofile['album'] = meta_tags['album']['name'] + audiofile['title'] = meta_tags['name'] + audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] + audiofile['discnumber'] = [meta_tags['disc_number'], 0] + audiofile['date'] = meta_tags['release_date'] + audiofile['originaldate'] = meta_tags['release_date'] + audiofile['media'] = meta_tags['type'] + audiofile['copyright'] = meta_tags['copyright'] + audiofile['author'] = meta_tags['artists'][0]['name'] + audiofile['lyricist'] = meta_tags['artists'][0]['name'] + audiofile['arranger'] = meta_tags['artists'][0]['name'] + audiofile['performer'] = meta_tags['artists'][0]['name'] + audiofile['encodedby'] = meta_tags['publisher'] + audiofile['isrc'] = meta_tags['external_ids']['isrc'] + audiofile['website'] = meta_tags['external_urls']['spotify'] + audiofile['length'] = str(meta_tags['duration_ms'] / 1000) + if meta_tags['genre']: + audiofile['genre'] = meta_tags['genre'] + audiofile.save(v2_version=3) + audiofile = ID3('Music/' + music_file + output_ext) + albumart = urllib2.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() + audiofile.save(v2_version=3) - if meta_tags is None: - print('Could not find meta-tags') - elif output_ext == '.m4a': - print('Fixing meta-tags') - self.embed_m4a(music_file, meta_tags, output_ext) - elif output_ext == '.mp3': - print('Fixing meta-tags') - self.embed_mp3(music_file, meta_tags, output_ext) - else: - print('Cannot embed meta-tags into given output extension') +def embed_m4a(music_file, meta_tags, output_ext): + # eyed serves only mp3 not aac so using mutagen + # Apple has specific tags - see mutagen docs - + # http://mutagen.readthedocs.io/en/latest/api/mp4.html + tags = {'album': '\xa9alb', + 'artist': '\xa9ART', + 'date': '\xa9day', + 'title': '\xa9nam', + 'originaldate': 'purd', + 'comment': '\xa9cmt', + 'group': '\xa9grp', + 'writer': '\xa9wrt', + 'genre': '\xa9gen', + 'tracknumber': 'trkn', + 'albumartist': 'aART', + 'disknumber': 'disk', + 'cpil': 'cpil', + 'albumart': 'covr', + 'copyright': 'cprt', + 'tempo': 'tmpo'} - def embed_mp3(self, music_file, meta_tags, output_ext): - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) - audiofile = EasyID3('Music/' + music_file + output_ext) - audiofile['artist'] = artists - audiofile['albumartist'] = meta_tags['artists'][0]['name'] - audiofile['album'] = meta_tags['album']['name'] - audiofile['title'] = meta_tags['name'] - audiofile['tracknumber'] = [meta_tags['track_number'], meta_tags['total_tracks']] - audiofile['discnumber'] = [meta_tags['disc_number'], 0] - audiofile['date'] = meta_tags['release_date'] - audiofile['originaldate'] = meta_tags['release_date'] - audiofile['media'] = meta_tags['type'] - audiofile['copyright'] = meta_tags['copyright'] - audiofile['author'] = meta_tags['artists'][0]['name'] - audiofile['lyricist'] = meta_tags['artists'][0]['name'] - audiofile['arranger'] = meta_tags['artists'][0]['name'] - audiofile['performer'] = meta_tags['artists'][0]['name'] - audiofile['encodedby'] = meta_tags['publisher'] - audiofile['isrc'] = meta_tags['external_ids']['isrc'] - audiofile['website'] = meta_tags['external_urls']['spotify'] - audiofile['length'] = str(meta_tags['duration_ms'] / 1000) - if meta_tags['genre']: - audiofile['genre'] = meta_tags['genre'] - audiofile.save(v2_version=3) - audiofile = ID3('Music/' + music_file + output_ext) - albumart = urllib2.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() - audiofile.save(v2_version=3) - - def embed_m4a(self, music_file, meta_tags, output_ext): - # eyed serves only mp3 not aac so using mutagen - # Apple has specific tags - see mutagen docs - - # http://mutagen.readthedocs.io/en/latest/api/mp4.html - tags = {'album': '\xa9alb', - 'artist': '\xa9ART', - 'date': '\xa9day', - 'title': '\xa9nam', - 'originaldate': 'purd', - 'comment': '\xa9cmt', - 'group': '\xa9grp', - 'writer': '\xa9wrt', - 'genre': '\xa9gen', - 'tracknumber': 'trkn', - 'albumartist': 'aART', - 'disknumber': 'disk', - 'cpil': 'cpil', - 'albumart': 'covr', - 'copyright': 'cprt', - 'tempo': 'tmpo'} - - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) - audiofile = MP4('Music/' + music_file + output_ext) - audiofile[tags['artist']] = artists - audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] - audiofile[tags['album']] = meta_tags['album']['name'] - audiofile[tags['title']] = meta_tags['name'] - audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] - audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] - audiofile[tags['date']] = meta_tags['release_date'] - audiofile[tags['originaldate']] = meta_tags['release_date'] - audiofile[tags['copyright']] = meta_tags['copyright'] - if meta_tags['genre']: - audiofile[tags['genre']] = meta_tags['genre'] - albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) - audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] - albumart.close() - audiofile.save() + artists = [] + for artist in meta_tags['artists']: + artists.append(artist['name']) + audiofile = MP4('Music/' + music_file + output_ext) + audiofile[tags['artist']] = artists + audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] + audiofile[tags['album']] = meta_tags['album']['name'] + audiofile[tags['title']] = meta_tags['name'] + audiofile[tags['tracknumber']] = [(meta_tags['track_number'], meta_tags['total_tracks'])] + audiofile[tags['disknumber']] = [(meta_tags['disc_number'], 0)] + audiofile[tags['date']] = meta_tags['release_date'] + audiofile[tags['originaldate']] = meta_tags['release_date'] + audiofile[tags['copyright']] = meta_tags['copyright'] + if meta_tags['genre']: + audiofile[tags['genre']] = meta_tags['genre'] + albumart = urllib2.urlopen(meta_tags['album']['images'][0]['url']) + audiofile[tags['albumart']] = [ MP4Cover(albumart.read(), imageformat=MP4Cover.FORMAT_JPEG) ] + albumart.close() + audiofile.save() From 303fe813be7f84a5576f4773d4920a5135ed9411 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 21:17:06 +0530 Subject: [PATCH 38/56] Improve code readability --- spotdl.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spotdl.py b/spotdl.py index 1ffe103..f21f62d 100644 --- a/spotdl.py +++ b/spotdl.py @@ -29,17 +29,19 @@ def generate_metadata(raw_song): meta_tags = spotify.track(raw_song) else: meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] - artist_id = spotify.artist(meta_tags['artists'][0]['id']) + + artist = spotify.artist(meta_tags['artists'][0]['id']) + album = spotify.album(meta_tags['album']['id']) try: meta_tags[u'genre'] = titlecase(artist_id['genres'][0]) except IndexError: meta_tags[u'genre'] = None - meta_tags[u'release_date'] = spotify.album(meta_tags['album']['id'])['release_date'] - meta_tags[u'copyright'] = spotify.album(meta_tags['album']['id'])['copyrights'][0]['text'] - meta_tags[u'publisher'] = spotify.album(meta_tags['album']['id'])['label'] - meta_tags[u'total_tracks'] = spotify.album(meta_tags['album']['id'])['tracks']['total'] + meta_tags[u'release_date'] = album['release_date'] + meta_tags[u'copyright'] = album['copyrights'][0]['text'] + meta_tags[u'publisher'] = album['label'] + meta_tags[u'total_tracks'] = album['tracks']['total'] #import pprint #pprint.pprint(meta_tags) #pprint.pprint(spotify.album(meta_tags['album']['id'])) From a2770badb6306ae6220e33708350631de6d44809 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 21:18:35 +0530 Subject: [PATCH 39/56] Fix variable names --- spotdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotdl.py b/spotdl.py index f21f62d..0f30cee 100644 --- a/spotdl.py +++ b/spotdl.py @@ -34,7 +34,7 @@ def generate_metadata(raw_song): album = spotify.album(meta_tags['album']['id']) try: - meta_tags[u'genre'] = titlecase(artist_id['genres'][0]) + meta_tags[u'genre'] = titlecase(artist['genres'][0]) except IndexError: meta_tags[u'genre'] = None From e2af7bf1f3e8bdd0b8ece0c42dd6ab3224e18ec0 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Fri, 16 Jun 2017 22:07:42 +0530 Subject: [PATCH 40/56] Small fixes --- core/metadata.py | 10 +++++++--- core/misc.py | 7 ++++++- spotdl.py | 37 ++++++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/core/metadata.py b/core/metadata.py index 13c3346..bb3f191 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -2,11 +2,13 @@ from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC from mutagen.mp4 import MP4, MP4Cover +# urllib2 is urllib.request in python3 try: import urllib2 except ImportError: import urllib.request as urllib2 +# check if input file title matches with expected title def compare(file, metadata): try: if file.endswith('.mp3'): @@ -33,11 +35,13 @@ def embed(music_file, meta_tags, output_ext): print('Cannot embed meta-tags into given output extension') def embed_mp3(music_file, meta_tags, output_ext): + #EasyID3 is fun to use ;) artists = [] for artist in meta_tags['artists']: artists.append(artist['name']) audiofile = EasyID3('Music/' + music_file + output_ext) - audiofile['artist'] = artists + #audiofile['artist'] = artists + audiofile['artist'] = ', '.join(artists) audiofile['albumartist'] = meta_tags['artists'][0]['name'] audiofile['album'] = meta_tags['album']['name'] audiofile['title'] = meta_tags['name'] @@ -65,7 +69,6 @@ def embed_mp3(music_file, meta_tags, output_ext): audiofile.save(v2_version=3) def embed_m4a(music_file, meta_tags, output_ext): - # eyed serves only mp3 not aac so using mutagen # Apple has specific tags - see mutagen docs - # http://mutagen.readthedocs.io/en/latest/api/mp4.html tags = {'album': '\xa9alb', @@ -89,7 +92,8 @@ def embed_m4a(music_file, meta_tags, output_ext): for artist in meta_tags['artists']: artists.append(artist['name']) audiofile = MP4('Music/' + music_file + output_ext) - audiofile[tags['artist']] = artists + #audiofile[tags['artist']] = artists + audiofile[tags['artist']] = ', '.join(artists) audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] diff --git a/core/misc.py b/core/misc.py index 29e5aba..bd17f46 100644 --- a/core/misc.py +++ b/core/misc.py @@ -9,6 +9,7 @@ try: except: from urllib.request import quote +# method to input (user playlists) and (track when using manual mode) def input_link(links): while True: try: @@ -22,6 +23,7 @@ def input_link(links): except ValueError: print('Choose a valid number!') +# remove first song from .txt def trim_song(file): with open(file, 'r') as fin: data = fin.read().splitlines(True) @@ -62,13 +64,15 @@ def is_spotify(raw_song): else: return False +# generate filename of the song to be downloaded def generate_filename(title): raw_title = title.replace(' ', '_') + # slugify removes any special characters filename = slugify(raw_title, ok='-_()[]{}', lower=False) return fix_encoding(filename) +# please respect this user token :) def generate_token(): - # Please respect this user token :) creds = oauth2.SpotifyClientCredentials( client_id='4fe3fecfe5334023a1472516cc99d805', client_secret='0f02b7c483c04257984695007a4a8d5c') @@ -79,6 +83,7 @@ def generate_search_URL(song): URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + quote(song) return URL +# fix encoding issues in python2 def fix_encoding(query): if sys.version_info > (3, 0): return query diff --git a/spotdl.py b/spotdl.py index 0f30cee..c3c390b 100644 --- a/spotdl.py +++ b/spotdl.py @@ -1,4 +1,5 @@ #!/usr/bin/env python + # -*- coding: UTF-8 -*- from core import metadata @@ -13,17 +14,20 @@ import sys import os import subprocess +# urllib2 is urllib.request in python3 try: import urllib2 except ImportError: import urllib.request as urllib2 +# generate song title to be searched on YouTube def generate_songname(raw_song): if misc.is_spotify(raw_song): tags = generate_metadata(raw_song) raw_song = tags['artists'][0]['name'] + ' - ' + tags['name'] return misc.fix_encoding(raw_song) +# fetch song's metadata from spotify def generate_metadata(raw_song): if misc.is_spotify(raw_song): meta_tags = spotify.track(raw_song) @@ -78,6 +82,7 @@ def generate_YouTube_URL(raw_song): full_link = "youtube.com" + result return full_link +# parse track from YouTube def go_pafy(raw_song): trackURL = generate_YouTube_URL(raw_song) if trackURL is None: @@ -85,6 +90,7 @@ def go_pafy(raw_song): else: return pafy.new(trackURL) +# title of the YouTube video def get_YouTube_title(content, number): title = content.title if number is None: @@ -92,6 +98,7 @@ def get_YouTube_title(content, number): else: return str(number) + '. ' + title +# write tracks into list file def feed_tracks(file, tracks): with open(file, 'a') as fout: for item in tracks['items']: @@ -101,6 +108,7 @@ def feed_tracks(file, tracks): except KeyError: pass +# fetch user playlists when using -u option def feed_playlist(username): playlists = spotify.user_playlists(username) links = [] @@ -113,6 +121,7 @@ def feed_playlist(username): playlist = misc.input_link(links) results = spotify.user_playlist(playlist['owner']['id'], playlist['id'], fields="tracks,next") print('') + # slugify removes any special characters file = slugify(playlist['name'], ok='-_()[]{}') + '.txt' print('Feeding ' + str(playlist['tracks']['total']) + ' tracks to ' + file) tracks = results['tracks'] @@ -132,6 +141,7 @@ def download_song(content): if link is not None: link.download(filepath='Music/' + music_file + args.input_ext) +# convert song from input_ext to output_ext def convert_song(music_file): if not args.input_ext == args.output_ext: print('Converting ' + music_file + args.input_ext + ' to ' + args.output_ext[1:]) @@ -173,7 +183,7 @@ def convert_with_FFmpeg(music_file): # libfdk_aac due to copyrights needs to be compiled by end user # on MacOS brew install ffmpeg --with-fdk-aac will do just that. Other OS? # https://trac.ffmpeg.org/wiki/Encode/AAC - # + if os.name == "nt": ffmpeg_pre = 'Scripts//ffmpeg.exe ' else: @@ -217,52 +227,56 @@ def convert_with_FFmpeg(music_file): os.remove('Music/' + music_file + args.input_ext) +# check if input song already exists in Music folder def check_exists(music_file, raw_song, islist): files = os.listdir("Music") for file in files: if file.endswith(".temp"): os.remove("Music/" + file) continue - + # check if any file with similar name is already present in Music/ if file.startswith(misc.generate_filename(music_file)): - + # check if the already downloaded song has correct metadata already_tagged = metadata.compare(file, 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/" + file) return False - + # do not prompt and skip the current song if already downloaded when using list if islist: return True + # if downloading only single song, prompt to re-download else: prompt = raw_input('Song with same name has already been downloaded. Re-download? (y/n): ').lower() - if prompt == "y": os.remove("Music/" + file) return False else: return True +# download songs from list def grab_list(file): with open(file, 'r') as listed: lines = (listed.read()).splitlines() - # Ignore blank lines in file (if any) + # ignore blank lines in file (if any) try: lines.remove('') except ValueError: pass print('Total songs in list = ' + str(len(lines)) + ' songs') print('') - # Count the number of song being downloaded + # nth input song number = 1 for raw_song in lines: try: grab_single(raw_song, number=number) + # refresh token when it expires (after 1 hour) except spotipy.oauth2.SpotifyOauthError: token = misc.generate_token() global spotify spotify = spotipy.Spotify(auth=token) grab_single(raw_song, number=number) + # detect network problems except (urllib2.URLError, TypeError, IOError): lines.append(raw_song) misc.trim_song(file) @@ -277,7 +291,7 @@ def grab_list(file): misc.trim_song(file) number += 1 -# Logic behind preparing the song to download to finishing meta-tags +# logic behind downloading some song def grab_single(raw_song, number=None): if number: islist = True @@ -298,7 +312,7 @@ def grab_single(raw_song, number=None): if __name__ == '__main__': - # Python 3 compatibility + # python 3 compatibility if sys.version_info > (3, 0): raw_input = input @@ -306,10 +320,11 @@ if __name__ == '__main__': if not os.path.exists("Music"): os.makedirs("Music") + # token is mandatory when using Spotify's API token = misc.generate_token() spotify = spotipy.Spotify(auth=token) - # Set up arguments + # set up arguments args = misc.get_arguments() if args.song: From 9236affd0ee8adf60138c1f61248d19c646f902e Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Fri, 16 Jun 2017 23:47:44 +0530 Subject: [PATCH 41/56] Embed only lead artist for compatibility purposes --- core/metadata.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/core/metadata.py b/core/metadata.py index bb3f191..1286f20 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -36,12 +36,8 @@ def embed(music_file, meta_tags, output_ext): def embed_mp3(music_file, meta_tags, output_ext): #EasyID3 is fun to use ;) - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) audiofile = EasyID3('Music/' + music_file + output_ext) - #audiofile['artist'] = artists - audiofile['artist'] = ', '.join(artists) + audiofile['artist'] = meta_tags['artists'][0]['name'] audiofile['albumartist'] = meta_tags['artists'][0]['name'] audiofile['album'] = meta_tags['album']['name'] audiofile['title'] = meta_tags['name'] @@ -88,12 +84,8 @@ def embed_m4a(music_file, meta_tags, output_ext): 'copyright': 'cprt', 'tempo': 'tmpo'} - artists = [] - for artist in meta_tags['artists']: - artists.append(artist['name']) audiofile = MP4('Music/' + music_file + output_ext) - #audiofile[tags['artist']] = artists - audiofile[tags['artist']] = ', '.join(artists) + audiofile[tags['artist']] = meta_tags['artists'][0]['name'] audiofile[tags['albumartist']] = meta_tags['artists'][0]['name'] audiofile[tags['album']] = meta_tags['album']['name'] audiofile[tags['title']] = meta_tags['name'] From c270aa836595caa866eae2e735faf57fbd11e65c Mon Sep 17 00:00:00 2001 From: Ritiek Date: Sun, 18 Jun 2017 02:16:25 +0530 Subject: [PATCH 42/56] Message on unavailable local tracks --- core/misc.py | 11 +++++++++++ spotdl.py | 14 ++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/misc.py b/core/misc.py index bd17f46..fad1376 100644 --- a/core/misc.py +++ b/core/misc.py @@ -64,6 +64,17 @@ def is_spotify(raw_song): else: return False +# write tracks into list file +def feed_tracks(file, tracks): + with open(file, 'a') as fout: + for item in tracks['items']: + track = item['track'] + try: + fout.write(track['external_urls']['spotify'] + '\n') + except KeyError: + title = track['name'] + ' by '+ track['artists'][0]['name'] + print('Skipping track ' + title + ' (local only?)') + # generate filename of the song to be downloaded def generate_filename(title): raw_title = title.replace(' ', '_') diff --git a/spotdl.py b/spotdl.py index c3c390b..6fccbe3 100644 --- a/spotdl.py +++ b/spotdl.py @@ -98,16 +98,6 @@ def get_YouTube_title(content, number): else: return str(number) + '. ' + title -# write tracks into list file -def feed_tracks(file, tracks): - with open(file, 'a') as fout: - for item in tracks['items']: - track = item['track'] - try: - fout.write(track['external_urls']['spotify'] + '\n') - except KeyError: - pass - # fetch user playlists when using -u option def feed_playlist(username): playlists = spotify.user_playlists(username) @@ -125,10 +115,10 @@ def feed_playlist(username): file = slugify(playlist['name'], ok='-_()[]{}') + '.txt' print('Feeding ' + str(playlist['tracks']['total']) + ' tracks to ' + file) tracks = results['tracks'] - feed_tracks(file, tracks) + misc.feed_tracks(file, tracks) while tracks['next']: tracks = spotify.next(tracks) - feed_tracks(file, tracks) + misc.feed_tracks(file, tracks) def download_song(content): music_file = misc.generate_filename(content.title) From 80c167e7022727e59d68657c6f9586ae484a23fd Mon Sep 17 00:00:00 2001 From: Ritiek Date: Sun, 18 Jun 2017 02:57:55 +0530 Subject: [PATCH 43/56] Add lots of comments --- core/metadata.py | 4 +++- core/misc.py | 4 +++- spotdl.py | 44 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/core/metadata.py b/core/metadata.py index 1286f20..e32d0a7 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -13,10 +13,12 @@ def compare(file, metadata): try: if file.endswith('.mp3'): audiofile = EasyID3('Music/' + file) + # fetch track title metadata already_tagged = audiofile['title'][0] == metadata['name'] elif file.endswith('.m4a'): tag = {'title': '\xa9nam'} audiofile = MP4('Music/' + file) + # fetch track title metadata already_tagged = audiofile[tags['title']] == metadata['name'] except KeyError: already_tagged = False @@ -35,7 +37,7 @@ def embed(music_file, meta_tags, output_ext): print('Cannot embed meta-tags into given output extension') def embed_mp3(music_file, meta_tags, output_ext): - #EasyID3 is fun to use ;) + # EasyID3 is fun to use ;) audiofile = EasyID3('Music/' + music_file + output_ext) audiofile['artist'] = meta_tags['artists'][0]['name'] audiofile['albumartist'] = meta_tags['artists'][0]['name'] diff --git a/core/misc.py b/core/misc.py index fad1376..b16a20c 100644 --- a/core/misc.py +++ b/core/misc.py @@ -58,6 +58,7 @@ def get_arguments(): return parser.parse_args() +# check if input song is spotify link def is_spotify(raw_song): if (len(raw_song) == 22 and raw_song.replace(" ", "%20") == raw_song) or (raw_song.find('spotify') > -1): return True @@ -91,6 +92,7 @@ def generate_token(): return token def generate_search_URL(song): + # urllib2.quote() encodes URL with special characters URL = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=" + quote(song) return URL @@ -105,4 +107,4 @@ def grace_quit(): print('') print('') print('Exitting..') - exit() + sys.exit() diff --git a/spotdl.py b/spotdl.py index 6fccbe3..68e1257 100644 --- a/spotdl.py +++ b/spotdl.py @@ -8,7 +8,6 @@ from bs4 import BeautifulSoup from titlecase import titlecase from slugify import slugify import spotipy -import spotipy.oauth2 as oauth2 import pafy import sys import os @@ -20,7 +19,7 @@ try: except ImportError: import urllib.request as urllib2 -# generate song title to be searched on YouTube +# decode spotify link to "[artist] - [song]" def generate_songname(raw_song): if misc.is_spotify(raw_song): tags = generate_metadata(raw_song) @@ -30,8 +29,10 @@ def generate_songname(raw_song): # fetch song's metadata from spotify def generate_metadata(raw_song): if misc.is_spotify(raw_song): + # fetch track information directly if it is spotify link meta_tags = spotify.track(raw_song) else: + # otherwise search on spotify and fetch information from first result meta_tags = spotify.search(raw_song, limit=1)['tracks']['items'][0] artist = spotify.artist(meta_tags['artists'][0]['id']) @@ -52,7 +53,9 @@ def generate_metadata(raw_song): return meta_tags def generate_YouTube_URL(raw_song): + # decode spotify http link to "[artist] - [song]" song = generate_songname(raw_song) + # generate direct search YouTube URL searchURL = misc.generate_search_URL(song) item = urllib2.urlopen(searchURL).read() items_parse = BeautifulSoup(item, "html.parser") @@ -62,32 +65,37 @@ def generate_YouTube_URL(raw_song): print(song) print('') print('0. Skip downloading this song') + # fetch all video links on first page on YouTube for x in items_parse.find_all('h3', {'class': 'yt-lockup-title'}): + # confirm the video result is not an advertisement if not x.find('channel') == -1 or not x.find('googleads') == -1: print(str(check) + '. ' + x.get_text()) links.append(x.find('a')['href']) check += 1 print('') + # let user select the song to download result = misc.input_link(links) if result is None: return None else: - result = items_parse.find_all( - attrs={'class': 'yt-uix-tile-link'})[0]['href'] - while not result.find('channel') == - \ - 1 or not result.find('googleads') == -1: - result = items_parse.find_all( - attrs={'class': 'yt-uix-tile-link'})[check]['href'] + # get video link of the first YouTube result + result = items_parse.find_all(attrs={'class': 'yt-uix-tile-link'})[0]['href'] + # confirm the video result is not an advertisement + # otherwise keep iterating until it is not + while not result.find('channel') == -1 or not result.find('googleads') == -1: + result = items_parse.find_all(attrs={'class': 'yt-uix-tile-link'})[check]['href'] check += 1 full_link = "youtube.com" + result return full_link # parse track from YouTube def go_pafy(raw_song): + # video link of the video to extract audio from trackURL = generate_YouTube_URL(raw_song) if trackURL is None: return None else: + # parse the YouTube video return pafy.new(trackURL) # title of the YouTube video @@ -100,22 +108,29 @@ def get_YouTube_title(content, number): # fetch user playlists when using -u option def feed_playlist(username): + # fetch all user playlists playlists = spotify.user_playlists(username) links = [] check = 1 + # iterate over user playlists for playlist in playlists['items']: print(str(check) + '. ' + misc.fix_encoding(playlist['name']) + ' (' + str(playlist['tracks']['total']) + ' tracks)') links.append(playlist) check += 1 print('') + # let user select playlist playlist = misc.input_link(links) + # fetch detailed information for playlist results = spotify.user_playlist(playlist['owner']['id'], playlist['id'], fields="tracks,next") print('') # slugify removes any special characters file = slugify(playlist['name'], ok='-_()[]{}') + '.txt' print('Feeding ' + str(playlist['tracks']['total']) + ' tracks to ' + file) tracks = results['tracks'] + # write tracks to file misc.feed_tracks(file, tracks) + # check if there are more pages + # 1 page = 50 results while tracks['next']: tracks = spotify.next(tracks) misc.feed_tracks(file, tracks) @@ -123,16 +138,19 @@ def feed_playlist(username): def download_song(content): music_file = misc.generate_filename(content.title) if args.input_ext == '.webm': + # download best available audio in .webm link = content.getbestaudio(preftype='webm') if link is not None: link.download(filepath='Music/' + music_file + args.input_ext) else: + # download best available audio in .webm link = content.getbestaudio(preftype='m4a') if link is not None: link.download(filepath='Music/' + music_file + args.input_ext) # convert song from input_ext to output_ext def convert_song(music_file): + # skip conversion if input_ext == output_ext if not args.input_ext == args.output_ext: print('Converting ' + music_file + args.input_ext + ' to ' + args.output_ext[1:]) if args.ffmpeg: @@ -141,6 +159,7 @@ def convert_song(music_file): convert_with_libav(music_file) def convert_with_libav(music_file): + # different path for windows if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe' else: @@ -260,8 +279,9 @@ def grab_list(file): for raw_song in lines: try: grab_single(raw_song, number=number) - # refresh token when it expires (after 1 hour) + # token expires after 1 hour except spotipy.oauth2.SpotifyOauthError: + # refresh token when it expires token = misc.generate_token() global spotify spotify = spotipy.Spotify(auth=token) @@ -269,7 +289,9 @@ def grab_list(file): # detect network problems except (urllib2.URLError, TypeError, IOError): lines.append(raw_song) + # remove the downloaded song from .txt misc.trim_song(file) + # and append it to the last line in .txt with open(file, 'a') as myfile: myfile.write(raw_song) print('Failed to download song. Will retry after other songs.') @@ -283,6 +305,7 @@ def grab_list(file): # logic behind downloading some song def grab_single(raw_song, number=None): + # check if song is being downloaded from list if number: islist = True else: @@ -290,7 +313,10 @@ def grab_single(raw_song, number=None): content = go_pafy(raw_song) if content is None: return + # print "[number]. [artist] - [song]" if downloading from list + # otherwise print "[artist] - [song]" print(get_YouTube_title(content, number)) + # generate file name of the song to download music_file = misc.generate_filename(content.title) if not check_exists(music_file, raw_song, islist=islist): download_song(content) From 086dc846bd89aad1db9c250a83996bfb43d31735 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Sun, 18 Jun 2017 03:27:36 +0530 Subject: [PATCH 44/56] Bug fix change tag to tags --- core/metadata.py | 2 +- spotdl.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/metadata.py b/core/metadata.py index e32d0a7..db36e81 100644 --- a/core/metadata.py +++ b/core/metadata.py @@ -16,7 +16,7 @@ def compare(file, metadata): # fetch track title metadata already_tagged = audiofile['title'][0] == metadata['name'] elif file.endswith('.m4a'): - tag = {'title': '\xa9nam'} + tags = {'title': '\xa9nam'} audiofile = MP4('Music/' + file) # fetch track title metadata already_tagged = audiofile[tags['title']] == metadata['name'] diff --git a/spotdl.py b/spotdl.py index 68e1257..4f850fe 100644 --- a/spotdl.py +++ b/spotdl.py @@ -220,17 +220,18 @@ def convert_with_FFmpeg(music_file): print('Unknown formats. Unable to convert.', args.input_ext, args.output_ext) return - #command = (ffmpeg_pre + - # '-i "Music/' + music_file + args.input_ext + '"' + - # ffmpeg_params + - # '"Music/' + music_file + args.output_ext + '"').split(' ') + command = (ffmpeg_pre + + '-i "Music/' + music_file + args.input_ext + '"' + + ffmpeg_params + + '"Music/' + music_file + args.output_ext + '"').split(' ') commandos = (ffmpeg_pre + '-i "Music/' + music_file + args.input_ext + '" ' + ffmpeg_params + '"Music/' + music_file + args.output_ext + '" ') - #print(command) - #print(commandos) + print(command) + print(commandos) + exit() os.system(commandos) #subprocess.call(command) @@ -337,6 +338,7 @@ if __name__ == '__main__': os.makedirs("Music") # token is mandatory when using Spotify's API + # https://developer.spotify.com/news-stories/2017/01/27/removing-unauthenticated-calls-to-the-web-api/ token = misc.generate_token() spotify = spotipy.Spotify(auth=token) From ab51545d64823c8fdddf33820f260fa327ac245e Mon Sep 17 00:00:00 2001 From: Ritiek Date: Sun, 18 Jun 2017 03:52:14 +0530 Subject: [PATCH 45/56] Use subprocess.call() for FFmpeg --- spotdl.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/spotdl.py b/spotdl.py index 4f850fe..aacc3c1 100644 --- a/spotdl.py +++ b/spotdl.py @@ -170,17 +170,13 @@ def convert_with_libav(music_file): else: level = '0' - #print([avconv_path, - # '-loglevel', level, - # '-i', 'Music/' + music_file + args.input_ext, - # '-ab', '192k', - # 'Music/' + music_file + args.output_ext]) + command = [avconv_path, + '-loglevel', level, + '-i', 'Music/' + music_file + args.input_ext, + '-ab', '192k', + 'Music/' + music_file + args.output_ext] - subprocess.call([avconv_path, - '-loglevel', level, - '-i', 'Music/' + music_file + args.input_ext, - '-ab', '192k', - 'Music/' + music_file + args.output_ext]) + subprocess.call(command) os.remove('Music/' + music_file + args.input_ext) @@ -204,16 +200,16 @@ def convert_with_FFmpeg(music_file): if args.input_ext == '.m4a': if args.output_ext == '.mp3': - ffmpeg_params = ' -codec:v copy -codec:a libmp3lame -q:a 2 ' + ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' elif output_ext == '.webm': - ffmpeg_params = ' -c:a libopus -vbr on -b:a 192k -vn ' + ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' else: return elif args.input_ext == '.webm': if args.output_ext == '.mp3': ffmpeg_params = ' -ab 192k -ar 44100 -vn ' elif args.output_ext == '.m4a': - ffmpeg_params = ' -cutoff 20000 -c:a libfdk_aac -b:a 256k -vn ' + ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 256k -vn ' else: return else: @@ -221,20 +217,11 @@ def convert_with_FFmpeg(music_file): return command = (ffmpeg_pre + - '-i "Music/' + music_file + args.input_ext + '"' + + '-i Music/' + music_file + args.input_ext + ' ' + ffmpeg_params + - '"Music/' + music_file + args.output_ext + '"').split(' ') - - commandos = (ffmpeg_pre + - '-i "Music/' + music_file + args.input_ext + '" ' + - ffmpeg_params + - '"Music/' + music_file + args.output_ext + '" ') - print(command) - print(commandos) - exit() - os.system(commandos) - #subprocess.call(command) + 'Music/' + music_file + args.output_ext + '').split(' ') + subprocess.call(command) os.remove('Music/' + music_file + args.input_ext) # check if input song already exists in Music folder From ccf2c818a09b323d907d9be5b6b38296d1eff4a1 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Mon, 19 Jun 2017 23:23:24 +0530 Subject: [PATCH 46/56] Small updates --- spotdl.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/spotdl.py b/spotdl.py index aacc3c1..d914d4a 100644 --- a/spotdl.py +++ b/spotdl.py @@ -157,6 +157,7 @@ def convert_song(music_file): convert_with_FFmpeg(music_file) else: convert_with_libav(music_file) + os.remove('Music/' + music_file + args.input_ext) def convert_with_libav(music_file): # different path for windows @@ -178,7 +179,6 @@ def convert_with_libav(music_file): subprocess.call(command) - os.remove('Music/' + music_file + args.input_ext) def convert_with_FFmpeg(music_file): # What are the differences and similarities between ffmpeg, libav, and avconv? @@ -190,7 +190,7 @@ def convert_with_FFmpeg(music_file): # https://trac.ffmpeg.org/wiki/Encode/AAC if os.name == "nt": - ffmpeg_pre = 'Scripts//ffmpeg.exe ' + ffmpeg_pre = 'Scripts\\ffmpeg.exe ' else: ffmpeg_pre = 'ffmpeg ' @@ -203,18 +203,11 @@ def convert_with_FFmpeg(music_file): ffmpeg_params = '-codec:v copy -codec:a libmp3lame -q:a 2 ' elif output_ext == '.webm': ffmpeg_params = '-c:a libopus -vbr on -b:a 192k -vn ' - else: - return elif args.input_ext == '.webm': if args.output_ext == '.mp3': ffmpeg_params = ' -ab 192k -ar 44100 -vn ' elif args.output_ext == '.m4a': - ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 256k -vn ' - else: - return - else: - print('Unknown formats. Unable to convert.', args.input_ext, args.output_ext) - return + ffmpeg_params = '-cutoff 20000 -c:a libfdk_aac -b:a 192k -vn ' command = (ffmpeg_pre + '-i Music/' + music_file + args.input_ext + ' ' + @@ -222,7 +215,6 @@ def convert_with_FFmpeg(music_file): 'Music/' + music_file + args.output_ext + '').split(' ') subprocess.call(command) - os.remove('Music/' + music_file + args.input_ext) # check if input song already exists in Music folder def check_exists(music_file, raw_song, islist): From 20d4d7c2d5d9d8bfbd5379a965634cea89a12ca9 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Tue, 20 Jun 2017 20:25:47 +0530 Subject: [PATCH 47/56] Change defaults to FFmpeg --- core/misc.py | 4 ++-- spotdl.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/misc.py b/core/misc.py index b16a20c..6087724 100644 --- a/core/misc.py +++ b/core/misc.py @@ -46,8 +46,8 @@ def get_arguments(): help='choose the song to download manually', action='store_true') parser.add_argument('-nm', '--no-metadata', default=False, help='do not embed metadata in songs', action='store_true') - parser.add_argument('-f', '--ffmpeg', default=False, - help='Use ffmpeg for conversion otherwise set defaults to libav', + parser.add_argument('-f', '--avconv', default=False, + help='Use avconv for conversion otherwise set defaults to ffmpeg', action='store_true') parser.add_argument('-v', '--verbose', default=False, help='show debug output', action='store_true') diff --git a/spotdl.py b/spotdl.py index d914d4a..7b7acee 100644 --- a/spotdl.py +++ b/spotdl.py @@ -153,10 +153,10 @@ def convert_song(music_file): # skip conversion if input_ext == output_ext if not args.input_ext == args.output_ext: print('Converting ' + music_file + args.input_ext + ' to ' + args.output_ext[1:]) - if args.ffmpeg: - convert_with_FFmpeg(music_file) + if args.avconv: + convert_with_avconv(music_file) else: - convert_with_libav(music_file) + convert_with_FFmpeg(music_file) os.remove('Music/' + music_file + args.input_ext) def convert_with_libav(music_file): From 86506307ff5401ff0b9a1032c515c3a7cb2b60ff Mon Sep 17 00:00:00 2001 From: Ritiek Date: Tue, 20 Jun 2017 20:51:28 +0530 Subject: [PATCH 48/56] Add instructions for getting FFmpeg up --- README.md | 19 ++++++++++--------- core/misc.py | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 07be5f8..d083699 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - Track number - Disc number - Release date + - and more.. - Works straight out of the box and does not require to generate or mess with your API keys. @@ -48,25 +49,25 @@ git clone https://github.com/Ritiek/Spotify-Downloader cd Spotify-Downloader pip install -U -r requirements.txt ``` -You'll also need to install avconv (use `--ffmpeg` option when using the script if you want `ffmpeg`): -`sudo apt-get install libav-tools` (`brew install libav` for Mac) +You'll also need to install FFmpeg for conversion (use `--avconv` if you'd like to use that instead): + +Linux: `sudo apt-get install ffmpeg` +Mac: `brew install ffmpeg --with-libass --with-opus --with-fdk-aac` ### Windows Assuming you have Python already installed.. -- Download Libav-Tools for windows: https://builds.libav.org/windows/release-gpl/libav-x86_64-w64-mingw32-11.7.7z. Copy all the contents of bin folder (of libav) to Scripts folder (in your python's installation directory). +- Download FFmpeg for windows from [here](http://ffmpeg.zeranoe.com/builds/). Copy `ffmpeg.exe` from bin folder (of FFmpeg) to Scripts folder (in your python's installation directory). -- Download the zip file of this repository and extract its contents in your python's installation folder as well. +- Download the zip file of this repository and extract its contents in your python's installation folder. -Shift+right-click on empty area and open cmd and type: +Change your current working directory to python's installation directory. Shift+right-click on empty area and open cmd and type: `"Scripts/pip.exe" install -U -r requirements.txt` - If you do not want to naviagte to your python folder from the command-line everytime you want to run the script, you can have your python 'PATH' environment variables set and then you can run the script from any directory. - - python install folder: like (C:\Program Files\Python36) - - python scripts folder: like (C:\Program Files\Python36\Scripts) ## Instructions for Downloading Songs @@ -88,8 +89,8 @@ optional arguments: -n, --no-convert skip the conversion process and meta-tags (default: False) -m, --manual choose the song to download manually (default: False) - -f, --ffmpeg Use ffmpeg instead of libav for conversion. If not set - defaults to libav (default: False) + -a, --avconv Use avconv for conversion. If not set + defaults to FFmpeg (default: False) -v, --verbose show debug output (default: False) -i INPUT_EXT, --input_ext INPUT_EXT prefered input format .m4a or .webm (Opus) (default: diff --git a/core/misc.py b/core/misc.py index 6087724..b903520 100644 --- a/core/misc.py +++ b/core/misc.py @@ -46,7 +46,7 @@ def get_arguments(): help='choose the song to download manually', action='store_true') parser.add_argument('-nm', '--no-metadata', default=False, help='do not embed metadata in songs', action='store_true') - parser.add_argument('-f', '--avconv', default=False, + parser.add_argument('-a', '--avconv', default=False, help='Use avconv for conversion otherwise set defaults to ffmpeg', action='store_true') parser.add_argument('-v', '--verbose', default=False, From 94ab975caaab7a94b860cbf90ac805849a5f0a5d Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Tue, 20 Jun 2017 20:55:22 +0530 Subject: [PATCH 49/56] Enhance formatting --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d083699..d017bd1 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,7 @@ That's how your Music library will look like! ## Reporting Issues -- **Spotify made it mandatory to use a token to fetch track information. So, if you get rate limited or face any token problems, please let me know in [#58](https://github.com/Ritiek/Spotify-Downloader/issues/58).** - -- Search for your problem in the [issues section](https://github.com/Ritiek/Spotify-Downloader/issues?utf8=%E2%9C%93&q=) before opening a new ticket. It might be already answered and save you and me some time :D. +- Search for your problem in the [issues section](https://github.com/Ritiek/Spotify-Downloader/issues?utf8=%E2%9C%93&q=) before opening a new ticket. It might be already answered and save us time :D. - Provide as much information possible when opening your ticket. @@ -53,6 +51,7 @@ pip install -U -r requirements.txt You'll also need to install FFmpeg for conversion (use `--avconv` if you'd like to use that instead): Linux: `sudo apt-get install ffmpeg` + Mac: `brew install ffmpeg --with-libass --with-opus --with-fdk-aac` ### Windows @@ -63,7 +62,7 @@ Assuming you have Python already installed.. - Download the zip file of this repository and extract its contents in your python's installation folder. -Change your current working directory to python's installation directory. Shift+right-click on empty area and open cmd and type: +- Change your current working directory to python's installation directory. Shift+right-click on empty area and open cmd and type: `"Scripts/pip.exe" install -U -r requirements.txt` From eb675c7f2af96c00909a77d614bca9d521890c25 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Tue, 20 Jun 2017 20:59:50 +0530 Subject: [PATCH 50/56] Some more metadata --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d017bd1..924b473 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - Track number - Disc number - Release date - - and more.. + - and some more.. - Works straight out of the box and does not require to generate or mess with your API keys. From 464189060f348c5c44b36eb28e884b9ac4c4c7e9 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Tue, 20 Jun 2017 22:24:20 +0530 Subject: [PATCH 51/56] Fix some encoding problems --- spotdl.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spotdl.py b/spotdl.py index 7b7acee..a478231 100644 --- a/spotdl.py +++ b/spotdl.py @@ -100,7 +100,7 @@ def go_pafy(raw_song): # title of the YouTube video def get_YouTube_title(content, number): - title = content.title + title = misc.fix_encoding(content.title) if number is None: return title else: @@ -297,7 +297,8 @@ def grab_single(raw_song, number=None): # otherwise print "[artist] - [song]" print(get_YouTube_title(content, number)) # generate file name of the song to download - music_file = misc.generate_filename(content.title) + title = misc.fix_encoding(content.title) + music_file = misc.generate_filename(title) if not check_exists(music_file, raw_song, islist=islist): download_song(content) print('') From f36b3cc3f02f3f0f52c6b527c661ab1ebba600c8 Mon Sep 17 00:00:00 2001 From: Ritiek Malhotra Date: Tue, 20 Jun 2017 22:29:51 +0530 Subject: [PATCH 52/56] Remove unnecessary `fix_encoding()` --- core/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/misc.py b/core/misc.py index b903520..5987585 100644 --- a/core/misc.py +++ b/core/misc.py @@ -81,7 +81,7 @@ def generate_filename(title): raw_title = title.replace(' ', '_') # slugify removes any special characters filename = slugify(raw_title, ok='-_()[]{}', lower=False) - return fix_encoding(filename) + return filename # please respect this user token :) def generate_token(): From 0c328079a8b90d2e203f42f14e9ea140bb313b8e Mon Sep 17 00:00:00 2001 From: Ritiek Date: Wed, 21 Jun 2017 00:34:24 +0530 Subject: [PATCH 53/56] Fix encoding problems in python2 --- .gitignore | 2 +- core/misc.py | 19 +++++++++++-------- spotdl.py | 3 +-- test/.cache/v/cache/lastfailed | 1 + test/test_sample.py | 6 ++++++ 5 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 test/.cache/v/cache/lastfailed create mode 100644 test/test_sample.py diff --git a/.gitignore b/.gitignore index 1159f8e..ffeaad5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.pyc -/core/__pycache__/ +__pycache__/ /Music/ /*.txt diff --git a/core/misc.py b/core/misc.py index 5987585..e268b3b 100644 --- a/core/misc.py +++ b/core/misc.py @@ -78,12 +78,14 @@ def feed_tracks(file, tracks): # generate filename of the song to be downloaded def generate_filename(title): - raw_title = title.replace(' ', '_') + # IMO python2 sucks dealing with unicode + title = fix_encoding(title, decode=True) + title = title.replace(' ', '_') # slugify removes any special characters - filename = slugify(raw_title, ok='-_()[]{}', lower=False) + filename = slugify(title, ok='-_()[]{}', lower=False) return filename -# please respect this user token :) +# please respect these credentials :) def generate_token(): creds = oauth2.SpotifyClientCredentials( client_id='4fe3fecfe5334023a1472516cc99d805', @@ -97,11 +99,12 @@ def generate_search_URL(song): return URL # fix encoding issues in python2 -def fix_encoding(query): - if sys.version_info > (3, 0): - return query - else: - return query.encode('utf-8') +def fix_encoding(query, decode=False): + if sys.version_info < (3, 0): + query = query.encode('utf-8') + if decode: + query = query.decode('utf-8') + return query def grace_quit(): print('') diff --git a/spotdl.py b/spotdl.py index a478231..c5fd806 100644 --- a/spotdl.py +++ b/spotdl.py @@ -297,8 +297,7 @@ def grab_single(raw_song, number=None): # otherwise print "[artist] - [song]" print(get_YouTube_title(content, number)) # generate file name of the song to download - title = misc.fix_encoding(content.title) - music_file = misc.generate_filename(title) + music_file = misc.generate_filename(content.title) if not check_exists(music_file, raw_song, islist=islist): download_song(content) print('') diff --git a/test/.cache/v/cache/lastfailed b/test/.cache/v/cache/lastfailed new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/test/.cache/v/cache/lastfailed @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/test_sample.py b/test/test_sample.py new file mode 100644 index 0000000..ff0cc08 --- /dev/null +++ b/test/test_sample.py @@ -0,0 +1,6 @@ +# content of test_sample.py +def func(x): + return x + 1 + +def test_answer(): + assert func(3) == 4 From dbae345680ed7fd2d271aa44912ab57ba71f72ce Mon Sep 17 00:00:00 2001 From: Ritiek Date: Wed, 21 Jun 2017 00:34:42 +0530 Subject: [PATCH 54/56] Fix encoding problems in python2 --- test/.cache/v/cache/lastfailed | 1 - test/test_sample.py | 6 ------ 2 files changed, 7 deletions(-) delete mode 100644 test/.cache/v/cache/lastfailed delete mode 100644 test/test_sample.py diff --git a/test/.cache/v/cache/lastfailed b/test/.cache/v/cache/lastfailed deleted file mode 100644 index 9e26dfe..0000000 --- a/test/.cache/v/cache/lastfailed +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/test/test_sample.py b/test/test_sample.py deleted file mode 100644 index ff0cc08..0000000 --- a/test/test_sample.py +++ /dev/null @@ -1,6 +0,0 @@ -# content of test_sample.py -def func(x): - return x + 1 - -def test_answer(): - assert func(3) == 4 From 1848d3a16d7af61001bdd5c97aa38c7ffbbee8d0 Mon Sep 17 00:00:00 2001 From: Ritiek Date: Wed, 21 Jun 2017 00:52:18 +0530 Subject: [PATCH 55/56] Fix encoding error when getting YT track title --- core/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/misc.py b/core/misc.py index e268b3b..ba41320 100644 --- a/core/misc.py +++ b/core/misc.py @@ -83,7 +83,7 @@ def generate_filename(title): title = title.replace(' ', '_') # slugify removes any special characters filename = slugify(title, ok='-_()[]{}', lower=False) - return filename + return fix_encoding(filename) # please respect these credentials :) def generate_token(): From 9d0e769060199799bf819463da916290bcf3930d Mon Sep 17 00:00:00 2001 From: Ritiek Date: Wed, 21 Jun 2017 01:02:21 +0530 Subject: [PATCH 56/56] Rename function --- spotdl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotdl.py b/spotdl.py index c5fd806..b9eed58 100644 --- a/spotdl.py +++ b/spotdl.py @@ -159,7 +159,7 @@ def convert_song(music_file): convert_with_FFmpeg(music_file) os.remove('Music/' + music_file + args.input_ext) -def convert_with_libav(music_file): +def convert_with_avconv(music_file): # different path for windows if os.name == 'nt': avconv_path = 'Scripts\\avconv.exe'