spotifywebapiPython 1.0.179__py3-none-any.whl → 1.0.180__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of spotifywebapiPython might be problematic. Click here for more details.

Files changed (23) hide show
  1. {spotifywebapiPython-1.0.179.dist-info → spotifywebapiPython-1.0.180.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.179.dist-info → spotifywebapiPython-1.0.180.dist-info}/RECORD +23 -23
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +9 -2
  5. spotifywebapipython/docs/index.html +2 -2
  6. spotifywebapipython/docs/search.js +1 -1
  7. spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
  8. spotifywebapipython/docs/spotifywebapipython/saappmessages.html +54 -2
  9. spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
  10. spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
  11. spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
  12. spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
  13. spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +4 -3
  14. spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
  15. spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
  18. spotifywebapipython/saappmessages.py +15 -0
  19. spotifywebapipython/spotifyclient.py +69 -31
  20. spotifywebapipython/spotifywebplayertoken.py +59 -42
  21. {spotifywebapiPython-1.0.179.dist-info → spotifywebapiPython-1.0.180.dist-info}/LICENSE +0 -0
  22. {spotifywebapiPython-1.0.179.dist-info → spotifywebapiPython-1.0.180.dist-info}/WHEEL +0 -0
  23. {spotifywebapiPython-1.0.179.dist-info → spotifywebapiPython-1.0.180.dist-info}/top_level.txt +0 -0
@@ -1164,6 +1164,7 @@ class SpotifyClient:
1164
1164
  - 500 Internal Server Error. You should never receive this error because our clever coders catch them all ... but if you are unlucky enough to get one, please report it to us through a comment at the bottom of this page.
1165
1165
  - 502 Bad Gateway - The server was acting as a gateway or proxy and received an invalid response from the upstream server.
1166
1166
  - 503 Service Unavailable - The server is currently unable to handle the request due to a temporary condition which will be alleviated after some delay. You can choose to resend the request again.
