diff --git a/README.md b/README.md index 2359a33..82520d6 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,15 @@ * Go to [https://developer.spotify.com/dashboard/applications](https://developer.spotify.com/dashboard/applications) and create an application. Call it whatever you want. -* Create a `.env` or `.envrc` file with contents like this, put it wherever you - want. +* Go to "Edit Settings" in your newly created app, then add `http://localhost` + as a redirect URI and hit "Save" -``` -export SPOTIPY_CLIENT_ID='YOUR_CLIENT_ID' -export SPOTIPY_CLIENT_SECRET='YOUR_CLIENT_SECRET' -export SPOTIPY_REDIRECT_URI='http://localhost' -export SPOTIFY_USERNAME='YOUR_SPOTIFY_USERNAME' -``` +* Find your spotify username [here](https://www.spotify.com/us/account/overview/) -* You can find the values for `SPOTIPY_CLIENT_ID` and `SPOTIPY_CLIENT_SECRET` - on the page for your application that you just created. +* Now you can run `spotsync --username=username --host=my_mpd_host` where + `username` is your username from before, and `my_mpd_host` is the host you + are running MPD on (defaults to `localhost` if you do not pass it) -* You can find your spotify username [here](https://www.spotify.com/us/account/overview/) - -* Now you can run `spotsync` with your `.env` file sourced (e.g. `source .env`) +* You will be prompted to grant permission to the app, once that's done, it + will cache the credentials locally and you should be able to just run + spotsync. diff --git a/requirements.txt b/requirements.txt index 65e4d8a..018d068 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,12 @@ +bottle==0.12.16 certifi==2019.3.9 cffi==1.12.3 chardet==3.0.4 +httplib2==0.12.3 idna==2.8 pycparser==2.19 python-mpd2==1.0.0 requests==2.21.0 +spotify-mpd-sync-weskerfoot==0.0.1 spotipy==2.4.4 urllib3==1.24.2 diff --git a/setup.py b/setup.py index 37a198e..b90fc4c 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,8 @@ setuptools.setup( packages=setuptools.find_packages(), install_requires = [ "spotipy>=2.4.4", - "python-mpd2>=1.0.0" + "python-mpd2>=1.0.0", + "bottle>=0.12.16" ], classifiers= [ "Programming Language :: Python :: 3", diff --git a/spotify_mpd_sync/msplaylist/authenticate.py b/spotify_mpd_sync/msplaylist/authenticate.py new file mode 100644 index 0000000..f8778b7 --- /dev/null +++ b/spotify_mpd_sync/msplaylist/authenticate.py @@ -0,0 +1,94 @@ +# shows a user's playlists (need to be authenticated via oauth) + +from __future__ import print_function +import os +import spotipy.oauth2 as oauth2 +import spotipy +import queue +import threading +from bottle import route, run, response, request + +auth_token_queue = queue.Queue() + +@route('/') +def index(): + auth_code = request.query.code + if auth_code: + auth_token_queue.put(auth_code) + return "It worked! You may close this tab now" + + return "Oops! Something went wrong. Please file a bug report" + +def run_server(): + task = threading.Thread(target=lambda : run(host='localhost', port=8080)) + task.start() + +def prompt_for_user_token(username, scope=None, client_id = None, + client_secret = None, redirect_uri = None, cache_path = None): + """ + prompts the user to login if necessary and returns + the user token suitable for use with the spotipy.Spotify + constructor + + Parameters: + + - username - the Spotify username + - scope - the desired scope of the request + - client_id - the client id of your app + - client_secret - the client secret of your app + - redirect_uri - the redirect URI of your app + - cache_path - path to location to save tokens + """ + + if not client_id: + client_id = os.getenv('SPOTIPY_CLIENT_ID') + + if not client_secret: + client_secret = os.getenv('SPOTIPY_CLIENT_SECRET') + + if not redirect_uri: + redirect_uri = os.getenv('SPOTIPY_REDIRECT_URI') + + if not client_id: + print(''' + You need to set your Spotify API credentials. You can do this by + setting environment variables like so: + + export SPOTIPY_CLIENT_ID='your-spotify-client-id' + export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret' + export SPOTIPY_REDIRECT_URI='your-app-redirect-url' + + Get your credentials at + https://developer.spotify.com/my-applications + ''') + raise spotipy.SpotifyException(550, -1, 'no credentials set') + + cache_path = cache_path or ".cache-" + username + sp_oauth = oauth2.SpotifyOAuth(client_id, client_secret, redirect_uri, + scope=scope, cache_path=cache_path) + + # try to get a valid token for this user, from the cache, + # if not in the cache, the create a new (this will send + # the user to a web page where they can authorize this app) + + token_info = sp_oauth.get_cached_token() + + if not token_info: + auth_url = sp_oauth.get_authorize_url() + try: + import webbrowser + webbrowser.open(auth_url) + print("Opened %s in your browser" % auth_url) + except: + print("Please navigate here: %s" % auth_url) + + response = "localhost:8080?code=%s" % auth_token_queue.get() + auth_token_queue.task_done() + + code = sp_oauth.parse_response_code(response) + token_info = sp_oauth.get_access_token(code) + # Auth'ed API request + if token_info: + return token_info['access_token'] + else: + return None diff --git a/spotify_mpd_sync/msplaylist/spotify.py b/spotify_mpd_sync/msplaylist/spotify.py index 732dec9..9297145 100644 --- a/spotify_mpd_sync/msplaylist/spotify.py +++ b/spotify_mpd_sync/msplaylist/spotify.py @@ -2,19 +2,30 @@ import spotipy from spotipy.oauth2 import SpotifyClientCredentials +from spotipy.util import prompt_for_user_token from mpd import MPDClient from mpd.base import CommandError from collections import defaultdict from re import sub from os import environ +import spotipy.util as util + +from spotify_mpd_sync.msplaylist.authenticate import run_server, prompt_for_user_token class Spotify(): def __init__(self): + run_server() self.username = environ.get("SPOTIFY_USERNAME") - self.client_credentials_manager = SpotifyClientCredentials() - self.sp = spotipy.Spotify( - client_credentials_manager=self.client_credentials_manager - ) + + scope = "playlist-read-private" + + token = prompt_for_user_token(self.username, + scope, + client_id=environ.get("SPOTIPY_CLIENT_ID"), + client_secret=environ.get("SPOTIPY_CLIENT_SECRET"), + redirect_uri=environ.get("SPOTIPY_REDIRECT_URI")) + if token: + self.sp = spotipy.Spotify(auth=token) self.mpd_client = MPDClient() self.mpd_client.connect("127.0.0.1", 6600)