Browse Source

Merge pull request #3 from weskerfoot/seamless-auth

Support seamless authentication
pull/4/head
Wesley Kerfoot 6 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. ### 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. ### 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. * 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 * Go to "Edit Settings" in your newly created app, then add
want. `http://localhost:8080`
as a redirect URI and hit "Save" (You may choose a different one by setting
`SPOTIPY_REDIRECT_URI` in your environment)
``` * Find your spotify username [here](https://www.spotify.com/us/account/overview/)
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'
```
* You can find the values for `SPOTIPY_CLIENT_ID` and `SPOTIPY_CLIENT_SECRET` * Now you can run `spotsync --host=my_mpd_host` where `my_mpd_host` is the host you
on the page for your application that you just created. 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/) * 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
* Now you can run `spotsync` with your `.env` file sourced (e.g. `source .env`) spotsync.

5
requirements.txt

@ -1,9 +1,14 @@
bottle==0.12.16
certifi==2019.3.9 certifi==2019.3.9
cffi==1.12.3 cffi==1.12.3
chardet==3.0.4 chardet==3.0.4
gevent==1.4.0
greenlet==0.4.15
httplib2==0.12.3
idna==2.8 idna==2.8
pycparser==2.19 pycparser==2.19
python-mpd2==1.0.0 python-mpd2==1.0.0
requests==2.21.0 requests==2.21.0
spotify-mpd-sync-weskerfoot==0.0.1
spotipy==2.4.4 spotipy==2.4.4
urllib3==1.24.2 urllib3==1.24.2

6
setup.py

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="spotify-mpd-sync-weskerfoot", name="spotify-mpd-sync-weskerfoot",
version="0.0.1", version="0.0.2",
author="Wesley Kerfoot", author="Wesley Kerfoot",
author_email="wes@wesk.tech", author_email="wes@wesk.tech",
description="Synchronize Spotify Playlist to MPD", description="Synchronize Spotify Playlist to MPD",
@ -15,7 +15,9 @@ setuptools.setup(
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
install_requires = [ install_requires = [
"spotipy>=2.4.4", "spotipy>=2.4.4",
"python-mpd2>=1.0.0" "python-mpd2>=1.0.0",
"bottle>=0.12.16",
"gevent>=1.4.0"
], ],
classifiers= [ classifiers= [
"Programming Language :: Python :: 3", "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 #! /usr/bin/env python
import gevent.monkey
gevent.monkey.patch_all()
import spotipy import spotipy
from spotipy.oauth2 import SpotifyClientCredentials from spotipy.oauth2 import SpotifyClientCredentials
from spotipy.util import prompt_for_user_token
from mpd import MPDClient from mpd import MPDClient
from mpd.base import CommandError from mpd.base import CommandError
from collections import defaultdict from collections import defaultdict
from re import sub from re import sub
from os import environ from os import environ
import spotipy.util as util
import argparse
from spotify_mpd_sync.msplaylist.authenticate import prompt_for_user_token
class Spotify(): class Spotify():
def __init__(self): def __init__(self, host="localhost", port=6600):
self.username = environ.get("SPOTIFY_USERNAME") self.username = environ.get("SPOTIFY_USERNAME")
self.client_credentials_manager = SpotifyClientCredentials()
self.sp = spotipy.Spotify( scope = "playlist-read-private"
client_credentials_manager=self.client_credentials_manager
) token = prompt_for_user_token(self.username, scope)
if token:
self.sp = spotipy.Spotify(auth=token)
self.mpd_client = MPDClient() self.mpd_client = MPDClient()
self.mpd_client.connect("127.0.0.1", 6600) self.mpd_client.connect(host, port)
self._playlists = defaultdict(lambda: []) self._playlists = defaultdict(lambda: [])
@ -81,5 +90,21 @@ class Spotify():
def run_sync(): 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() spotify.persist_playlists()

Loading…
Cancel
Save