1167
+ - 504 Gateway Timeout - The server is currently unable to handle the request due to a temporary condition which will be alleviated after some delay. You can choose to resend the request again.
1167
1168
  """
1168
1169
  apiMethodName:str = 'MakeRequest'
1169
1170
  apiMethodParms:SIMethodParmListContext = None
@@ -1228,49 +1229,86 @@ class SpotifyClient:
1228
1229
  # in the logic below, ensure that ALL urllib3.request method calls conform to version 1.26.18.
1229
1230
  # urllib3 version 2.0 is not supported! see internal developer notes for more details.
1230
1231
 
1231
- # call the appropriate poolmanager request method.
1232
- if msg.HasUrlParameters:
1232
+ # request retry loop for failed requests that are temporary in nature (504 Gateway Timeout, etc).
1233
+ loopTotalDelay:float = 0
1234
+ LOOP_DELAY:float = 0.200
1235
+ LOOP_TIMEOUT:float = 1.000
1236
+ while True:
1237
+
1238
+ # call the appropriate poolmanager request method.
1239
+ if msg.HasUrlParameters:
1233
1240
 
1234
- # add querystring parameters to url; if url already has a partial parm
1235
- # string (e.g. has a '?xxx=...') then use the append separator (e.g. '...&xxx=...').
1236
- urlQS:str = urlencode(msg.UrlParameters)
1237
- urlParmSep:str = '?'
1238
- if (url.find('?') > 0):
1239
- urlParmSep = '&'
1240
- url = url + urlParmSep + urlQS
1241
+ # add querystring parameters to url; if url already has a partial parm
1242
+ # string (e.g. has a '?xxx=...') then use the append separator (e.g. '...&xxx=...').
1243
+ urlQS:str = urlencode(msg.UrlParameters)
1244
+ urlParmSep:str = '?'
1245
+ if (url.find('?') > 0):
1246
+ urlParmSep = '&'
1247
+ url = url + urlParmSep + urlQS
1241
1248
 
1242
- _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (with urlparms)" % (url), msg.UrlParameters, prettyPrint=True)
1243
- response = self._Manager.request_encode_url(method, url, headers=msg.RequestHeaders)
1249
+ _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (with urlparms)" % (url), msg.UrlParameters, prettyPrint=True)
1250
+ response = self._Manager.request_encode_url(method, url, headers=msg.RequestHeaders)
1244
1251
 
1245
- elif msg.HasRequestData:
1252
+ elif msg.HasRequestData:
1246
1253
 
1247
- if msg.IsRequestDataEncoded:
1254
+ if msg.IsRequestDataEncoded:
1248
1255
 
1249
- _logsi.LogText(SILevel.Verbose, "SpotifyClient http request: '%s' (with body encoded)" % (url), msg.RequestData)
1250
- response = self._Manager.request(method, url, body=msg.RequestData, headers=msg.RequestHeaders)
1256
+ _logsi.LogText(SILevel.Verbose, "SpotifyClient http request: '%s' (with body encoded)" % (url), msg.RequestData)
1257
+ response = self._Manager.request(method, url, body=msg.RequestData, headers=msg.RequestHeaders)
1251
1258
 
1252
- else:
1259
+ else:
1253
1260
 
1254
- _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (with body)" % (url), msg.RequestData, prettyPrint=True)
1255
- response = self._Manager.request_encode_body(method, url, fields=msg.RequestData, headers=msg.RequestHeaders, encode_multipart=False)
1261
+ _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (with body)" % (url), msg.RequestData, prettyPrint=True)
1262
+ response = self._Manager.request_encode_body(method, url, fields=msg.RequestData, headers=msg.RequestHeaders, encode_multipart=False)
1256
1263
 
1257
- elif msg.HasRequestJson:
1264
+ elif msg.HasRequestJson:
1258
1265
 
1259
- _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (with json body)" % (url), msg.RequestJson, prettyPrint=True)
1266
+ _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (with json body)" % (url), msg.RequestJson, prettyPrint=True)
1260
1267
 
1261
- # add content-type=json header and convert the dictionary to json format.
1262
- if not ("content-type" in map(str.lower, msg.RequestHeaders.keys())):
1263
- #headers = HTTPHeaderDict(headers)
1264
- msg.RequestHeaders["Content-Type"] = "application/json"
1265
- reqBody:str = json.dumps(msg.RequestJson, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
1266
- _logsi.LogBinary(SILevel.Debug, "SpotifyClient http request JSON body", reqBody)
1267
- response = self._Manager.request(method, url, body=reqBody, headers=msg.RequestHeaders)
1268
+ # add content-type=json header and convert the dictionary to json format.
1269
+ if not ("content-type" in map(str.lower, msg.RequestHeaders.keys())):
1270
+ #headers = HTTPHeaderDict(headers)
1271
+ msg.RequestHeaders["Content-Type"] = "application/json"
1272
+ reqBody:str = json.dumps(msg.RequestJson, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
1273
+ _logsi.LogBinary(SILevel.Debug, "SpotifyClient http request JSON body", reqBody)
1274
+ response = self._Manager.request(method, url, body=reqBody, headers=msg.RequestHeaders)
1268
1275
 
1269
- else:
1276
+ else:
1277
+
1278
+ _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (no body)" % (url), msg.RequestData, prettyPrint=True)
1279
+ response = self._Manager.request(method, url, headers=msg.RequestHeaders)
1280
+
1281
+ # TEST TODO - for testing retry logic.
1282
+ # if (response.status_code == 200) and (loopTotalDelay <= 0.200):
1283
+ # _logsi.LogWarning("TEST TODO - Testing Spotify Web API 504 status (Gateway timeout) condition ...", colorValue=SIColors.Red)
1284
+ # response.status_code = 404
1285
+ # response.reason = "URL Not Found"
1286
+ # if (response.status_code == 200) and (loopTotalDelay <= 0.200):
1287
+ # _logsi.LogWarning("TEST TODO - Testing Spotify Web API 504 status (Gateway timeout) condition ...", colorValue=SIColors.Red)
1288
+ # response.status_code = 504
1289
+ # response.reason = "Gateway Timeout"
1290
+
1291
+ # check for errors that are temporary in nature; for these errors, we will retry the
1292
+ # request for a specified number of tries with a small wait period in between.
1293
+ if (response.status == 504):
1294
+
1295
+ # only retry so many times before we give up;
1296
+ if (loopTotalDelay >= LOOP_TIMEOUT):
1297
+ raise SpotifyApiError(SAAppMessages.MSG_SPOTIFY_WEB_API_RETRY_TIMEOUT % (loopTotalDelay), None, logsi=_logsi)
1298
+
1299
+ # trace.
1300
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_API_RETRY_RESPONSE_STATUS % (response.status, response.reason), colorValue=SIColors.Red)
1301
+
1302
+ # wait just a bit between requests.
1303
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_API_RETRY_REQUEST_DELAY % (LOOP_DELAY))
1304
+ time.sleep(LOOP_DELAY)
1305
+ loopTotalDelay = loopTotalDelay + LOOP_DELAY
1306
+
1307
+ else:
1308
+
1309
+ # otherwise, break out of retry loop and process response.
1310
+ break
1270
1311
 
1271
- _logsi.LogDictionary(SILevel.Verbose, "SpotifyClient http request: '%s' (no body)" % (url), msg.RequestData, prettyPrint=True)
1272
- response = self._Manager.request(method, url, headers=msg.RequestHeaders)
1273
-
1274
1312
  # process based upon response status code; some requests will not return response data.
1275
1313
  # I know this could have been simplified, but I broke it down into possible return code ranges.
1276
1314
  if response.status >= 200 and response.status <= 299:
@@ -7,6 +7,7 @@ import requests
7
7
  import time
8
8
 
9
9
  # our package imports.
10
+ from .saappmessages import SAAppMessages
10
11
  from .spotifyapierror import SpotifyApiError
11
12
  from .const import (
12
13
  SPOTIFY_WEBUI_URL_BASE,
@@ -14,7 +15,7 @@ from .const import (
14
15
  )
15
16
 
16
17
  # get smartinspect logger reference; create a new session for this module name.
17
- from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext
18
+ from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext, SIColors
18
19
  import logging
19
20
  _logsi:SISession = SIAuto.Si.GetSession(__name__)
20
21
  if (_logsi == None):
@@ -276,17 +277,17 @@ class SpotifyWebPlayerToken:
276
277
  "productType": "web_player",
277
278
  }
278
279
 
279
- # retry if gateway timeout error is returned.
280
+ # trace.
281
+ _logsi.LogVerbose("Exchanging Spotify Web Player cookie credentials for OAuth2 authorization access token")
282
+ _logsi.LogDictionary(SILevel.Verbose, "%s http request: \"%s\" (cookies)" % (tracePrefix, SPOTIFY_WEBUI_URL_GET_ACCESS_TOKEN), reqCookies)
283
+ _logsi.LogDictionary(SILevel.Debug, "%s http request: \"%s\" (headers)" % (tracePrefix, SPOTIFY_WEBUI_URL_GET_ACCESS_TOKEN), reqHeaders)
284
+ _logsi.LogDictionary(SILevel.Verbose, "%s http request: \"%s\" (parms)" % (tracePrefix, SPOTIFY_WEBUI_URL_GET_ACCESS_TOKEN), reqParms)
285
+
286
+ # request retry loop for failed requests that are temporary in nature (504 Gateway Timeout, etc).
280
287
  loopTotalDelay:float = 0
281
- LOOP_DELAY:float = 0.100
282
- LOOP_TIMEOUT:float = 0.500
288
+ LOOP_DELAY:float = 0.200
289
+ LOOP_TIMEOUT:float = 1.000
283
290
  while True:
284
-
285
- # trace.
286
- _logsi.LogVerbose("Exchanging Spotify Web Player cookie credentials for OAuth2 authorization access token")
287
- _logsi.LogDictionary(SILevel.Verbose, "%s http request: \"%s\" (cookies)" % (tracePrefix, SPOTIFY_WEBUI_URL_GET_ACCESS_TOKEN), reqCookies)
288
- _logsi.LogDictionary(SILevel.Debug, "%s http request: \"%s\" (headers)" % (tracePrefix, SPOTIFY_WEBUI_URL_GET_ACCESS_TOKEN), reqHeaders)
289
- _logsi.LogDictionary(SILevel.Verbose, "%s http request: \"%s\" (parms)" % (tracePrefix, SPOTIFY_WEBUI_URL_GET_ACCESS_TOKEN), reqParms)
290
291
 
291
292
  # convert the spotify web player cookie credentials to an access token.
292
293
  response = session.get(
@@ -297,42 +298,58 @@ class SpotifyWebPlayerToken:
297
298
  allow_redirects=False,
298
299
  )
299
300
 
300
- # trace.
301
- if _logsi.IsOn(SILevel.Debug):
302
- _logsi.LogObject(SILevel.Debug, "%s http response object - type=\"%s\", module=\"%s\"" % (tracePrefix, type(response).__name__, type(response).__module__), response)
303
-
304
- # trace.
305
- if _logsi.IsOn(SILevel.Debug):
306
- _logsi.LogObject(SILevel.Debug, "%s http response [%s-%s] (response)" % (tracePrefix, response.status_code, response.reason), response)
307
- if (response.headers):
308
- _logsi.LogCollection(SILevel.Debug, "%s http response [%s-%s] (headers)" % (tracePrefix, response.status_code, response.reason), response.headers.items())
309
-
310
- # raise exception if we could not get the access token, or if a redirect occured.
311
- # the redirect usually indicates the sp_dc and sp_key are expired, and it's redirecting to a login page.
312
- respHdrLocation:str = response.headers.get("Location", None)
313
- if (response.status_code == 302 and respHdrLocation == "/get_access_token?reason=transport&productType=web_player&_authfailed=1"):
314
- raise SpotifyApiError("Unsuccessful token request, received status 302 and Location header \"%s\". sp_dc and sp_key could be expired. Please update values in token storage file." % (respHdrLocation), None, logsi=_logsi)
315
-
316
- # if successful request, then break out of the retry loop.
317
- if response.status_code == 200:
318
- _logsi.LogVerbose("Token exchange request was successful within %f seconds from initial request; processing results" % (loopTotalDelay))
319
- break
301
+ # TEST TODO - for testing retry logic.
302
+ # if (response.status_code == 200) and (loopTotalDelay <= 0.200):
303
+ # _logsi.LogWarning("TEST TODO - Testing Spotify Web API 504 status (Gateway timeout) condition ...", colorValue=SIColors.Red)
304
+ # response.status_code = 404
305
+ # response.reason = "URL Not Found"
306
+ # if (response.status_code == 200) and (loopTotalDelay <= 0.200):
307
+ # _logsi.LogWarning("TEST TODO - Testing Spotify Web API 504 status (Gateway timeout) condition ...", colorValue=SIColors.Red)
308
+ # response.status_code = 504
309
+ # response.reason = "Gateway Timeout"
310
+
311
+ # check for errors that are temporary in nature; for these errors, we will retry the
312
+ # request for a specified number of tries with a small wait period in between.
313
+ if (response.status_code == 504):
314
+
315
+ # only retry so many times before we give up.
316
+ if (loopTotalDelay >= LOOP_TIMEOUT):
317
+ raise SpotifyApiError(SAAppMessages.MSG_SPOTIFY_WEB_API_RETRY_TIMEOUT % (loopTotalDelay), None, logsi=_logsi)
320
318
 
321
- # any other status code besides 504 (gateway timeout) is an exception event!
322
- if response.status_code != 504:
323
- raise SpotifyApiError("Token exchange request failed: %s - %s" % (response.status_code, response.reason), None, logsi=_logsi)
319
+ # trace.
320
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_API_RETRY_RESPONSE_STATUS % (response.status_code, response.reason), colorValue=SIColors.Red)
324
321
 
325
- # only retry so many times before we give up;
326
- if (loopTotalDelay > LOOP_TIMEOUT):
327
- raise SpotifyApiError("Timed out waiting for Spotify Gateway to become available; gave up after %f seconds from initial request" % (loopTotalDelay), None, logsi=_logsi)
322
+ # wait just a bit between requests.
323
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_API_RETRY_REQUEST_DELAY % (LOOP_DELAY))
324
+ time.sleep(LOOP_DELAY)
325
+ loopTotalDelay = loopTotalDelay + LOOP_DELAY
328
326
 
329
- # trace.
330
- _logsi.LogVerbose("Spotify Web Player cookie credentials token exchange request returned with a 504 status (Gateway timeout); request will be retried")
327
+ else:
328
+
329
+ # otherwise, break out of retry loop and process response.
330
+ break
331
331
 
332
- # wait just a bit between requests.
333
- _logsi.LogVerbose("Delaying for %s seconds to allow Spotify Gateway to become available" % (LOOP_DELAY))
334
- time.sleep(LOOP_DELAY)
335
- loopTotalDelay = loopTotalDelay + LOOP_DELAY
332
+ # trace.
333
+ if _logsi.IsOn(SILevel.Debug):
334
+ _logsi.LogObject(SILevel.Debug, "%s http response object - type=\"%s\", module=\"%s\"" % (tracePrefix, type(response).__name__, type(response).__module__), response)
335
+
336
+ # trace.
337
+ if _logsi.IsOn(SILevel.Debug):
338
+ _logsi.LogObject(SILevel.Debug, "%s http response [%s-%s] (response)" % (tracePrefix, response.status_code, response.reason), response)
339
+ if (response.headers):
340
+ _logsi.LogCollection(SILevel.Debug, "%s http response [%s-%s] (headers)" % (tracePrefix, response.status_code, response.reason), response.headers.items())
341
+
342
+ # raise exception if we could not get the access token, or if a redirect occured.
343
+ # the redirect usually indicates the sp_dc and sp_key are expired, and it's redirecting to a login page.
344
+ respHdrLocation:str = response.headers.get("Location", None)
345
+ if (response.status_code == 302 and respHdrLocation == "/get_access_token?reason=transport&productType=web_player&_authfailed=1"):
346
+ raise SpotifyApiError("Unsuccessful token request, received status 302 and Location header \"%s\". sp_dc and sp_key could be expired. Please update values in token storage file." % (respHdrLocation), None, logsi=_logsi)
347
+
348
+ # if successful request, then break out of the retry loop.
349
+ if (response.status_code == 200):
350
+ _logsi.LogVerbose("Token exchange request was successful within %f seconds from initial request; processing results" % (loopTotalDelay))
351
+ else:
352
+ raise SpotifyApiError("Token exchange request failed: %s - %s" % (response.status_code, response.reason), None, logsi=_logsi)
336
353
 
337
354
  # load request response.
338
355
  data = response.content.decode('utf-8')