From aab301b25a45ba13a3b4b2c332029013abba2174 Mon Sep 17 00:00:00 2001 From: Kepoor Hampond Date: Tue, 20 Dec 2016 20:44:19 -0800 Subject: [PATCH] Added a default so that playlists are automatically downloaded into one folder rather than being spread out across multiple artist folders and the such. --- README.md | 5 +- irs/__main__.py | 62 ++++-- irs/manage.py | 45 ++++- setup.py | 2 +- v2.0/build/lib/irs/__init__.py | 0 v2.0/build/lib/irs/__main__.py | 153 ++++++++++++++ v2.0/build/lib/irs/manage.py | 265 +++++++++++++++++++++++++ v2.0/build/lib/irs/metadata.py | 166 ++++++++++++++++ v2.0/build/lib/irs/utils.py | 55 +++++ v2.0/dist/irs-1.5.10-py3.5.egg | Bin 0 -> 15408 bytes v2.0/irs.egg-info/PKG-INFO | 10 + v2.0/irs.egg-info/SOURCES.txt | 13 ++ v2.0/irs.egg-info/dependency_links.txt | 1 + v2.0/irs.egg-info/entry_points.txt | 3 + v2.0/irs.egg-info/requires.txt | 4 + v2.0/irs.egg-info/top_level.txt | 1 + v2.0/irs/__main__.py | 80 ++++++++ v2.0/irs/manager.py | 42 ++++ v2.0/irs/metadata.py | 89 +++++++++ v2.0/irs/utils.py | 55 +++++ v2.0/old/__init__.py | 0 v2.0/old/__main__.py | 153 ++++++++++++++ v2.0/old/manage.py | 265 +++++++++++++++++++++++++ v2.0/old/metadata.py | 96 +++++++++ v2.0/setup.cfg | 5 + v2.0/setup.py | 21 ++ 26 files changed, 1567 insertions(+), 24 deletions(-) create mode 100644 v2.0/build/lib/irs/__init__.py create mode 100644 v2.0/build/lib/irs/__main__.py create mode 100644 v2.0/build/lib/irs/manage.py create mode 100644 v2.0/build/lib/irs/metadata.py create mode 100644 v2.0/build/lib/irs/utils.py create mode 100644 v2.0/dist/irs-1.5.10-py3.5.egg create mode 100644 v2.0/irs.egg-info/PKG-INFO create mode 100644 v2.0/irs.egg-info/SOURCES.txt create mode 100644 v2.0/irs.egg-info/dependency_links.txt create mode 100644 v2.0/irs.egg-info/entry_points.txt create mode 100644 v2.0/irs.egg-info/requires.txt create mode 100644 v2.0/irs.egg-info/top_level.txt create mode 100644 v2.0/irs/__main__.py create mode 100644 v2.0/irs/manager.py create mode 100644 v2.0/irs/metadata.py create mode 100644 v2.0/irs/utils.py create mode 100644 v2.0/old/__init__.py create mode 100644 v2.0/old/__main__.py create mode 100644 v2.0/old/manage.py create mode 100644 v2.0/old/metadata.py create mode 100644 v2.0/setup.cfg create mode 100644 v2.0/setup.py diff --git a/README.md b/README.md index 45c96b2..df71a1b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ___ usage: irs (-h | -v) irs [-l] - irs -p PLAYLIST [-c COMMAND] [-l] + irs -p PLAYLIST [-ng] [-c COMMAND] [-l] irs -a ARTIST (-s SONG | -A ALBUM [-st SEARCH_TERMS]) [-c COMMAND] [-l] Options: @@ -31,9 +31,10 @@ Options: -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. ``` [![asciicast](https://asciinema.org/a/bcs7i0sjmka052wsdyxg5xrug.png)](https://asciinema.org/a/bcs7i0sjmka052wsdyxg5xrug?speed=3&autoplay=true) diff --git a/irs/__main__.py b/irs/__main__.py index 8c775b2..f1101a8 100644 --- a/irs/__main__.py +++ b/irs/__main__.py @@ -5,7 +5,7 @@ HELP = \ usage: irs (-h | -v) irs [-l] - irs -p PLAYLIST [-c COMMAND] [-l] + irs -p PLAYLIST [-ng] [-c COMMAND] [-l] irs -a ARTIST (-s SONG | -A ALBUM [-st SEARCH_TERMS]) [-c COMMAND] [-l] Options: @@ -25,9 +25,10 @@ Options: -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 @@ -36,29 +37,51 @@ from sys import exit from .manage import * from .utils import * -def console(): +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\n\t2) Album") + 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 1 or 2." + bc.ENDC) + print (bc.FAIL + "\nPlease enter a valid number." + bc.ENDC) - print (bc.HEADER + "Artist of song/album ", end="") - artist = input(bc.BOLD + bc.YELLOW + ": " + 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) - 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) + 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) @@ -72,6 +95,8 @@ 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", \ + 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.") @@ -80,6 +105,9 @@ def main(): 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.") + args = parser.parse_args() @@ -106,10 +134,10 @@ def main(): exit(1) elif not args.artist and not args.playlist: - console() + console(args) elif args.playlist: - rip_playlist(args.playlist, args.command, choose_link=args.link) + rip_playlist(args.playlist, args.command, choose_link=args.link, no_organize=args.no_organize) elif args.artist: if args.album: diff --git a/irs/manage.py b/irs/manage.py index bb8a04f..05556b9 100644 --- a/irs/manage.py +++ b/irs/manage.py @@ -32,7 +32,7 @@ def find_mp3(song, artist, given_up_score = 0 if not choose_link: - print (bc.YELLOW + "Finding youtube link ...", end="\r") + print (bc.YELLOW + "\nFinding youtube link ...", end="\r") while in_title == False: i += 1 given_up_score += 1 @@ -76,7 +76,9 @@ def find_mp3(song, artist, 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: @@ -85,12 +87,46 @@ def rip_playlist(file_name, errors = [] + song_number = 0 + for line in file: + if line.strip() == "": + pass + try: arr = line.strip("\n").split(" - ") song = arr[0] artist = arr[1] - rip_mp3(song, artist, command=command) + + 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) @@ -176,7 +212,7 @@ def rip_mp3(song, artist, 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. + choose_link=False, # Whether to allow the user to choose the link. ): audio_code = find_mp3(song, artist) @@ -204,7 +240,6 @@ def rip_mp3(song, artist, 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): @@ -226,3 +261,5 @@ def rip_mp3(song, artist, if command: loc = location + "/" + filename os.system((command.replace("%(loc)s", '"%s"' % loc) + " &")) + + return (location + "/" + filename) diff --git a/setup.py b/setup.py index c00f65d..43fe8bb 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( name='irs', - version='1.5.10', + version='1.6.10', description='A music downloader that just gets metadata.', url='https://github.com/kepoorhampond/irs', author='Kepoor Hampond', diff --git a/v2.0/build/lib/irs/__init__.py b/v2.0/build/lib/irs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/v2.0/build/lib/irs/__main__.py b/v2.0/build/lib/irs/__main__.py new file mode 100644 index 0000000..f1101a8 --- /dev/null +++ b/v2.0/build/lib/irs/__main__.py @@ -0,0 +1,153 @@ +#!/usr/bin python + +HELP = \ +""" +usage: + irs (-h | -v) + irs [-l] + irs -p PLAYLIST [-ng] [-c COMMAND] [-l] + irs -a ARTIST (-s SONG | -A ALBUM [-st SEARCH_TERMS]) [-c COMMAND] [-l] + +Options: + -h, --help show this help message and exit + -v, --version Display the version and exit. + -c COMMAND, --command COMMAND + Run a background command with each song's location. + Example: `-c "rhythmbox %(loc)s"` + -a ARTIST, --artist ARTIST + Specify the artist name. + -p PLAYLIST, --playlist PLAYLIST + Specify playlist filename. Each line in the file + should be formatted like so: `SONGNAME - ARTIST` + -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 +from sys import exit +from .manage import * +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') + parser.add_argument('-v', '--version', dest="version", action='store_true', help="Display the version and exit.") + parser.add_argument('-c', '--command', dest="command", help="Run a background command with each song's location.") + parser.add_argument('-a', '--artist', dest="artist", help="Specify the artist name.") + + parser.add_argument('-l', '--choose-link', action='store_true', dest="link", \ + help="Whether or not to choose the link from a list of titles.") + + 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", \ + 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.") + + + args = parser.parse_args() + + if args.help: + global HELP + print (HELP) + + elif args.version: + import pkg_resources + print ("\n\n" + color("Ingenious Redistribution System", ["HEADER", "BOLD"])) + 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") + 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") + exit(1) + + elif not args.artist and not args.playlist: + console(args) + + elif args.playlist: + rip_playlist(args.playlist, args.command, choose_link=args.link, no_organize=args.no_organize) + + elif args.artist: + if args.album: + rip_album(args.album, args.artist, command=args.command, \ + search=args.search_terms, choose_link=args.link) + + elif args.song: + rip_mp3(args.song, args.artist, command=args.command, choose_link=args.link) + + + +if __name__ == "__main__": + main() diff --git a/v2.0/build/lib/irs/manage.py b/v2.0/build/lib/irs/manage.py new file mode 100644 index 0000000..05556b9 --- /dev/null +++ b/v2.0/build/lib/irs/manage.py @@ -0,0 +1,265 @@ +# 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 + 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/v2.0/build/lib/irs/metadata.py b/v2.0/build/lib/irs/metadata.py new file mode 100644 index 0000000..3dda37e --- /dev/null +++ b/v2.0/build/lib/irs/metadata.py @@ -0,0 +1,166 @@ +# MP3 Metadata editing +from mutagen.mp3 import MP3, EasyMP3 +from mutagen.easyid3 import EasyID3 +from mutagen.id3 import ID3, APIC + +# Info getting +from urllib.parse import quote_plus, quote +from urllib.request import urlopen, Request + +# Info parsing +import json +from re import match +from bs4 import BeautifulSoup + +# Local utils +from .utils import * + +def search_google(song, artist, 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 + + 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', + } + + texts = BeautifulSoup(urlopen(Request(filename, \ + headers=hdr)).read(), 'html.parser').findAll(text=True) + + 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): + try: + urlopen(albumart).read() + except Exception: + return False + 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") + + 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"] + + return albumart diff --git a/v2.0/build/lib/irs/utils.py b/v2.0/build/lib/irs/utils.py new file mode 100644 index 0000000..545c3b2 --- /dev/null +++ b/v2.0/build/lib/irs/utils.py @@ -0,0 +1,55 @@ +import sys + +def strip_special_chars(string): + special_chars = "\ / : * ? \" < > | - ( )".split(" ") + for char in special_chars: + string.replace(char, "") + return string + +def supports_color(): + """ + Returns True if the running system's terminal supports color, and False + otherwise. + """ + plat = sys.platform + supported_platform = plat != 'Pocket PC' and (plat != 'win32' or + 'ANSICON' in os.environ) + # isatty is not always implemented, #6223. + is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + if not supported_platform or not is_a_tty: + return False + return True + +if supports_color(): + class bc: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[32m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + GRAY = '\033[30m' + YELLOW = '\033[33m' +else: + class bc: + HEADER = '' + OKBLUE = '' + OKGREEN = '' + WARNING = '' + FAIL = '' + ENDC = '' + BOLD = '' + UNDERLINE = '' + GRAY = '' + YELLOW = '' + +def color(text, colors=[]): + if colors == []: + raise "Must have definitions when calling color(text, colors=[])" + color_string = "" + for color in colors: + color_string += "bc.%s + " % color + color_string = color_string[:-2] + return (bc.ENDC + eval(color_string) + text + bc.ENDC) diff --git a/v2.0/dist/irs-1.5.10-py3.5.egg b/v2.0/dist/irs-1.5.10-py3.5.egg new file mode 100644 index 0000000000000000000000000000000000000000..dd3beb426631ca1dba2afd14b94d5fbcf5747b0e GIT binary patch literal 15408 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 P_^5v9dH+W{AfW#TiFM~t literal 0 HcmV?d00001 diff --git a/v2.0/irs.egg-info/PKG-INFO b/v2.0/irs.egg-info/PKG-INFO new file mode 100644 index 0000000..ed0ea49 --- /dev/null +++ b/v2.0/irs.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: irs +Version: 1.5.10 +Summary: A music downloader that just gets metadata. +Home-page: https://github.com/kepoorhampond/irs +Author: Kepoor Hampond +Author-email: kepoorh@gmail.com +License: GNU +Description: UNKNOWN +Platform: UNKNOWN diff --git a/v2.0/irs.egg-info/SOURCES.txt b/v2.0/irs.egg-info/SOURCES.txt new file mode 100644 index 0000000..3011688 --- /dev/null +++ b/v2.0/irs.egg-info/SOURCES.txt @@ -0,0 +1,13 @@ +setup.cfg +setup.py +irs/__init__.py +irs/__main__.py +irs/manage.py +irs/metadata.py +irs/utils.py +irs.egg-info/PKG-INFO +irs.egg-info/SOURCES.txt +irs.egg-info/dependency_links.txt +irs.egg-info/entry_points.txt +irs.egg-info/requires.txt +irs.egg-info/top_level.txt \ No newline at end of file diff --git a/v2.0/irs.egg-info/dependency_links.txt b/v2.0/irs.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/v2.0/irs.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/v2.0/irs.egg-info/entry_points.txt b/v2.0/irs.egg-info/entry_points.txt new file mode 100644 index 0000000..047a3c6 --- /dev/null +++ b/v2.0/irs.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +irs = irs.__main__:main + diff --git a/v2.0/irs.egg-info/requires.txt b/v2.0/irs.egg-info/requires.txt new file mode 100644 index 0000000..a35c36c --- /dev/null +++ b/v2.0/irs.egg-info/requires.txt @@ -0,0 +1,4 @@ +bs4 +mutagen +youtube-dl +requests diff --git a/v2.0/irs.egg-info/top_level.txt b/v2.0/irs.egg-info/top_level.txt new file mode 100644 index 0000000..098d8cc --- /dev/null +++ b/v2.0/irs.egg-info/top_level.txt @@ -0,0 +1 @@ +irs diff --git a/v2.0/irs/__main__.py b/v2.0/irs/__main__.py new file mode 100644 index 0000000..1408268 --- /dev/null +++ b/v2.0/irs/__main__.py @@ -0,0 +1,80 @@ +#!/usr/bin python + +HELP = \ +""" +usage: + irs (-h | -v) + irs [-l] + irs -p PLAYLIST [-ng] [-c COMMAND] [-l] + irs -a ARTIST (-s SONG | -A ALBUM [-st SEARCH_TERMS]) [-c COMMAND] [-l] + +Options: + -h, --help show this help message and exit + -v, --version Display the version and exit. + -c COMMAND, --command COMMAND + Run a background command with each song's location. + Example: `-c "rhythmbox %(loc)s"` + -a ARTIST, --artist ARTIST + Specify the artist name. + -p PLAYLIST, --playlist PLAYLIST + Specify playlist filename. Each line in the file + should be formatted like so: `SONGNAME - ARTIST` + -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. +""" + +# For exiting +from sys import exit + +# Parsing args +import argparse + +# Import the manager +from .manager import Manager + +def main(): + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('-h', '--help', action='store_true', dest='help') + parser.add_argument('-v', '--version', dest="version", action='store_true', help="Display the version and exit.") + parser.add_argument('-c', '--command', dest="command", help="Run a background command with each song's location.") + parser.add_argument('-a', '--artist', dest="artist", help="Specify the artist name.") + + parser.add_argument('-l', '--choose-link', action='store_true', dest="link", \ + help="Whether or not to choose the link from a list of titles.") + + 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", \ + 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.") + + + args = parser.parse_args() + + if args.help: + global HELP + print (HELP) + exit(1) + + Manager(args).main + + +if __name__ == "__main__": + main() diff --git a/v2.0/irs/manager.py b/v2.0/irs/manager.py new file mode 100644 index 0000000..348c46c --- /dev/null +++ b/v2.0/irs/manager.py @@ -0,0 +1,42 @@ +from os import system +from sys import exit +from .manage import * +from .utils import * + +class Manager: + def __init__(self, args): + self.args = args + + def main(): + args = self.a + + if args.version: + import pkg_resources + print ("\n\n" + color("Ingenious Redistribution System", ["HEADER", "BOLD"])) + 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") + 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") + exit(1) + + elif not args.artist and not args.playlist: + console(args) + + elif args.playlist: + rip_playlist(args.playlist, args.command, choose_link=args.link, no_organize=args.no_organize) + + elif args.artist: + if args.album: + rip_album(args.album, args.artist, command=args.command, \ + search=args.search_terms, choose_link=args.link) + + elif args.song: + rip_mp3(args.song, args.artist, command=args.command, choose_link=args.link) diff --git a/v2.0/irs/metadata.py b/v2.0/irs/metadata.py new file mode 100644 index 0000000..716e5a5 --- /dev/null +++ b/v2.0/irs/metadata.py @@ -0,0 +1,89 @@ +# MP3 Metadata editing +from mutagen.mp3 import MP3, EasyMP3 +from mutagen.easyid3 import EasyID3 +from mutagen.id3 import ID3, APIC + +# Info getting +from urllib.parse import quote_plus, quote +from urllib.request import urlopen, Request + +# Info parsing +import json +from re import match +from bs4 import BeautifulSoup + +# Local utils +from .utils import * + +class Metadata: + def __init__(self, song, artist, location): + self.song = song + self.artist = artist + self.location = location + + self.info = search_google(song, artist) + self.mp3 = MP3("%s/%s" % (location, filename), ID3=EasyID3) + + + def parse_title(): + self.mp3['title'] = self.song + self.mp3.save() + print (bc.OKGREEN + "Title parsed: " + bc.ENDC + self.mp3['title'][0]) + + def parse_artist(): + self.mp3['artist'] = self.artist + self.mp3.save() + print (bc.OKGREEN + "Artist parsed: " + bc.ENDC + self.mp3['artist'][0]) + + def parse_album(album=None): + try: + if not album: + for i, j in enumerate(self.info): + if "Album:" in j: + album = (self.info[i + 1]) + except Exception as e: + album = None + + if album: + self.mp3['album'] = album + print (bc.OKGREEN + "Album parsed: " + bc.ENDC + self.mp3['album'][0]) + else: + print (bc.FAIL + "Album not parsed.") + + self.mp3.save() + + def parse_release_date(): + for i, j in enumerate(self.info): + if "Released:" in j: + date = (self.info[i + 1]) + + try: + self.mp3['date'] = date + print (bc.OKGREEN + "Release date parsed: " + bc.ENDC + self.mp3['date'][0]) + except Exception: + self.mp3['date'] = "" + pass + + self.mp3.save() + + def search_google(self, song, artist, 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" % (song, artist, search_terms) + url = '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', + } + + texts = BeautifulSoup(urlopen(Request(filename, \ + headers=hdr)).read(), 'html.parser').findAll(text=True) + + return list(filter(visible, texts)) diff --git a/v2.0/irs/utils.py b/v2.0/irs/utils.py new file mode 100644 index 0000000..545c3b2 --- /dev/null +++ b/v2.0/irs/utils.py @@ -0,0 +1,55 @@ +import sys + +def strip_special_chars(string): + special_chars = "\ / : * ? \" < > | - ( )".split(" ") + for char in special_chars: + string.replace(char, "") + return string + +def supports_color(): + """ + Returns True if the running system's terminal supports color, and False + otherwise. + """ + plat = sys.platform + supported_platform = plat != 'Pocket PC' and (plat != 'win32' or + 'ANSICON' in os.environ) + # isatty is not always implemented, #6223. + is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + if not supported_platform or not is_a_tty: + return False + return True + +if supports_color(): + class bc: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[32m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + GRAY = '\033[30m' + YELLOW = '\033[33m' +else: + class bc: + HEADER = '' + OKBLUE = '' + OKGREEN = '' + WARNING = '' + FAIL = '' + ENDC = '' + BOLD = '' + UNDERLINE = '' + GRAY = '' + YELLOW = '' + +def color(text, colors=[]): + if colors == []: + raise "Must have definitions when calling color(text, colors=[])" + color_string = "" + for color in colors: + color_string += "bc.%s + " % color + color_string = color_string[:-2] + return (bc.ENDC + eval(color_string) + text + bc.ENDC) diff --git a/v2.0/old/__init__.py b/v2.0/old/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/v2.0/old/__main__.py b/v2.0/old/__main__.py new file mode 100644 index 0000000..f1101a8 --- /dev/null +++ b/v2.0/old/__main__.py @@ -0,0 +1,153 @@ +#!/usr/bin python + +HELP = \ +""" +usage: + irs (-h | -v) + irs [-l] + irs -p PLAYLIST [-ng] [-c COMMAND] [-l] + irs -a ARTIST (-s SONG | -A ALBUM [-st SEARCH_TERMS]) [-c COMMAND] [-l] + +Options: + -h, --help show this help message and exit + -v, --version Display the version and exit. + -c COMMAND, --command COMMAND + Run a background command with each song's location. + Example: `-c "rhythmbox %(loc)s"` + -a ARTIST, --artist ARTIST + Specify the artist name. + -p PLAYLIST, --playlist PLAYLIST + Specify playlist filename. Each line in the file + should be formatted like so: `SONGNAME - ARTIST` + -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 +from sys import exit +from .manage import * +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') + parser.add_argument('-v', '--version', dest="version", action='store_true', help="Display the version and exit.") + parser.add_argument('-c', '--command', dest="command", help="Run a background command with each song's location.") + parser.add_argument('-a', '--artist', dest="artist", help="Specify the artist name.") + + parser.add_argument('-l', '--choose-link', action='store_true', dest="link", \ + help="Whether or not to choose the link from a list of titles.") + + 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", \ + 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.") + + + args = parser.parse_args() + + if args.help: + global HELP + print (HELP) + + elif args.version: + import pkg_resources + print ("\n\n" + color("Ingenious Redistribution System", ["HEADER", "BOLD"])) + 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") + 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") + exit(1) + + elif not args.artist and not args.playlist: + console(args) + + elif args.playlist: + rip_playlist(args.playlist, args.command, choose_link=args.link, no_organize=args.no_organize) + + elif args.artist: + if args.album: + rip_album(args.album, args.artist, command=args.command, \ + search=args.search_terms, choose_link=args.link) + + elif args.song: + rip_mp3(args.song, args.artist, command=args.command, choose_link=args.link) + + + +if __name__ == "__main__": + main() diff --git a/v2.0/old/manage.py b/v2.0/old/manage.py new file mode 100644 index 0000000..05556b9 --- /dev/null +++ b/v2.0/old/manage.py @@ -0,0 +1,265 @@ +# 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 + 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/v2.0/old/metadata.py b/v2.0/old/metadata.py new file mode 100644 index 0000000..fc633ad --- /dev/null +++ b/v2.0/old/metadata.py @@ -0,0 +1,96 @@ + + +def parse_metadata(song, artist, location, filename, + tracknum="", + album="", + album_art_url="" + ): + + # 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): + try: + urlopen(albumart).read() + except Exception: + return False + 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") + + 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"] + + return albumart diff --git a/v2.0/setup.cfg b/v2.0/setup.cfg new file mode 100644 index 0000000..79bc678 --- /dev/null +++ b/v2.0/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/v2.0/setup.py b/v2.0/setup.py new file mode 100644 index 0000000..c00f65d --- /dev/null +++ b/v2.0/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup + +setup( + name='irs', + version='1.5.10', + description='A music downloader that just gets metadata.', + url='https://github.com/kepoorhampond/irs', + author='Kepoor Hampond', + author_email='kepoorh@gmail.com', + license='GNU', + packages =['irs'], + install_requires=[ + 'bs4', + 'mutagen', + 'youtube-dl', + 'requests', + ], + entry_points = { + 'console_scripts': ['irs = irs.__main__:main'], + }, +)