Merge pull request #28 from kepoorhampond/random-unicode-character-errors

fixed random unicode character error and removed the need to install ffmpeg.
This commit is contained in:
Kepoor Hampond 2017-06-10 18:16:43 -07:00 committed by GitHub
commit 573bc7c656
12 changed files with 139 additions and 55 deletions

3
.gitignore vendored
View file

@ -4,6 +4,7 @@
/*.egg-info/
/build/
__pycache__/
.eggs
# For easy updating of stuff.
update_pypi_and_github.py
@ -21,4 +22,4 @@ update_pypi_and_github.py
.coverage
# Temporarily built binaries
ffmpeg binaries/
ffmpeg binaries/

View file

@ -4,13 +4,8 @@ python:
- "3.5"
- "3.6"
before_script:
- sudo aptitude -y -q install ffmpeg libavcodec-extra-53 lame libmp3lame0
# These dependencies are necessary for ffmpeg. I currently hate all things
# doing with Travis and ffmpeg because I have no direct access to test stuff.
# I've gone through 25 seperate commits to get it working.
install:
- pip install -r requirements.txt
- python setup.py install
script:

View file

@ -1,7 +1,6 @@
<div align="center"><img src ="http://i.imgur.com/VbsyTe7.png" /></div>
Ironic Redistribution System
===
# Ironic Redistribution System
[![License: GNU](https://img.shields.io/badge/license-gnu-yellow.svg?style=flat-square)](http://www.gnu.org/licenses/gpl.html)
[![Stars](https://img.shields.io/github/stars/kepoorhampond/irs.svg?style=flat-square)](https://github.com/kepoorhampond/irs/stargazers)
@ -14,10 +13,9 @@ Ironic Redistribution System
> A music downloader that understands your metadata needs.
A tool to download your music with metadata. It uses [Spotify](https://www.spotify.com/) for finding metadata and [Youtube](https://www.youtube.com/) for the actual audio source.
A tool to download your music with metadata. It uses [Spotify](https://www.spotify.com/) for finding metadata and [Youtube](https://www.youtube.com/) for the actual audio source. You will need to have some Spotify tokens, the instructions to set them up are [here](https://github.com/kepoorhampond/irs#spotify-tokens).
Works with Python 2 and 3.
___
## Demo and Usages
@ -59,7 +57,7 @@ $ irs -s "Bohemian Rhapsody"
$ irs -p "Best Nirvana"
```
## Install & The Dependencies <sub><sup>(my new band name)</sub></sup>
## Install & The Dependencies <sub><sup>(my new band name <sub><sup><sup><sub>\s</sub></sup></sup></sub>)</sub></sup>
Really there's only one actual external dependency: `ffmpeg`. For windows, you'll want to follow [this](http://www.wikihow.com/Install-FFmpeg-on-Windows) guide. For OSX, you'll want to install it through [`brew`](https://brew.sh/) with this command:
```
@ -76,9 +74,11 @@ Other than `ffmpeg` though, all other dependencies are automatically installed w
$ sudo pip install irs
```
## Spotify Playlists
**You will need to have some Spotify tokens, the instructions to set them up are [here](https://github.com/kepoorhampond/irs#spotify-tokens).**
To download spotify playlists, you'll want to head to their Dev Apps page, [here](https://developer.spotify.com/my-applications/). After doing that you'll want to create a new app. Name it whatever you want and then once you've done that, find the `Client ID` and `Client Secret` keys. You'll want to take those keys and paste them into your system's environment variables as `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET`, correspondingly. Viola! You can now download playlists!
## Spotify Tokens
To download metadata through spotify, you'll want to head to their Dev Apps page, [here](https://developer.spotify.com/my-applications/). After doing that you'll want to create a new app. Name it whatever you want and then once you've done that, find the `Client ID` and `Client Secret` keys. You'll want to take those keys and paste them into your system's environment variables as `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET`, correspondingly. Viola! You can now download metadata with IRS!
## Metadata

View file

@ -53,7 +53,7 @@ directory to place files in.")
}
}
# Combiner args from argparse and the ripper_args as above and then
# Combine args from argparse and the ripper_args as above and then
# remove all keys with the value of "None"
ripper_args.update(vars(args))

View file

@ -1,24 +1,9 @@
CONFIG = dict(
import sys
from os import path
default_flags = ['-o'],
# For default flags. Right now, it organizes your files into an
# artist/album/song structure.
# To add a flag or argument, add an element to the index:
# default_flags = ['-o', '-l', '~/Music']
SPOTIFY_CLIENT_ID = '',
SPOTIFY_CLIENT_SECRET = '',
# You can either specify Spotify keys here, or in environment variables.
sys.path.append(path.expanduser("~/.irs")) # Add config to path
additional_search_terms = 'lyrics',
# Search terms for youtube
import config_ # from "~/.irs/config_.py"
organize = True,
# True always forces organization.
# False always forces non-organization.
# None allows options and flags to determine if the files
# will be organized.
custom_directory = "",
# Defaults to '~/Music'
)
CONFIG = config_.CONFIG

24
irs/config_preset Normal file
View file

@ -0,0 +1,24 @@
CONFIG = dict(
default_flags = ['-o'],
# For default flags. Right now, it organizes your files into an
# artist/album/song structure.
# To add a flag or argument, add an element to the index:
# default_flags = ['-o', '-l', '~/Music']
SPOTIFY_CLIENT_ID = '',
SPOTIFY_CLIENT_SECRET = '',
# You can either specify Spotify keys here, or in environment variables.
additional_search_terms = 'lyrics',
# Search terms for youtube
organize = True,
# True always forces organization.
# False always forces non-organization.
# None allows options and flags to determine if the files
# will be organized.
custom_directory = "",
# When blank, defaults to '~/Music'
)

View file

@ -56,9 +56,9 @@ class Metadata:
mp3.save()
def find_album_and_track(song, artist):
tracks = spotipy.Spotify().search(q=song, type="track"
)["tracks"]["items"]
def find_album_and_track(song, artist, spotify=spotipy.Spotify()):
tracks = spotify.search(q=song, type="track")["tracks"]["items"]
for track in tracks:
if om.blank_include(track["name"], song):
if om.blank_include(track["artists"][0]["name"], artist):

View file

@ -1,14 +1,19 @@
# Powered by:
import youtube_dl
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
# System
import sys
import os
import glob
import shutil
# Add youtube-dl binary to path
sys.path.append(os.path.expanduser("~/.irs/bin/youtube-dl"))
# Powered by:
import youtube_dl # Locally imported from the binary
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
# Local utilities
from .utils import YdlUtils, ObjManip, Config
from .metadata import Metadata
@ -80,7 +85,7 @@ class Ripper:
# those flaws for being exclusive to them.
# And if those flaws are really enough to turn you off of them,
# then you *probably* don't really want to be with them anyways.
# Either way, it's up to you.
# Either way, it's up to you. (I'd just ignore this)
if Config.parse_organize(self):
if self.type in ("album", "song"):
@ -125,7 +130,7 @@ class Ripper:
def find_yt_url(self, song=None, artist=None, additional_search=None):
if additional_search is None:
additional_search = Config.parse_search_terms(self)
print(self.args["hook-text"].get("youtube"))
print(str(self.args["hook-text"].get("youtube")))
try:
if not song:
@ -136,7 +141,8 @@ class Ripper:
raise ValueError("Must specify song_title/artist in `args` with \
init, or in method arguments.")
search_terms = song + " " + artist + " " + additional_search
search_terms = str(song) + " " + str(artist
) + " " + str(additional_search)
query_string = urlencode({"search_query": (
search_terms.encode('utf-8'))})
link = "http://www.youtube.com/results?" + query_string
@ -194,7 +200,7 @@ album".split(" ")
return ("https://youtube.com" + self.code["href"], self.code["title"])
def album(self, title, artist=None): # Alias for `spotify_list("album", ...)`
def album(self, title, artist=None): # Alias for spotify_list("album", ..)
return self.spotify_list("album", title=title, artist=artist)
def playlist(self, title, username):
@ -321,7 +327,7 @@ with init, or in method arguments.")
return locations
def parse_song_data(self, song, artist):
album, track = find_album_and_track(song, artist)
album, track = find_album_and_track(song, artist, self.spotify)
if album is False:
return {}
@ -372,8 +378,7 @@ init, or in method arguments.")
print(self.args["hook-text"].get("song").format(song, artist))
file_name = str(data["file_prefix"] + ObjManip.blank(song, False) +
".mp3")
file_name = data["file_prefix"] + ObjManip.blank(song, False) + ".mp3"
ydl_opts = {
'format': 'bestaudio/best',
@ -386,6 +391,7 @@ init, or in method arguments.")
'progress_hooks': [YdlUtils.my_hook],
'output': "tmp_file",
'prefer-ffmpeg': True,
'ffmpeg_location': os.path.expanduser("~/.irs/bin/")
}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:

View file

@ -15,7 +15,11 @@ from time import sleep
import pkg_resources
# Config File and Flags
from .config import CONFIG
if sys.version_info[0] == 2:
import config
CONFIG = config.CONFIG
else:
from irs.config import CONFIG
# ==================
@ -56,6 +60,22 @@ class YdlUtils:
# Object Manipulation and Checking
# ================================
def set_encoding(ld, encoding): # ld => list or dictionary with strings in it
if type(ld) == dict:
for k in ld:
if type(ld[k]) == dict or type(ld[k]) == list:
ld[k] = set_encoding(ld[k], encoding)
elif type(ld[k]) == str:
ld[k] = encoding(ld[k])
elif type(ld) == list:
for index, datum in enumerate(ld):
if type(datum) == str:
ld[index] = encoding(datum)
elif type(ld[k]) == dict or type(ld[k]) == list:
ld[k] = set_encoding(ld[k], encoding)
return ld
@staticmethods
class ObjManip: # Object Manipulation
def limit_song_name(song):
@ -77,7 +97,7 @@ class ObjManip: # Object Manipulation
def blank(string, downcase=True):
import re
regex = re.compile('[^a-zA-Z0-9\ ]')
string = regex.sub('', string)
string = regex.sub('', str(string))
if downcase:
string = string.lower()
return ' '.join(string.split())
@ -118,11 +138,18 @@ class ObjManip: # Object Manipulation
del new_d[x]
return new_d
# ld => a list or dictionary with strings in it
def set_utf8_encoding(ld):
return set_encoding(ld, lambda x: x.encode('utf-8'))
def set_encoding(*args):
return set_encoding(*args)
# ========================================
# Download Log Reading/Updating/Formatting
# ========================================
@staticmethods
class DLog:
def format_download_log_line(t, download_status="not downloaded"):

7
requirements.txt Normal file
View file

@ -0,0 +1,7 @@
--index-url https://pypi.python.org/simple/
bs4
mutagen
requests
spotipy
ydl-binaries

View file

@ -1,4 +1,39 @@
from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
class PostDevelopCommand(develop):
"""Post-installation for development mode."""
def run(self):
# PUT YOUR PRE-INSTALL SCRIPT HERE or CALL A FUNCTION
develop.run(self)
# PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION
class PostInstallCommand(install):
"""Post-installation for installation mode."""
def run(self):
install.run(self) # Actually install the module and dependencies
try:
import ydl_binaries
except ImportError:
import pip
pip.main(['install', "ydl-binaries"])
import ydl_binaries
from os import path
from shutil import copyfile
print("\n\nDownloading Dependencies:\n")
ydl_binaries.download_ffmpeg("~/.irs/bin")
ydl_binaries.update_ydl("~/.irs/bin")
config_file = path.expanduser("~/.irs/config_.py")
if not path.isfile(config_file):
copyfile("irs/config_preset", config_file)
setup(
name = 'irs',
@ -9,14 +44,18 @@ setup(
author_email = 'kepoorh@gmail.com',
license = 'GPL',
packages = ['irs'],
install_requires=[
install_requires = [
'bs4',
'mutagen',
'youtube-dl',
'requests',
'spotipy',
'ydl-binaries'
],
entry_points = {
'console_scripts': ['irs = irs.cli:main'],
},
cmdclass = {
'develop': PostDevelopCommand,
'install': PostInstallCommand,
},
)

View file

@ -2,4 +2,4 @@ from irs.ripper import Ripper
print ("[*] Testing `song.py`")
Ripper().song("Da Frame 2R", "Arctic Monkeys")
print ("[+] Passed!")
print ("[+] Passed!")