Browse Source

Merge pull request #3 from weskerfoot/seamless-auth

Support seamless authentication
pull/4/head
Wesley Kerfoot 5 years ago
committed by GitHub
parent
commit
f003dab887
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      README.md
  2. 5
      requirements.txt
  3. 6
      setup.py
  4. 104
      spotify_mpd_sync/msplaylist/authenticate.py
  5. 39
      spotify_mpd_sync/msplaylist/spotify.py

25
README.md

@ -3,23 +3,20 @@
### Please note that this won't work without Spotify Premium because Spotify limits web API access to non-free accounts.
### This also requires you to have set up [mopidy-spotify](https://github.com/mopidy/mopidy-spotify) in order to play spotify tracks.
* Run `pip install --user .` in this repo.
* Run `pip3 install --user .` in this repo.
* 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:8080`
as a redirect URI and hit "Save" (You may choose a different one by setting
`SPOTIPY_REDIRECT_URI` in your environment)
```
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 --host=my_mpd_host` where `my_mpd_host` is the host you
are running MPD on (defaults to `localhost:6600` 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.

5
requirements.txt

@ -1,9 +1,14 @@
bottle==0.12.16
certifi==2019.3.9
cffi==1.12.3
chardet==3.0.4
gevent==1.4.0
greenlet==0.4.15
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

6
setup.py

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup(
name="spotify-mpd-sync-weskerfoot",
version="0.0.1",
version="0.0.2",
author="Wesley Kerfoot",
author_email="wes@wesk.tech",
description="Synchronize Spotify Playlist to MPD",
@ -15,7 +15,9 @@ 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",
"gevent>=1.4.0"
],
classifiers= [
"Programming Language :: Python :: 3",

104
spotify_mpd_sync/msplaylist/authenticate.py

@ -0,0 +1,104 @@
# shows a user's playlists (need to be authenticated via oauth)
import threading
import spotipy.oauth2 as oauth2
import spotipy
import queue
import os
from bottle import route, run, response, request
auth_token_queue = queue.Queue()
event_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 wait_for_done(task):
server = threading.Thread(target=task)
server.daemon = True
server.start()
while True:
event = event_queue.get()
if event == "done":
break
def run_server():
threading.Thread(target= lambda: wait_for_done(lambda: run(quiet=True,
host='localhost',
port=8080))).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", "http://localhost:8080")
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:
run_server()
auth_url = sp_oauth.get_authorize_url()
try:
import webbrowser
webbrowser.open(auth_url)
except:
print("Please navigate here: %s" % auth_url)
response = "%s?code=%s" % (redirect_uri, auth_token_queue.get())
event_queue.put("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

39
spotify_mpd_sync/msplaylist/spotify.py

@ -1,23 +1,32 @@
#! /usr/bin/env python
import gevent.monkey
gevent.monkey.patch_all()
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
import argparse
from spotify_mpd_sync.msplaylist.authenticate import prompt_for_user_token
class Spotify():
def __init__(self):
def __init__(self, host="localhost", port=6600):
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)
if token:
self.sp = spotipy.Spotify(auth=token)
self.mpd_client = MPDClient()
self.mpd_client.connect("127.0.0.1", 6600)
self.mpd_client.connect(host, port)
self._playlists = defaultdict(lambda: [])
@ -81,5 +90,21 @@ class Spotify():
def run_sync():
spotify = Spotify()
parser = argparse.ArgumentParser()
parser.add_argument("-H",
"--host",
default="localhost",
help="The MPD server you would like spotsync to use, defaults localhost")
parser.add_argument("-P",
"--port",
type=int,
default=6600,
help="The MPD port you would like spotsync to use, defaults to 6600")
args = parser.parse_args()
spotify = Spotify(host=args.host,
port=args.port)
spotify.persist_playlists()

Loading…
Cancel
Save