From b75cf268e87c96f4f730a8bac4999da3256a0ad2 Mon Sep 17 00:00:00 2001 From: "David Todd (c0de)" Date: Tue, 30 Jan 2018 14:27:29 -0600 Subject: [PATCH] Wait 10s and Add Error Handling This can determine the difference between playing/not playing, if the user token expired, and if we've been rate-limited. This can be easily expanded in the future for various other exceptions. --- README.md | 2 +- save_now_playing.py | 103 +++++++++++++++++++++++++++++++------------- 2 files changed, 75 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 7f34d25..7c842b7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is a collection of experimentations with the Spotipy python library. All of these scripts will request an auth token for a particular user (username arg) upon startup From my experimentation so far, this will remmeber that the app was previously authorized for whatever "authorization scope" was approved by the user at the oauth screen -save_now_playing.py - This script will poll for the user's now playing track every minute and update a text file. +save_now_playing.py - This script will poll for the user's now playing track every 10s and update a text file get_user_auth.py - "Hello World" from the docs, this shows how to authorize using env show_all_playlist.py - Unlike the name suggests, I did not see all my playlists. This could be an auth scope issue however diff --git a/save_now_playing.py b/save_now_playing.py index a2d8861..8653164 100644 --- a/save_now_playing.py +++ b/save_now_playing.py @@ -3,11 +3,16 @@ # License: MIT # Copyright (c) 2018 c0de +# Edit "set_env.sh" to configure your API key +# $ virtualenv venv +# $ ./set_env.sh +# $ pip install -r requirements.txt + +import sys +import time import spotipy import spotipy.util as util -import time -import sys -import os +from spotipy.client import SpotifyException start_time = time.time() @@ -20,35 +25,75 @@ else: def get_now_playing(sp): # This should be sp.currently_playing(), but somehow does not work... - # So I its return: https://github.com/plamere/spotipy/blob/master/spotipy/client.py#L899 - # Get the currently playing track and artist - np = sp._get("me/player/currently-playing", market=None) - return "%s by: %s" % (np['item']['name'], np['item']['artists'][0]['name']) + # So I used its return: https://github.com/plamere/spotipy/blob/master/spotipy/client.py#L899 -# Authorization Scope, can possibly be a dict? -scope = 'user-read-currently-playing' + try: + # Get the currently playing track and artist + np = sp._get("me/player/currently-playing", market=None) + # Take a look at the other goodies that gets returned here for future stuff + return "%s by: %s" % (np['item']['name'], np['item']['artists'][0]['name']) + except TypeError: + return "Nothing is playing" -# User Auth-Token - Achieved by running a webserver on localhost (might not be needed) as we just paste the URL here -token = util.prompt_for_user_token(username, scope) +def authorize_api(username, scope=None): + if not scope: + # Authorization Scope, can possibly be a dict? + scope = 'user-read-currently-playing' -if token: - # Authorize with the API - sp = spotipy.Spotify(auth=token) + print "[%.2fs] Authorizing %s with this auth scope: %s" % \ + (time.time()-start_time, username, scope) + # User Auth-Token - Achieved by running a webserver on localhost (might not be needed) as we just paste the URL here + # The access token appears to expire around 2000ish seconds of use. + # Rerunning this and retrieving the callback url (with new token) will allow the script to continue + token = util.prompt_for_user_token(username, scope) - while True: + if token: + # Authorize with the API + return spotipy.Spotify(auth=token) + else: + print "[%.2fs] Unable to authorize %s - Did they approve the oauth login?" % \ + (time.time()-start_time, username) + sys.exit() + +sp = authorize_api(username) + +# Main loop +while True: + try: np = get_now_playing(sp) + except SpotifyException as e: + if e.http_status == '401' and e.code == '-1': # Unauthorized and Expired access token + print "[%.2fs] Access Token for %s Expired, reaquiring..." % \ + (time.time()-start_time, username) + # Reauth and get the next now playing + sp = authorize_api(username) + continue + if e.http_status == '429': # Too Many Requests + # We're hitting the API too fast. The response should include a "Retry-After" Header + if e.headers and e.headers["Retry-After"]: + secs = float(e.headers["Retry-After"]) + print "[%.2fs] Too Many API Requests, waiting %.1fs before continuing" % \ + (time.time()-start_time, secs) + # Wait X Seconds before trying again + time.sleep(secs - (time.time() - start_time) % secs) + continue + else: + # Wait 30 secs + print "[%.2fs] Too Many API Requests and No Retry-After Header, \ + waiting 30s before continuing" % (time.time()-start_time) + time.sleep(30.0 - (time.time() - start_time) % 30.0) + continue + else: + raise Exception(e) - try: - with open(outfile, 'w') as output: - output.write(np) - print "[%.2fs] %s" % (time.time()-start_time, np) - output.close() - except: - print "[%.2fs] Unable to open %s" % (time.time()-start_time, outfile) - sys.exit() - - # Wait 1 minute before checking again - time.sleep(60.0 - ((time.time() - start_time) % 60.0)) -else: - print "Can't get a token for %s" % username - sys.exit() + try: + with open(outfile, 'w') as output: + output.write(np) + print "[%.2fs] %s" % (time.time()-start_time, np) + output.close() + except: + print "[%.2fs] Unable to open %s" % (time.time()-start_time, outfile) + sys.exit() + + # Wait approximately 10s before checking again + time.sleep(10.0 - ((time.time() - start_time) % 10.0))