I'm running into this issue where with my script and I'm a bit stumped. I've been running this script with no problems for the past few weeks and now I'm getting a KeyError for the token.
Here's my code:
# IMPORTS
import os
import re
import requests
import json
import numpy as np
import pandas as pd
import time
from pprint import pprint as pp
import datetime as dt
import sys
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
acc_path = "../../access/"
sys.path.append(acc_path)
pd.set_option('display.max_rows', 10000)
pd.set_option('display.max_columns', 100)
pd.set_option('display.max_colwidth', -1)
# Spotify Credentials
sp_url = 'https://api.spotify.com/v1/'
client_id = os.environ.get('SPOT_CLIENT_ID')
client_secret = os.environ.get('SPOT_CLIENT_SECRET')
output_data = '/users/Desktop/file_date.csv'
spot_scopes = os.environ.get('SPOT_SCOPES')
spot_user_name = os.environ.get('SPOT_USER_NAME') # spotify account username
sp_acc = requests.post('https://accounts.spotify.com/api/token', data = {'grant_type' : 'client_credentials'},
auth = (client_id, client_secret))
sp_bear_head = {'Authorization' : 'Bearer' + str(sp_acc.json()['access_token'])}
KeyERROR
---> 31 sp_bear_head = {'Authorization' : 'Bearer' + str(sp_acc.json()['access_token'])}
KeyError: 'access_token'
I checked my bash to make sure client id, secret scopes etc. are all correct and can confirm that is not the issue. Any direction here would be very helpful!
The reason why it worked before and not now is due to the expiration of the access token.
The token's expiration time is determined by Spotify so you just have to work around their set constraints.
That being said, you can anticipate when a new token will need to be generated/used based on the property expires_in which Spotify sends back in the response when you request a token (https://accounts.spotify.com/api/token). The expires_in property is an integer and it tells you how many seconds the token will be good for. As seen in their authorization documentation, the expires_in property is returned with the value 3600 (seconds) or, 1 hour.
After that hour is up, use your refresh_token to request a new token.
Related
Hi everyone I'm seeing tutorials of Spotify API but I have one doubt, it's possible to send directly from code comands like play, next or play an specific song?
And if you are playing spotify in your phone it will change the song?
Update 1: This is the code that I have at the moment but I have this error message when I run it: (HELP WITH THIS)
SpotifyException: http status: 403, code:-1 - https://api.spotify.com/v1/me/player/play:
Player command failed: Premium required, reason: PREMIUM_REQUIRED
And this is my code:
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy
client_id = ""
client_secret = ""
autor = 'Radiohead'
sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials(client_id, client_secret))
result = sp.search(autor)
print(result)
#
sp.start_playback(uris=['spotify:artist:4Z8W4fKeB5YxbusRsdQVPb'])
Here is working code:
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from pprint import pprint
client_id = ""
client_secret = ""
redirect_uri = ""
scope = "user-read-playback-state,user-modify-playback-state"
sp = spotipy.Spotify(
auth_manager=spotipy.SpotifyOAuth(
client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
scope=scope, open_browser=False))
# Shows playing devices
res = sp.devices()
pprint(res)
# Change track
sp.start_playback(uris=['spotify:track:6gdLoMygLsgktydTQ71b15'])
You have to put a dummy redirect URL inside the code and in the dashboard of your app.
The script will ask “Go to the following URL:”, and after logging in, you'll need copy the resulting URL after “Enter the URL you was redirected to:”.
from time import sleep
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests
plt.style.use("fivethirtyeight")
import alpaca_trade_api as tradeapi
import threading
from bs4 import BeautifulSoup
import datetime
import logging
api_key = 'YOUR API KEY'
api_secret = 'YOUR API SECRET KEY'
base_url = 'https://paper-api.alpaca.markets'
data_url = 'wss://data.alpaca.markets'
ws_url = 'wss://data.alpaca.markets'
# instantiate REST API
api = tradeapi.REST(api_key, api_secret, base_url, api_version='v2')
# init WebSocket
conn = tradeapi.stream2.StreamConn(
api_key,
api_secret,
base_url=base_url,
data_url=data_url,
data_stream='alpacadatav1',
)
I get the error
conn = tradeapi.stream2.StreamConn(
AttributeError: module 'alpaca_trade_api' has no attribute 'stream2'
This code is coming from https://algotrading101.com/learn/alpaca-trading-api-guide/ on the part where he shows how he made a trading algorithm near the end of the page.
Do I need to import any extra libraries or am I wrong somewhere In the code.
That tutorial is slightly out of date. Newer versions of alpaca_trade_api are using the Stream class:
conn = tradeapi.stream.Stream(
key_id=api_key,
secret_key=api_secret,
base_url='https://paper-api.alpaca.markets',
data_feed='iex'
)
Note that the free data feed is 'iex' and the paid data feed is 'sip'. Once you establish a connection, you will need to subscribe to trades or quotes using conn.subscribe_trades() or conn.subscribe_quotes(). See the github page for details.
I'm attempting to call the Spotify API and have set up an app/got my client ID and Secret. Here's an example of my code (with specifics blocked out):
import spotipy
import spotipy.util as util
from spotipy.oauth2 import SpotifyClientCredentials
cid ="xx"
secret = "xx"
username = "xx"
client_credentials_manager = SpotifyClientCredentials(client_id=cid, client_secret=secret)
sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
scope = 'user-library-read playlist-read-private'
token = util.prompt_for_user_token(username,scope,client_id='http://localhost:8888/callback/',client_secret='http://localhost:8888/callback/',redirect_uri='http://localhost:8888/callback/')
if token:
sp = spotipy.Spotify(auth=token)
else:
print("Can't get token for", username)
cache_token = token.get_access_token()
sp = spotipy.Spotify(cache_token)
currentfaves = sp.current_user_top_tracks(limit=20, offset=0, time_range='medium_term')
print(currentfaves)
I've made sure my URL is exactly the same as what's registered in my Spotify app development page, and I've added the client ID, redirect URIs and client Secret keys to my environment variables.
So far a separate tab opens up (https://accounts.spotify.com/authorize?client_id=http%3A%2F%2Flocalhost%3A8888%2Fcallback%2F&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2Fcallback%2F&scope=playlist-read-private+user-library-read) but I'm only getting 'INVALID_CLIENT: Invalid client' on that page. What can I do/change to make this work?
token = util.prompt_for_user_token(username,scope,client_id='http://localhost:8888/callback/',client_secret='http://localhost:8888/callback/',redirect_uri='http://localhost:8888/callback/')
Is there a typo in here, since you copied the redirect uri also as the client id and secret?
I've followed the directions in https://developers.google.com/accounts/docs/OAuth2ServiceAccount to use a service account to authenticate to the Google Cloud Storage API. I tried to send a JWT to google's authenticate servers in python, but got an error:
urllib2.HTTPError: HTTP Error 400: Bad Request
It looks like there's something wrong with the way I'm making, signing, or sending the JWT? The error wasn't specific so it could be any part of the process. Does anyone have any ideas?
import Crypto.PublicKey.RSA as RSA
import Crypto.Hash.SHA as SHA
import Crypto.Signature.PKCS1_v1_5 as PKCS1_v1_5
import base64
import json
import time
import urllib2
import urllib
# Settings
json_key_file = 'GooglePM-9f75ad112f87-service.json'
# Load the private key associated with the Google service account
with open(json_key_file) as json_file:
json_data = json.load(json_file)
key = RSA.importKey(json_data['private_key'])
# Create an PKCS1_v1_5 object
signer = PKCS1_v1_5.new(key)
# Encode the JWT header
header_b64 = base64.urlsafe_b64encode(json.dumps({'alg':'RS256','typ':'JWT'}))
# JWT claims
jwt = {
'iss': json_data['client_email'],
'scope': 'https://www.googleapis.com/auth/devstorage.read_write',
'aud': 'https://accounts.google.com/o/oauth2/token',
'exp': int(time.time())+3600,
'iat': int(time.time())
}
jwt_json = json.dumps(jwt)
# Encode the JWT claims
jwt_json_b64 = base64.urlsafe_b64encode(jwt_json)
# Sign the JWT header and claims
msg_hash = SHA.new(header_b64 + "." + jwt_json_b64)
signature_b64 = base64.urlsafe_b64encode(signer.sign(msg_hash))
# Make the complete message
jwt_complete = header_b64 + "." + jwt_json_b64 + "." + signature_b64
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': jwt_complete}
f = urllib2.urlopen("https://accounts.google.com/o/oauth2/token", urllib.urlencode(data))
print f.read()
If I try to use curl to post to the server, I get the invalid grants error:
(venv)$ curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiMTM1MDY3NjIyMTk4LWVhbWUwZnFqdTNvamRoZ29zdDg2dnBpdTBsYW91NnZlQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwgInNjb3BlIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIiwgImF1ZCI6ICJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvdG9rZW4iLCAiZXhwIjogMTQwODY1MTU2OCwgImlhdCI6IDE0MDg2NDg1NTh9.HWC7h3QiOy7QsSuta4leq_Gjwmy9IdF-MUwflPhiohzAJ-Amykd56Ye4Y_Saf_sAc5STzOCmrSPzOTYvGXr6X_T_AmSTxXK2AJ2SpAiEUs2_Wp5h18xTUY3Y_hkKvSZLh5bRzeJ_0xRcmRIPE6tua0FHFwUDdnCIGdh4DGg6i4E%3D' https://accounts.google.com/o/oauth2/token
{
"error" : "invalid_grant"
}
Ok so there's a better way to do this! Google already has a python client API that handles some of the complexity. The following code works after installing google python client API: https://developers.google.com/api-client-library/python/guide/aaa_oauth
from oauth2client.client import SignedJwtAssertionCredentials
import json
import urllib
import urllib2
# Settings
json_key_file = 'GooglePM-9f75ad112f87-service.json'
# Load the private key associated with the Google service account
with open(json_key_file) as json_file:
json_data = json.load(json_file)
# Get and sign JWT
credential = SignedJwtAssertionCredentials(json_data['client_email'], json_data['private_key'], 'https://www.googleapis.com/auth/devstorage.read_write')
jwt_complete = credential._generate_assertion()
# Get token from server
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': jwt_complete}
f = urllib2.urlopen("https://accounts.google.com/o/oauth2/token", urllib.urlencode(data))
print f.read()
maybe, slightly simplier:
import oauth2client.service_account
jsonfile = 'GooglePM-9f7sdf342f87-service.json'
# use static method .from_json_keyfile_name(filename)
credentials = oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name(jsonfile)
Your first algorithm is completely ok and working, but you need SHA256, not SHA. Thank you for your code.
Initial code is also working using Python 3.6. You only have to use bytes instead of str in case of base64:
import requests
import json as js
import Crypto.PublicKey.RSA as RSA
import Crypto.Hash.SHA256 as SHA
import Crypto.Signature.PKCS1_v1_5 as PKCS1_v1_5
import base64
import time
# Settings
json_key_file = 'google-api.json'
# Load the private key associated with the Google service account
with open(json_key_file) as json_file:
json_data = js.load(json_file)
key = RSA.importKey(json_data['private_key'])
# Create an PKCS1_v1_5 object
signer = PKCS1_v1_5.new(key)
header = js.dumps({'alg':'RS256','typ':'JWT'})
# Encode the JWT header
header_b64 = base64.urlsafe_b64encode(header.encode("UTF-8"))
# JWT claims
jwt = {
'iss': json_data['client_email'],
'scope': 'https://www.googleapis.com/auth/analytics.readonly',
'aud': 'https://accounts.google.com/o/oauth2/token',
'exp': int(time.time())+3600,
'iat': int(time.time())
}
jwt_json = js.dumps(jwt)
# Encode the JWT claims
jwt_json_b64 = base64.urlsafe_b64encode(jwt_json.encode("UTF-8"))
# Sign the JWT header and claims
msg_hash = SHA.new((header_b64.decode("UTF-8") + "." + jwt_json_b64.decode("UTF-8")).encode("UTF-8"))
signature_b64 = base64.urlsafe_b64encode(signer.sign(msg_hash))
# Make the complete message
jwt_complete = (header_b64.decode("UTF-8") + "." + jwt_json_b64.decode("UTF-8") + "." + signature_b64.decode("UTF-8")).encode("UTF-8")
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': jwt_complete}
requests.post(url="https://accounts.google.com/o/oauth2/token",data=data).text
I've really been struggling with getting OAuth to work with the WiThings API lately, using Python 3.3. For reference, here is the documentation for WiThings: http://www.withings.com/api
Now... As I've said, I have been working with the WiThings API in Python, using the requests library (http://docs.python-requests.org/en/latest/). Supposedly, this has built in support for OAuth 1.0.
Using this, when I put in my consumer key and consumer secret, then perform the token request, I get this response...
b'<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>413 Request Entity Too Large</title>\n</head><body>\n<h1>Request Entity Too Large</h1>\nThe requested resource<br />/index.php<br />\ndoes not allow request data with POST requests, or the amount of data provided in\nthe request exceeds the capacity limit.\n<hr>\n<address>Apache Server at oauth.withings.com Port 80</address>\n</body></html>\n'
Any idea what could be causing this? I have a feeling its WiThings specific... but their support is horrible.
Next, I did some more research, and found this: https://github.com/maximebf/python-withings
While also rather poorly documented, I installed it, and have this code:
from __future__ import unicode_literals
from urllib.parse import parse_qs
import requests
from requests_oauthlib import OAuth1
import withings
CONSUMER_KEY = "omitted"
CONSUMER_SECRET = "omitted"
auth = WithingsAuth(CONSUMER_KEY, CONSUMER_SECRET)
authorize_url = auth.get_authorize_url()
print("Go to %s allow the app and copy your oauth_verifier" %authorize_url)
oauth_verifier = raw_input('Please enter your oauth_verifier: ')
creds = auth.get_credentials(oauth_verifier)
client = WithingsApi(creds)
measures = client.get_measures(limit=1)
print("Your last measured weight: %skg" % measures[0].weight)
And get the following error...
File "withings.py", line 5, in <module>
import withjings
File C:\User_Directory\withings.py", line 11, in <module>
auth = WithingsAuth(CONSUMER_KEY, CONSUMER_SECRET)
NameError: name 'WithingsAuth' is not defined
Any help on either of these issues? Has anyone had success working with Withings in python?
Thanks for the help guys
It should be
from withings import WithingsAuth, WithingsApi
For me it works well I was able to extract my last measured weight.
You either need to import WithingsAuth from withings, or specify that you want to use the withings.WithingsAuth. Changing your code it becomes:
from __future__ import unicode_literals
try:
from urllib.parse import parse_qs
except:
import urlparse as parse_qs
try:
input_method = raw_input
except:
input_method = input
import requests
from requests_oauthlib import OAuth1
import withings
CONSUMER_KEY = "omitted"
CONSUMER_SECRET = "omitted"
auth = withings.WithingsAuth(CONSUMER_KEY, CONSUMER_SECRET)
authorize_url = auth.get_authorize_url()
print("Go to %s allow the app and copy your oauth_verifier" %authorize_url)
oauth_verifier = input_method('Please enter your oauth_verifier: ')
creds = auth.get_credentials(oauth_verifier)
client = withings.WithingsApi(creds)
measures = client.get_measures(limit=1)
print("Your last measured weight: %skg" % measures[0].weight)