diff --git a/.idea/irs.iml b/.idea/irs.iml new file mode 100644 index 0000000..e98082a --- /dev/null +++ b/.idea/irs.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5bbe586 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0dc461c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..53bb332 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1482431898415 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/flexx-app/center.py b/flexx-app/center.py new file mode 100644 index 0000000..5d17a4b --- /dev/null +++ b/flexx-app/center.py @@ -0,0 +1,9 @@ +def center(lst): + length = len(lst) + center = -1 + for num in range(0, length): + if not (num % 2): + center += 1 + return lst[center] + +print (center(center([1, 2, [1, 2, 3], 4, 5]))) \ No newline at end of file diff --git a/flexx-app/chat.py b/flexx-app/chat.py new file mode 100644 index 0000000..0f260c0 --- /dev/null +++ b/flexx-app/chat.py @@ -0,0 +1,87 @@ +""" +Simple chat web app in less than 80 lines. + +This app might be running at the demo server: http://flexx1.zoof.io +""" + +from flexx import app, ui, event + + +class Relay(event.HasEvents): + """ Global object to relay messages to all participants. + """ + @event.emitter + def new_message(self, msg): + return dict(msg=msg + '
') + + +class MessageBox(ui.Label): + CSS = """ + .flx-MessageBox { + overflow-y:scroll; + background: #e8e8e8; + border: 1px solid #444; + margin: 3px; + } + """ + + +class ChatRoom(ui.Widget): + """ Despite the name, this represents one connection to the chat room.""" + + def init(self): + with ui.HBox(): + ui.Widget(flex=1) + with ui.VBox(): + self.name = ui.LineEdit(placeholder_text='your name') + self.people = ui.Label(flex=1, base_size=(250, 0)) + with ui.VBox(): + self.messages = MessageBox(flex=1) + with ui.HBox(): + self.message = ui.LineEdit(flex=1, placeholder_text='enter message') + self.ok = ui.Button(text='Send') + ui.Widget(flex=1) + + # Pipe messages send by the relay into this app + relay.connect(self._push_info, 'new_message:' + self.id) + + self._update_participants() + + def _push_info(self, *events): + if self.session.status: + for ev in events: + self.emit('new_message', ev) + + def _update_participants(self): + if not self.session.status: + relay.disconnect('new_message:' + self.id) + return # and dont't invoke a new call + proxies = app.manager.get_connections(self.__class__.__name__) + names = [p.app.name.text for p in proxies] + del proxies + text = '
%i persons in this chat:

' % len(names) + text += '
'.join([name or 'anonymous' for name in sorted(names)]) + self.people.text = text + app.call_later(3, self._update_participants) + + @event.connect('ok.mouse_down', 'message.submit') + def _send_message(self, *events): + text = self.message.text + if text: + name = self.name.text or 'anonymous' + relay.new_message('%s: %s' % (name, text)) + self.message.text = '' + + class JS: + + @event.connect('new_message') + def _update_total_text(self, *events): + self.messages.text += ''.join([ev.msg for ev in events]) + + +# Create global relay +relay = Relay() + +if __name__ == '__main__': + m = app.launch(ChatRoom) # for use during development + app.run() diff --git a/flexx-app/circles.py b/flexx-app/circles.py new file mode 100644 index 0000000..2a3daef --- /dev/null +++ b/flexx-app/circles.py @@ -0,0 +1,51 @@ +""" +Example that shows animated circles. The animation is run from Python. +Doing that in JS would be more efficient, but we have not implemented timers +yet. +""" + +import math + +from flexx import app, ui + + +class Circle(ui.Label): + CSS = """ + .flx-Circle { + background: #f00; + border-radius: 10px; + width: 10px; + height: 10px; + } + """ + +class Circles(ui.Widget): + + def init(self): + self._circles = [] + + with ui.PinboardLayout(): + for i in range(32): + x = math.sin(i*0.2)*0.3 + 0.5 + y = math.cos(i*0.2)*0.3 + 0.5 + w = Circle(pos=(x, y)) + self._circles.append(w) + + self.tick() + # todo: animate in JS! + + def tick(self): + if not self.session.status: + return + import time + t = time.time() + for i, circle in enumerate(self._circles): + x = math.sin(i*0.2 + t)*0.3 + 0.5 + y = math.cos(i*0.2 + t)*0.3 + 0.5 + circle.pos = x, y + app.call_later(0.03, self.tick) + + +if __name__ == '__main__': + m = app.launch(Circles) + app.run() diff --git a/flexx-app/form.py b/flexx-app/form.py new file mode 100644 index 0000000..bfb3dc7 --- /dev/null +++ b/flexx-app/form.py @@ -0,0 +1,28 @@ +""" +Simple example that shows two forms, one which is stretched, and one +in which we use a dummy Widget to fill up space so that the form is +more compact. +""" + +from flexx import app, ui + + +class Form(ui.Widget): + + def init(self): + + with ui.BoxPanel(): + with ui.FormLayout() as self.form: + self.b1 = ui.Button(title='Name:', text='Hola') + self.b2 = ui.Button(title='Age:', text='Hello world') + self.b3 = ui.Button(title='Favorite color:', text='Foo bar') + with ui.FormLayout() as self.form: + self.b4 = ui.Button(title='Name:', text='Hola') + self.b5 = ui.Button(title='Age:', text='Hello world') + self.b6 = ui.Button(title='Favorite color:', text='Foo bar') + ui.Widget(flex=1) # Add a flexer + + +if __name__ == '__main__': + m = app.launch(Form) + app.run() diff --git a/flexx-app/irs-app.py b/flexx-app/irs-app.py new file mode 100644 index 0000000..4c5285a --- /dev/null +++ b/flexx-app/irs-app.py @@ -0,0 +1,22 @@ +from flexx import app, ui, event +import os + +class IRS(ui.Widget): + + def init(self): + + with ui.FormLayout() as self.form: + self.song = ui.LineEdit(placeholder_text="Song Name") + self.artist = ui.LineEdit(placeholder_text="Artist Name") + self.submit = ui.Button(text="Submit") + self.output = ui.Label(text="") + ui.Widget(flex=2) + + """@event.connect("submit.mouse_click", "artist.submit") + def _button_clicked(self, *events): + self.output.text = os.system('irs -a "%s" -s "%s"' % (self.artist.text, self.song.text)) +""" + +if __name__ == '__main__': + m = app.launch(IRS) + app.run() diff --git a/irs/__main__.py b/irs/__main__.py index f1101a8..c64613d 100644 --- a/irs/__main__.py +++ b/irs/__main__.py @@ -1,5 +1,4 @@ #!/usr/bin python - HELP = \ """ usage: @@ -22,67 +21,22 @@ Options: -s SONG, --song SONG Specify song name of the artist. -A ALBUM, --album ALBUM Specify album name of the artist. - -st SEARCH_TERMS, --search-terms SEARCH_TERMS - Only use if calling -A/--album. Acts as extra search - terms when looking for the album. -l, --choose-link If supplied, will bring up a console choice for what link you want to download based off a list of titles. -ng, --no-organize Only use if calling -p/--playlist. Forces all files downloaded to be organized normally. """ -import argparse -from os import system +# For exiting from sys import exit -from .manage import * + +# Parsing args +import argparse + +# Import the manager +from .manager import Manager from .utils import * -def console(args): - system("clear") - media = None - while type(media) is not int: - print (bc.HEADER) - print ("What type of media would you like to download?") - print ("\t1) Song") - print ("\t2) Album") - print ("\t3) Playlist") - try: - media = int(input(bc.YELLOW + bc.BOLD + ":: " + bc.ENDC)) - if media not in (1, 2, 3): - raise ValueError - - except ValueError: - print (bc.FAIL + "\nPlease enter a valid number." + bc.ENDC) - - if media in (1, 2): - print (bc.HEADER + "Artist of song/album ", end="") - artist = input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC) - - if media == 1: - print (bc.HEADER + "Song you would like to download ", end="") - song = input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC) - rip_mp3(song, artist, command=args.command, choose_link=args.link) - - elif media == 2: - print (bc.HEADER + "Album you would like to download ", end="") - album = input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC) - rip_album(album, artist, command=args.command, choose_link=args.link) - - elif media == 3: - print (bc.HEADER + "Playlist file name ", end="") - playlist = input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC) - - organize = "" - while organize not in ("y", "n", "yes", "no", ""): - print (bc.HEADER + "Would you like to place all songs into a single folder? (Y/n)", end="") - organize = input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC).lower() - - if organize in ("y", "yes", ""): - rip_playlist(playlist, command=args.command, choose_link=args.link, \ - no_organize=True) - elif organize in ("n", "no"): - rip_playlist(playlist, command=args.command, choose_link=args.link) - def main(): parser = argparse.ArgumentParser(add_help=False) parser.add_argument('-h', '--help', action='store_true', dest='help') @@ -95,15 +49,13 @@ def main(): parser.add_argument('-p', '--playlist', dest="playlist", \ help="Specify playlist filename. Each line should be formatted like so: SONGNAME - ARTIST") - parser.add_argument('-ng', '--no-organize', action="store_false", dest="no_organize", \ + parser.add_argument('-ng', '--no-organize', action="store_false", dest="organize", \ help="Only use if calling -p/--playlist. Forces all files downloaded to be organizes normally.") media = parser.add_mutually_exclusive_group() media.add_argument('-s', '--song', dest="song", help="Specify song name of the artist.") media.add_argument('-A', '--album', dest="album", help="Specify album name of the artist.") - parser.add_argument('-st', '--search-terms', dest="search_terms", \ - help="Only use if calling -A/--album. Acts as extra search terms for the album.") parser.add_argument('-o', '--order-files', action='store_true', dest="order_files",\ help="Only use if callign with -p/--playlist or -A/--album. Adds a digit to front of each file specifying order.") @@ -111,9 +63,15 @@ def main(): args = parser.parse_args() + if args.organize == None: + args.organize = True + + manager = Manager(args) + if args.help: global HELP print (HELP) + exit(1) elif args.version: import pkg_resources @@ -121,32 +79,31 @@ def main(): print ("Homepage: " + color("https://github.com/kepoorhampond/irs", ["OKGREEN"])) print ("License: " + color("The GNU", ["YELLOW"]) + " (http://www.gnu.org/licenses/gpl.html)") print ("Version: " + pkg_resources.get_distribution("irs").version) - print ("\n") exit(0) - elif not args.album and args.search_terms: - parser.error("error: must supply -A/--album if you are going to supply -st/--search-terms") + elif not args.organize and not args.playlist: + parser.error("error: must supply -p/--playlist if specifying -ng/--no-organize") exit(1) elif args.artist and not (args.album or args.song): - print ("error: must specify -A/--album or -s/--song if specifying -a/--artist") + parser.error("error: must supply -A/--album or -s/--song if specifying -a/--artist") exit(1) + + elif not args.artist and not args.playlist: - console(args) + manager.console() elif args.playlist: - rip_playlist(args.playlist, args.command, choose_link=args.link, no_organize=args.no_organize) + manager.rip_playlist() elif args.artist: if args.album: - rip_album(args.album, args.artist, command=args.command, \ - search=args.search_terms, choose_link=args.link) + manager.rip_album() elif args.song: - rip_mp3(args.song, args.artist, command=args.command, choose_link=args.link) - + manager.rip_mp3() if __name__ == "__main__": diff --git a/irs/manage.py b/irs/manage.py deleted file mode 100644 index dd9c83f..0000000 --- a/irs/manage.py +++ /dev/null @@ -1,265 +0,0 @@ -# Powered by: -import youtube_dl - -# Info getting -from urllib.request import urlopen -from urllib.parse import urlencode - -# Info parsing -from re import findall -import os, json -from bs4 import BeautifulSoup - -# Local utils -from .utils import * -from .metadata import * - -def find_mp3(song, artist, - choose_link=False, # Whether to allow the user to choose the link. - ): - - os.system("clear") - print (color(song, ["BOLD", "UNDERLINE"]) + ' by ' + color(artist, ["BOLD", "UNDERLINE"])) - - search_terms = song + " " + artist + " lyrics" - query_string = urlencode({"search_query" : (search_terms)}) - - html_content = urlopen("http://www.youtube.com/results?" + query_string) - search_results = findall(r'href=\"\/watch\?v=(.{11})', html_content.read().decode()) - - in_title = False - i = -1 - given_up_score = 0 - - if not choose_link: - print (bc.YELLOW + "\nFinding youtube link ...", end="\r") - while in_title == False: - i += 1 - given_up_score += 1 - - if given_up_score >= 10: - in_title = True - - audio_url = ("http://www.youtube.com/watch?v=" + search_results[i]) - title = strip_special_chars((BeautifulSoup(urlopen(audio_url), 'html.parser')).title.string.lower()) - song_title = song.lower().split("/") - - for song in song_title: - song = strip_special_chars(song) - if song in title and "full album" not in title: - in_title = True - - print (bc.OKGREEN + "Found youtube link! \n" + bc.ENDC) - else: - results = [] - - print (bc.YELLOW + "Finding links ... " + bc.ENDC, end="\r") - - for key in search_results[:10]: - results.append(BeautifulSoup(urlopen(("http://www.youtube.com/watch?v="\ - + key)), 'html.parser').title.string.replace(" - YouTube" , "")) - - valid_choice = False - while valid_choice == False: - print (bc.HEADER + "What song would you like to download?") - index = 0 - for result in results: - index += 1 - print (" %s) %s" % (index, result)) - i = int(input(bc.YELLOW + bc.BOLD + ":: " + bc.ENDC)) - if i in tuple(range(1, 11)): - i -= 1 - valid_choice = True - - return search_results[i] - -def rip_playlist(file_name, - command=None, # Whether to run a special user-supplied command. - choose_link=False, # Whether to allow the user to choose the link. - no_organize=True, # Whether to organize the file downloaded. - ): - - try: - file = open(file_name, 'r') - except Exception: - print (file_name + bc.FAIL + " could not be found." + bc.ENDC) - exit(1) - - errors = [] - - song_number = 0 - - for line in file: - if line.strip() == "": - pass - - try: - arr = line.strip("\n").split(" - ") - song = arr[0] - artist = arr[1] - - if os.path.isdir(artist): - remove = False - else: - remove = True - - location = rip_mp3(song, artist, command=command) - - song_number += 1 - - locations = location.split("/") - - # Enter... the reorganizing... - if no_organize: - - folder_name = ("playlist - " + file_name)[:40] - - if not os.path.isdir(folder_name): - os.makedirs(folder_name) - - os.rename(location, "%s/%s - %s" % (folder_name, song_number, locations[-1])) - - if remove: - import shutil # Only import this if I have to. - shutil.rmtree(locations[0]) - - if os.path.isfile(filename): - os.rename(filename, folder_name + "/" + filename) - - os.rename(folder_name, folder_name.replace("playlist")) - - except Exception as e: - errors.append(line + color(" : ", ["YELLOW"]) + bc.FAIL + str(e) + bc.ENDC) - - if len(errors) > 0: - print (bc.FAIL + "Something was wrong with the formatting of the following lines:" + bc.ENDC) - - for i in errors: - print ("\t%s" % i) - - -def rip_album(album, artist, - tried=False, # for if it can't find the album the first time - search="album", # ditto - command=None, # For running a command with the song's location - choose_link=False # Whether to allow the user to choose the link. - ): - - if search in (None, False): - search = "album" - - visible_texts = search_google(album, artist, search) - errors = [] - try: - songs = [] - num = True - - for i, j in enumerate(visible_texts): - if 'Songs' in j: - if visible_texts[i + 1] == "1": - indexed = i - - while num == True: - try: - - if type(int(visible_texts[indexed])) is int: - a = visible_texts[indexed + 1] - songs.append(a) - indexed += 1 - - except: - indexed += 1 - if indexed >= 1000: - num = False - else: - pass - - print ("") - print (bc.HEADER + "Album Contents:" + bc.ENDC) - for i, j in enumerate(songs): - print (bc.OKBLUE + " - " + j + bc.ENDC) - - print (bc.YELLOW + "\nFinding album cover ... " + bc.ENDC, end="\r") - album_art_url = get_albumart_url(album, artist) - print (bc.OKGREEN + "Album cover found: " + bc.ENDC + album_art_url) - - for i, j in enumerate(songs): - song = j - print (color("\n%s/%s - " % (i + 1, len(songs)), ["UNDERLINE"]), end="") - rip_mp3(j, artist, part_of_album=True, album=album, tracknum=i + 1, \ - album_art_url=album_art_url, command=command, choose_link=choose_link) - - if len(errors) > 0: - for error in errors: print (error) - else: - print (bc.BOLD + bc.UNDERLINE + album + bc.ENDC + bc.OKGREEN + " downloaded successfully!\n") - - except Exception as e: - if str(e) == "local variable 'indexed' referenced before assignment" or str(e) == 'list index out of range': - if tried != True: - print (bc.OKBLUE + "Trying to find album ..." + bc.ENDC) - rip_album(album, artist, tried=True, search="", choose_link=choose_link) - else: - print (bc.FAIL + 'Could not find album "%s"' % album + bc.ENDC) - else: - errors.append(bc.FAIL + "There was a problem with downloading: " + bc.ENDC + song + "\n" + str(e)) - pass - - -def rip_mp3(song, artist, - part_of_album=False, # neccessary for creating folders. - album=None, # if you want to specify an album and save a bit of time. - tracknum=None, # to specify the tracknumber in the album. - album_art_url=None, # if you want to save a lot of time trying to find album cover. - command=None, # For running a command with the song's location. - choose_link=False, # Whether to allow the user to choose the link. - ): - - audio_code = find_mp3(song, artist) - - filename = strip_special_chars(song) + ".mp3" - - ydl_opts = { - 'format': 'bestaudio/best', - #'quiet': True, - 'postprocessors': [{ - 'key': 'FFmpegExtractAudio', - 'preferredcodec': 'mp3', - }], - } - - with youtube_dl.YoutubeDL(ydl_opts) as ydl: - ydl.download(["http://www.youtube.com/watch?v=" + audio_code]) - - - artist_folder = artist - - if not os.path.isdir(artist_folder): - os.makedirs(artist_folder) - - if not part_of_album: - location = artist_folder - - if album and part_of_album: - album_folder = artist + "/" + album - if not os.path.isdir(album_folder): - os.makedirs(album_folder) - location = album_folder - - - for file in os.listdir("."): - if audio_code in file: - os.rename(file, location + "/" + filename) - - - parse_metadata(song, artist, location, filename, tracknum=tracknum, album=album, album_art_url=album_art_url) - - - print (color(song, ["BOLD", "UNDERLINE"]) + bc.OKGREEN + ' downloaded successfully!'+ bc.ENDC) - print ("") - - if command: - loc = location + "/" + filename - os.system((command.replace("%(loc)s", '"%s"' % loc) + " &")) - - return (location + "/" + filename) diff --git a/irs/manager.py b/irs/manager.py new file mode 100644 index 0000000..a9a5f91 --- /dev/null +++ b/irs/manager.py @@ -0,0 +1,312 @@ +# Powered by: +import youtube_dl +import spotipy + +# Info getting +from urllib.request import urlopen +from urllib.parse import urlencode + +# Info parsing +from re import findall +import os, json +from bs4 import BeautifulSoup + +# Local utils +from .utils import * +from .metadata import * + +class Manager: + def __init__(self, args): + self.args = args + + def console(self): + + args = self.args + + os.system("clear") + media = None + + while type(media) is not int: + print (bc.HEADER) + print ("What type of media would you like to download?") + print ("\t1) Song") + print ("\t2) Album") + print ("\t3) Playlist") + try: + media = int(input(bc.YELLOW + bc.BOLD + ":: " + bc.ENDC)) + if media not in (1, 2, 3): + raise ValueError + + except ValueError: + print (bc.FAIL + "\nPlease enter a valid number." + bc.ENDC) + + if media in (1, 2): + self.args.artist = color_input("Artist of song/album") + + if media == 1: + self.args.song = color_input("Song you would like to download") + self.rip_mp3() + + elif media == 2: + self.args.album = color_input("Album you would like to download") + self.rip_album() + + elif media == 3: + self.args.playlist = color_input("Playlist file name") + + organize = "" + while organize not in ("y", "n", "yes", "no", ""): + print (bc.HEADER + "Would you like to place all songs into a single folder? (Y/n)", end="") + organize = input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC).lower() + + if organize in ("y", "yes", ""): + self.args.organize = True + elif organize in ("n", "no"): + self.args.organize = False + + self.rip_playlist() + + def find_mp3(self, song=None, artist=None): + if not song: + song = self.args.song + + if not artist: + artist = self.args.artist + + os.system("clear") + print (color(song, ["BOLD", "UNDERLINE"]) + ' by ' + color(artist, ["BOLD", "UNDERLINE"])) + + search_terms = song + " " + artist + " lyrics" + query_string = urlencode({"search_query" : (search_terms)}) + + html_content = urlopen("http://www.youtube.com/results?" + query_string) + search_results = findall(r'href=\"\/watch\?v=(.{11})', html_content.read().decode()) + + in_title = False + i = -1 + given_up_score = 0 + + if not self.args.link: + print (bc.YELLOW + "\nFinding youtube link ...", end="\r") + while in_title == False: + i += 1 + given_up_score += 1 + + if given_up_score >= 10: + in_title = True + + audio_url = ("http://www.youtube.com/watch?v=" + search_results[i]) + title = strip_special_chars((BeautifulSoup(urlopen(audio_url), 'html.parser')).title.string.lower()) + song_title = song.lower().split("/") + + for song in song_title: + song = strip_special_chars(song) + if song in title and "full album" not in title: + in_title = True + + print (bc.OKGREEN + "Found youtube link! \n" + bc.ENDC) + else: + results = [] + + print (bc.YELLOW + "\nFinding links ... " + bc.ENDC, end="\r") + + for key in search_results[:10]: + results.append(BeautifulSoup(urlopen(("http://www.youtube.com/watch?v="\ + + key)), 'html.parser').title.string.replace(" - YouTube" , "")) + + valid_choice = False + while valid_choice == False: + print (bc.HEADER + "What song would you like to download?") + index = 0 + for result in results: + index += 1 + print (" %s) %s" % (index, result)) + i = int(input(bc.YELLOW + bc.BOLD + ":: " + bc.ENDC)) + if i in tuple(range(1, 11)): + i -= 1 + valid_choice = True + + return search_results[i] + + + def rip_playlist(self): + file_name = self.args.playlist + organize = self.args.organize + + try: + file = open(file_name, 'r') + except Exception: + print (file_name + bc.FAIL + " could not be found." + bc.ENDC) + exit(1) + + errors = [] + song_number = 0 + + for line in file: + if line.strip() == "": + pass + + #try: + arr = line.strip("\n").split(" - ") + self.args.song = arr[0] + self.args.artist = arr[1] + + if os.path.isdir(self.args.artist): + remove = False + else: + remove = True + + location = self.rip_mp3() + locations = location.split("/") + song_number += 1 + + # Enter... the reorganizing... + if organize: + + folder_name = ("playlist - " + file_name)[:40] + + if not os.path.isdir(folder_name): + os.makedirs(folder_name) + + os.rename(location, "%s/%s - %s" % (folder_name, song_number, locations[-1])) + + if remove: + import shutil # Only import this if I have to. + shutil.rmtree(locations[0]) + + if organize: + os.rename(file_name, folder_name + "/" + file_name) + + os.rename(folder_name, folder_name.replace("playlist - ", "")) + + #except Exception as e: + # errors.append(line + color(" : ", ["YELLOW"]) + bc.FAIL + str(e) + bc.ENDC) + + if len(errors) > 0: + print (bc.FAIL + "Something was wrong with the formatting of the following lines:" + bc.ENDC) + + for i in errors: + print ("\t%s" % i) + + def get_album_contents(self, search): + spotify = spotipy.Spotify() + + results = spotify.search(q=search, type='album') + items = results['albums']['items'] + if len(items) > 0: + album = items[0] + album_id = (album['uri']) + contents = spotify.album_tracks(album_id)["items"] + contents = contents[0:-1] + names = [] + for song in contents: + names.append(song["name"]) + return names + + def get_album_art(self, artist, album): + spotify = spotipy.Spotify() + + results = spotify.search(q="album:" + album, type='album') + items = results['albums']['items'] + if len(items) > 0: + album = items[0]['images'][0]['url'] + return album + + + def rip_album(self): + search = self.args.artist + " " + self.args.album + songs = self.get_album_contents(search) + + print ("") + print (bc.HEADER + "Album Contents:" + bc.ENDC) + for song in songs: + print (bc.OKBLUE + " - " + song + bc.ENDC) + + print (bc.YELLOW + "\nFinding album cover ... " + bc.ENDC, end="\r") + album_art_url = self.get_album_art(self.args.artist, self.args.album) + print (bc.OKGREEN + "Album cover found: " + bc.ENDC + album_art_url) + + for track_number, song in enumerate(songs): + print (color("\n%s/%s - " % (track_number + 1, len(songs)), ["UNDERLINE"]), end="") + self.rip_mp3(song, album=self.args.album, tracknum=track_number + 1, album_art_url=album_art_url) + + else: + print (bc.BOLD + bc.UNDERLINE + self.args.album + bc.ENDC + bc.OKGREEN + " downloaded successfully!\n") + + + def rip_mp3(self, song=None, artist=None, + album=None, # if you want to specify an album and save a bit of time. + tracknum=None, # to specify the tracknumber in the album. + album_art_url=None, # if you want to save a lot of time trying to find album cover. + ): + + if not song: + song = self.args.song + + if not artist: + artist = self.args.artist + + audio_code = self.find_mp3(song=song, artist=artist) + + filename = strip_special_chars(song) + ".mp3" + + ydl_opts = { + 'format': 'bestaudio/best', + #'quiet': True, + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + }], + } + + with youtube_dl.YoutubeDL(ydl_opts) as ydl: + ydl.download(["http://www.youtube.com/watch?v=" + audio_code]) + + + artist_folder = artist + + if not os.path.isdir(artist_folder): + os.makedirs(artist_folder) + + if album: + album_folder = artist + "/" + album + if not os.path.isdir(album_folder): + os.makedirs(album_folder) + location = album_folder + + elif not album: + location = artist_folder + + for file in os.listdir("."): + if audio_code in file: + os.rename(file, location + "/" + filename) + + + # Metadata + mp3 = Metadata(location + "/" + filename, song, artist) + + mp3.add_title() + exclaim_good("Title added: ", song) + + mp3.add_artist() + exclaim_good("Artist added: ", artist) + + test_goodness(mp3.add_album(album), "Album", "album", mp3) + + test_goodness(mp3.add_release_date(), "Release Date", "date", mp3) + + if tracknum: + mp3.add_track_number(tracknum) + + image_url = mp3.add_album_art(self.get_album_art(artist, mp3.get_attr('album'))) + exclaim_good("Album art added: ", image_url) + + + print (color(song, ["BOLD", "UNDERLINE"]) + bc.OKGREEN + ' downloaded successfully!'+ bc.ENDC) + print ("") + + if self.args.command: + loc = location + "/" + filename + os.system((self.args.command.replace("%(loc)s", '"%s"' % loc) + " &")) + + return (location + "/" + filename) diff --git a/irs/metadata.py b/irs/metadata.py index 3dda37e..b6640be 100644 --- a/irs/metadata.py +++ b/irs/metadata.py @@ -3,7 +3,7 @@ from mutagen.mp3 import MP3, EasyMP3 from mutagen.easyid3 import EasyID3 from mutagen.id3 import ID3, APIC -# Info getting +# Info finding from urllib.parse import quote_plus, quote from urllib.request import urlopen, Request @@ -15,152 +15,115 @@ from bs4 import BeautifulSoup # Local utils from .utils import * -def search_google(song, artist, search_terms=""): +# Powered by... +import spotipy - def visible(element): - if element.parent.name in ['style', 'script', '[document]', 'head', 'title']: - return False - elif match('', str(element)): - return False - return True +class Metadata: + def __init__(self, location, song, artist): + self.spotify = spotipy.Spotify() - string = "%s %s %s" % (song, artist, search_terms) - filename = 'http://www.google.com/search?q=' + quote_plus(string) - hdr = { - 'User-Agent':'Mozilla/5.0', - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - } + self.song = song + self.artist = artist + self.location = location - texts = BeautifulSoup(urlopen(Request(filename, \ - headers=hdr)).read(), 'html.parser').findAll(text=True) + self.info = self.search_google() + self.mp3 = MP3(self.location, ID3=EasyID3) - return list(filter(visible, texts)) - -def parse_metadata(song, artist, location, filename, - tracknum="", - album="", - album_art_url="" - ): - googled = search_google(song, artist) - mp3file = MP3("%s/%s" % (location, filename), ID3=EasyID3) - - # Song title - mp3file['title'] = song - mp3file.save() - - print("") - print (bc.OKGREEN + "Title parsed: " + bc.ENDC + mp3file['title'][0]) - - # Artist - mp3file['artist'] = artist - mp3file.save() - - print (bc.OKGREEN + "Artist parsed: " + bc.ENDC + mp3file['artist'][0]) - - - # Album - try: - if not album: - for i, j in enumerate(googled): - if "Album:" in j: - album = (googled[i + 1]) - except Exception as e: - album = None - - if album: - mp3file['album'] = album - print (bc.OKGREEN + "Album parsed: " + bc.ENDC + mp3file['album'][0]) - else: - print (bc.FAIL + "Album not parsed.") - - mp3file.save() - - - # Release date - for i, j in enumerate(googled): - if "Released:" in j: - date = (googled[i + 1]) - - try: - mp3file['date'] = date - print (bc.OKGREEN + "Release date parsed: " + bc.ENDC + mp3file['date'][0]) - except Exception: - mp3file['date'] = "" - pass - - mp3file.save() - - - # Track number - if tracknum: - mp3file['tracknumber'] = str(tracknum) - mp3file.save() - - - # Album art - try: - if album: - if not album_art_url: - print (bc.YELLOW + "Parsing album art ..." + bc.ENDC, end="\r") - temp_url = get_albumart_url(album, artist) - embed_mp3(temp_url, location + "/" + filename) - print (bc.OKGREEN + "Album art parsed: " + bc.ENDC + temp_url) - - else: # If part of an album, it should do this. - embed_mp3(album_art_url, location + "/" + filename) - print (bc.OKGREEN + "Album art parsed." + bc.ENDC) - - - except Exception as e: - print (bc.FAIL + "Album art not parsed: " + bc.ENDC + str(e)) - -def embed_mp3(albumart_url, song_location): - image = urlopen(albumart_url) - audio = EasyMP3(song_location, ID3=ID3) - - try: - audio.add_tags() - except Exception as e: - pass - - audio.tags.add( - APIC( - encoding = 3, - mime = 'image/png', - type = 3, - desc = 'Cover', - data = image.read() - ) - ) - audio.save() - -def get_albumart_url(album, artist): - def test_404(url): + def get_attr(self, attr): try: - urlopen(albumart).read() + return self.mp3[attr][0] except Exception: return False + + def add_title(self): + self.mp3['title'] = self.song + self.mp3.save() return True - tries = 0 - album = "%s %s Album Art" % (artist, album) - url = ("https://www.google.com/search?q=" + quote(album.encode('utf-8')) + "&source=lnms&tbm=isch") - header = { - 'User-Agent': - ''' - Mozilla/5.0 (Windows NT 6.1; WOW64) - AppleWebKit/537.36 (KHTML,like Gecko) - Chrome/43.0.2357.134 Safari/537.36 - ''' - } - soup = BeautifulSoup(urlopen(Request(url, headers=header)), "html.parser") + def add_artist(self): + self.mp3['artist'] = self.artist + self.mp3.save() + return True - albumart_divs = soup.findAll("div", {"class": "rg_meta"}) - albumart = json.loads(albumart_divs[tries].text)["ou"] - while not test_404(albumart): - tries += 1 - albumart = json.loads(albumart_divs[tries].text)["ou"] + def add_album(self, album=None): + try: + if not album: + for i, j in enumerate(self.info): + if "Album:" in j: + album = (self.info[i + 1]) - return albumart + self.mp3['album'] = album + self.mp3.save() + return True + except Exception: + self.mp3['album'] = self.song + self.mp3.save() + return False + + + def add_release_date(self, release_date=None): + try: + if not release_date: + for i, j in enumerate(self.info): + if "Released:" in j: + date = (self.info[i + 1]) + + self.mp3['date'] = date + self.mp3.save() + return release_date + except UnboundLocalError: + return False + + + def add_track_number(self, track_number): + self.mp3['tracknumber'] = str(track_number) + self.mp3.save() + return True + + + def add_album_art(self, image_url): + mp3 = EasyMP3(self.location, ID3=ID3) + + try: + mp3.add_tags() + except Exception as e: + pass + + if not image_url: + image_url = self.get_albumart_url(album) + + mp3.tags.add( + APIC( + encoding = 3, + mime = 'image/png', + type = 3, + desc = 'cover', + data = urlopen(image_url).read() + ) + ) + mp3.save() + return image_url + + + def search_google(self, search_terms=""): + def visible(element): + if element.parent.name in ['style', 'script', '[document]', 'head', 'title']: + return False + elif match('', str(element)): + return False + return True + + search_terms = "%s %s %s" % (self.song, self.artist, search_terms) + url = 'http://www.google.com/search?q=' + quote_plus(search_terms) + + hdr = { + 'User-Agent':'Mozilla/5.0', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + } + + texts = BeautifulSoup(urlopen(Request(url, \ + headers=hdr)).read(), 'html.parser').findAll(text=True) + + return list(filter(visible, texts)) diff --git a/irs/utils.py b/irs/utils.py index 545c3b2..b75e33f 100644 --- a/irs/utils.py +++ b/irs/utils.py @@ -53,3 +53,37 @@ def color(text, colors=[]): color_string += "bc.%s + " % color color_string = color_string[:-2] return (bc.ENDC + eval(color_string) + text + bc.ENDC) + +def color_input(text): + print (bc.HEADER + text, end=" ") + return input(bc.BOLD + bc.YELLOW + ": " + bc.ENDC) + +def exclaim_good(text, item): + print (bc.OKGREEN + text + bc.ENDC + item) + +def test_goodness(test, word, metadata_id, mp3): + if test: + exclaim_good(word + " added: ", mp3.get_attr(metadata_id)) + else: + print (bc.FAIL + word + " not added." + bc.ENDC) + +def search_google(self, search_terms=""): + def visible(element): + if element.parent.name in ['style', 'script', '[document]', 'head', 'title']: + return False + elif match('', str(element)): + return False + return True + + search_terms = "%s %s %s" % (self.song, self.artist, search_terms) + url = 'http://www.google.com/search?q=' + quote_plus(search_terms) + + hdr = { + 'User-Agent':'Mozilla/5.0', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + } + + texts = BeautifulSoup(urlopen(Request(url, \ + headers=hdr)).read(), 'html.parser').findAll(text=True) + + return list(filter(visible, texts)) diff --git a/setup.py b/setup.py index 847282b..9005ac9 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( name='irs', - version='1.6.12', + version='2.6.12', description='A music downloader that just gets metadata.', url='https://github.com/kepoorhampond/irs', author='Kepoor Hampond',