From b50675778bd766255eda0e725d0d863115384403 Mon Sep 17 00:00:00 2001 From: Kepoor Hampond Date: Wed, 21 Dec 2016 11:57:00 -0800 Subject: [PATCH] fixed stupid syntax error --- irs/manage.py | 2 +- 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 15408 -> 0 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 -- 24 files changed, 2 insertions(+), 1479 deletions(-) delete mode 100644 v2.0/build/lib/irs/__init__.py delete mode 100644 v2.0/build/lib/irs/__main__.py delete mode 100644 v2.0/build/lib/irs/manage.py delete mode 100644 v2.0/build/lib/irs/metadata.py delete mode 100644 v2.0/build/lib/irs/utils.py delete mode 100644 v2.0/dist/irs-1.5.10-py3.5.egg delete mode 100644 v2.0/irs.egg-info/PKG-INFO delete mode 100644 v2.0/irs.egg-info/SOURCES.txt delete mode 100644 v2.0/irs.egg-info/dependency_links.txt delete mode 100644 v2.0/irs.egg-info/entry_points.txt delete mode 100644 v2.0/irs.egg-info/requires.txt delete mode 100644 v2.0/irs.egg-info/top_level.txt delete mode 100644 v2.0/irs/__main__.py delete mode 100644 v2.0/irs/manager.py delete mode 100644 v2.0/irs/metadata.py delete mode 100644 v2.0/irs/utils.py delete mode 100644 v2.0/old/__init__.py delete mode 100644 v2.0/old/__main__.py delete mode 100644 v2.0/old/manage.py delete mode 100644 v2.0/old/metadata.py delete mode 100644 v2.0/setup.cfg delete mode 100644 v2.0/setup.py diff --git a/irs/manage.py b/irs/manage.py index 05556b9..4cbbaaf 100644 --- a/irs/manage.py +++ b/irs/manage.py @@ -123,7 +123,7 @@ def rip_playlist(file_name, import shutil # Only import this if I have to. shutil.rmtree(locations[0]) - if os.path.isfile(filename) + if os.path.isfile(filename): os.rename(filename, folder_name + "/" + filename) os.rename(folder_name, folder_name.replace("playlist")) diff --git a/setup.py b/setup.py index 43fe8bb..4d3f91d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup setup( name='irs', - version='1.6.10', + version='1.6.11', 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 deleted file mode 100644 index e69de29..0000000 diff --git a/v2.0/build/lib/irs/__main__.py b/v2.0/build/lib/irs/__main__.py deleted file mode 100644 index f1101a8..0000000 --- a/v2.0/build/lib/irs/__main__.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/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 deleted file mode 100644 index 05556b9..0000000 --- a/v2.0/build/lib/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 - 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 deleted file mode 100644 index 3dda37e..0000000 --- a/v2.0/build/lib/irs/metadata.py +++ /dev/null @@ -1,166 +0,0 @@ -# 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 deleted file mode 100644 index 545c3b2..0000000 --- a/v2.0/build/lib/irs/utils.py +++ /dev/null @@ -1,55 +0,0 @@ -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 deleted file mode 100644 index dd3beb426631ca1dba2afd14b94d5fbcf5747b0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15408 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 P_^5v9dH+W{AfW#TiFM~t diff --git a/v2.0/irs.egg-info/PKG-INFO b/v2.0/irs.egg-info/PKG-INFO deleted file mode 100644 index ed0ea49..0000000 --- a/v2.0/irs.egg-info/PKG-INFO +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 3011688..0000000 --- a/v2.0/irs.egg-info/SOURCES.txt +++ /dev/null @@ -1,13 +0,0 @@ -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 deleted file mode 100644 index 8b13789..0000000 --- a/v2.0/irs.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/v2.0/irs.egg-info/entry_points.txt b/v2.0/irs.egg-info/entry_points.txt deleted file mode 100644 index 047a3c6..0000000 --- a/v2.0/irs.egg-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -irs = irs.__main__:main - diff --git a/v2.0/irs.egg-info/requires.txt b/v2.0/irs.egg-info/requires.txt deleted file mode 100644 index a35c36c..0000000 --- a/v2.0/irs.egg-info/requires.txt +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 098d8cc..0000000 --- a/v2.0/irs.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -irs diff --git a/v2.0/irs/__main__.py b/v2.0/irs/__main__.py deleted file mode 100644 index 1408268..0000000 --- a/v2.0/irs/__main__.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/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 deleted file mode 100644 index 348c46c..0000000 --- a/v2.0/irs/manager.py +++ /dev/null @@ -1,42 +0,0 @@ -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 deleted file mode 100644 index 716e5a5..0000000 --- a/v2.0/irs/metadata.py +++ /dev/null @@ -1,89 +0,0 @@ -# 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 deleted file mode 100644 index 545c3b2..0000000 --- a/v2.0/irs/utils.py +++ /dev/null @@ -1,55 +0,0 @@ -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 deleted file mode 100644 index e69de29..0000000 diff --git a/v2.0/old/__main__.py b/v2.0/old/__main__.py deleted file mode 100644 index f1101a8..0000000 --- a/v2.0/old/__main__.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/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 deleted file mode 100644 index 05556b9..0000000 --- a/v2.0/old/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 - 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 deleted file mode 100644 index fc633ad..0000000 --- a/v2.0/old/metadata.py +++ /dev/null @@ -1,96 +0,0 @@ - - -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 deleted file mode 100644 index 79bc678..0000000 --- a/v2.0/setup.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[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 deleted file mode 100644 index c00f65d..0000000 --- a/v2.0/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -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'], - }, -)