spotifywebapiPython 1.0.39__py3-none-any.whl → 1.0.41__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.
- {spotifywebapiPython-1.0.39.dist-info → spotifywebapiPython-1.0.41.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.39.dist-info → spotifywebapiPython-1.0.41.dist-info}/RECORD +24 -22
- spotifywebapipython/const.py +6 -1
- spotifywebapipython/docs/__init__.html +19 -2
- spotifywebapipython/docs/index.html +2 -2
- spotifywebapipython/docs/search.js +1 -1
- spotifywebapipython/docs/spotifywebapiPython/const.html +20 -3
- spotifywebapipython/docs/spotifywebapiPython/saappmessages.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/sautils.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifyapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifyapimessage.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifyauthtoken.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifyclient.html +319 -17
- spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/__init__.py +2 -0
- spotifywebapipython/models/artistinfo.py +263 -0
- spotifywebapipython/models/artistinfotourevent.py +107 -0
- spotifywebapipython/sautils.py +29 -0
- spotifywebapipython/spotifyclient.py +644 -26
- {spotifywebapiPython-1.0.39.dist-info → spotifywebapiPython-1.0.41.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.39.dist-info → spotifywebapiPython-1.0.41.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.39.dist-info → spotifywebapiPython-1.0.41.dist-info}/top_level.txt +0 -0
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
# external package imports.
|
|
2
2
|
import base64
|
|
3
|
+
from datetime import datetime
|
|
3
4
|
import json
|
|
4
5
|
from io import BytesIO
|
|
5
6
|
from oauthlib.oauth2 import BackendApplicationClient, WebApplicationClient
|
|
7
|
+
import time
|
|
6
8
|
from typing import Tuple, Callable
|
|
7
9
|
from urllib3 import PoolManager, Timeout, HTTPResponse
|
|
8
10
|
from urllib.parse import urlencode
|
|
11
|
+
from lxml.etree import fromstring, Element
|
|
9
12
|
|
|
10
13
|
# our package imports.
|
|
11
14
|
from .oauthcli import AuthClient
|
|
12
15
|
from .models import *
|
|
13
16
|
from .models import UserProfile as UserProfileCurrentUser
|
|
14
|
-
from .sautils import export, GetUnixTimestampMSFromUtcNow
|
|
17
|
+
from .sautils import export, GetUnixTimestampMSFromUtcNow, _xmlGetInnerText
|
|
15
18
|
from .saappmessages import SAAppMessages
|
|
16
19
|
from .spotifyapierror import SpotifyApiError
|
|
17
20
|
from .spotifyapimessage import SpotifyApiMessage
|
|
@@ -29,6 +32,7 @@ from .const import (
|
|
|
29
32
|
TRACE_METHOD_RESULT_TYPE_PAGE,
|
|
30
33
|
TRACE_MSG_AUTHTOKEN_CREATE,
|
|
31
34
|
TRACE_MSG_AUTOPAGING_NEXT,
|
|
35
|
+
TRACE_MSG_DELAY_DEVICE,
|
|
32
36
|
TRACE_MSG_USERPROFILE,
|
|
33
37
|
TRACE_WARN_SPOTIFY_SEARCH_NO_MARKET,
|
|
34
38
|
)
|
|
@@ -37,7 +41,7 @@ CACHE_SOURCE_CACHED:str = "cached"
|
|
|
37
41
|
CACHE_SOURCE_CURRENT:str = "current"
|
|
38
42
|
|
|
39
43
|
# get smartinspect logger reference; create a new session for this module name.
|
|
40
|
-
from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext
|
|
44
|
+
from smartinspectpython.siauto import SIAuto, SILevel, SISession, SIMethodParmListContext, SISourceId
|
|
41
45
|
import logging
|
|
42
46
|
_logsi:SISession = SIAuto.Si.GetSession(__name__)
|
|
43
47
|
if (_logsi == None):
|
|
@@ -409,10 +413,7 @@ class SpotifyClient:
|
|
|
409
413
|
# no - treat it as utf-8 encoded data.
|
|
410
414
|
responseUTF8 = response.data.decode('utf-8')
|
|
411
415
|
_logsi.LogText(SILevel.Error, "SpotifyClient http response [%s-%s]: '%s' (utf-8)" % (response.status, response.reason, responseUrl), responseUTF8)
|
|
412
|
-
|
|
413
|
-
# at this point we don't know what Spotify Web Api returned, so let's
|
|
414
|
-
# just raise a new exception with the non-JSON response data.
|
|
415
|
-
#raise SpotifyWebApiError(response.status, responseUTF8, msg.MethodName, response.reason, _logsi)
|
|
416
|
+
responseData = responseUTF8
|
|
416
417
|
|
|
417
418
|
except SpotifyWebApiError: raise # pass handled exceptions on thru
|
|
418
419
|
except Exception as ex:
|
|
@@ -548,6 +549,32 @@ class SpotifyClient:
|
|
|
548
549
|
return result
|
|
549
550
|
|
|
550
551
|
|
|
552
|
+
def _ValidateDelay(self, delay:float, default:float=0.5, maxDelay:float=10) -> float:
|
|
553
|
+
"""
|
|
554
|
+
Validates a delay value.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
delay (int):
|
|
558
|
+
The delay value to validate.
|
|
559
|
+
default (int):
|
|
560
|
+
The default delay value to set if the user-input delay is not valid.
|
|
561
|
+
maxDelay (int):
|
|
562
|
+
The maximum delay value allowed.
|
|
563
|
+
Default is 10.
|
|
564
|
+
"""
|
|
565
|
+
if (isinstance(delay,int)):
|
|
566
|
+
delay = float(delay)
|
|
567
|
+
|
|
568
|
+
if (not isinstance(delay,float)) or (delay < 0):
|
|
569
|
+
result = default
|
|
570
|
+
elif (delay > maxDelay):
|
|
571
|
+
result = maxDelay
|
|
572
|
+
else:
|
|
573
|
+
result = delay
|
|
574
|
+
|
|
575
|
+
return result
|
|
576
|
+
|
|
577
|
+
|
|
551
578
|
def _ValidateMarket(self,
|
|
552
579
|
market:str,
|
|
553
580
|
) -> str:
|
|
@@ -640,7 +667,9 @@ class SpotifyClient:
|
|
|
640
667
|
# formulate the request url.
|
|
641
668
|
url:str = None
|
|
642
669
|
uri:str = msg.Uri
|
|
643
|
-
if (uri == self.SpotifyApiTokenUrl)
|
|
670
|
+
if (uri == self.SpotifyApiTokenUrl) \
|
|
671
|
+
or (uri == self.SpotifyApiAuthorizeUrl) \
|
|
672
|
+
or (uri.startswith('https:')):
|
|
644
673
|
url = uri
|
|
645
674
|
else:
|
|
646
675
|
url = f'{self.SpotifyWebApiUrlBase}{uri}'
|
|
@@ -3168,6 +3197,254 @@ class SpotifyClient:
|
|
|
3168
3197
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
3169
3198
|
|
|
3170
3199
|
|
|
3200
|
+
def GetArtistInfo(self,
|
|
3201
|
+
artistId:str,
|
|
3202
|
+
) -> ArtistInfo:
|
|
3203
|
+
"""
|
|
3204
|
+
Get artist about information from the Spotify Artist Biography page for the
|
|
3205
|
+
specified Spotify artist ID.
|
|
3206
|
+
|
|
3207
|
+
Args:
|
|
3208
|
+
artistId (str):
|
|
3209
|
+
The Spotify ID of the artist.
|
|
3210
|
+
Example: `6APm8EjxOHSYM5B4i3vT3q`
|
|
3211
|
+
|
|
3212
|
+
Returns:
|
|
3213
|
+
An `ArtistInfo` object that contains the artist info details.
|
|
3214
|
+
|
|
3215
|
+
Raises:
|
|
3216
|
+
SpotifyWebApiError:
|
|
3217
|
+
If the Spotify Web API request was for a non-authorization service
|
|
3218
|
+
and the response contains error information.
|
|
3219
|
+
SpotifApiError:
|
|
3220
|
+
If the method fails for any other reason.
|
|
3221
|
+
|
|
3222
|
+
<details>
|
|
3223
|
+
<summary>Sample Code</summary>
|
|
3224
|
+
```python
|
|
3225
|
+
.. include:: ../docs/include/samplecode/SpotifyClient/GetArtistInfo.py
|
|
3226
|
+
```
|
|
3227
|
+
</details>
|
|
3228
|
+
"""
|
|
3229
|
+
apiMethodName:str = 'GetArtistInfo'
|
|
3230
|
+
apiMethodParms:SIMethodParmListContext = None
|
|
3231
|
+
result:ArtistInfo = None
|
|
3232
|
+
|
|
3233
|
+
ABOUT_MONTHLY_LISTENERS:str = ' monthly listeners'
|
|
3234
|
+
|
|
3235
|
+
try:
|
|
3236
|
+
|
|
3237
|
+
# trace.
|
|
3238
|
+
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
3239
|
+
apiMethodParms.AppendKeyValue("artistId", artistId)
|
|
3240
|
+
_logsi.LogMethodParmList(SILevel.Verbose, "Get Spotify catalog information for a single artist", apiMethodParms)
|
|
3241
|
+
|
|
3242
|
+
# execute spotify web api request.
|
|
3243
|
+
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/artists/{id}'.format(id=artistId))
|
|
3244
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
3245
|
+
self.MakeRequest('GET', msg)
|
|
3246
|
+
|
|
3247
|
+
# process results.
|
|
3248
|
+
artist = Artist(root=msg.ResponseData)
|
|
3249
|
+
_logsi.LogObject(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(artist).__name__), artist, excludeNonPublic=True)
|
|
3250
|
+
|
|
3251
|
+
# create base result.
|
|
3252
|
+
result = ArtistInfo(artist.Id, artist.Name, artist.Type, artist.Uri, artist.ImageUrl)
|
|
3253
|
+
|
|
3254
|
+
# does artist have a bio information url? if not then we are done.
|
|
3255
|
+
if (artist.Href is None) or (len(artist.Href.strip()) == 0):
|
|
3256
|
+
_logsi.LogVerbose("No html returned for artist details page: '%s'" % (artist.ExternalUrls.Spotify))
|
|
3257
|
+
return result
|
|
3258
|
+
|
|
3259
|
+
# was a Spotify external url specified for the artist? if not then we are done.
|
|
3260
|
+
if artist.ExternalUrls is None or artist.ExternalUrls.Spotify is None:
|
|
3261
|
+
return artist
|
|
3262
|
+
|
|
3263
|
+
# retrieve artist details page.
|
|
3264
|
+
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, artist.ExternalUrls.Spotify)
|
|
3265
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
3266
|
+
self.MakeRequest('GET', msg)
|
|
3267
|
+
|
|
3268
|
+
# log html response.
|
|
3269
|
+
#_logsi.LogHtml(SILevel.Verbose, "GetArtistInfo response HTML", msg.ResponseData)
|
|
3270
|
+
_logsi.LogSource(SILevel.Verbose, "GetArtistInfo response Text", msg.ResponseData, SISourceId.Html)
|
|
3271
|
+
|
|
3272
|
+
# if no response data then we are done.
|
|
3273
|
+
if not msg.HasResponseData:
|
|
3274
|
+
_logsi.LogVerbose("No html returned for artist details page: '%s'" % (artist.ExternalUrls.Spotify))
|
|
3275
|
+
return result
|
|
3276
|
+
|
|
3277
|
+
# find the starting <html> tag.
|
|
3278
|
+
# ElementTree fromstring method will fail if initial tag is "<!doctype html>".
|
|
3279
|
+
html:str = msg.ResponseData
|
|
3280
|
+
if html is None:
|
|
3281
|
+
return result
|
|
3282
|
+
if not html.startswith('<html'):
|
|
3283
|
+
idx:int = html.find('<html')
|
|
3284
|
+
if idx == -1:
|
|
3285
|
+
_logsi.LogVerbose("<html> tag could not be found for artist details page: '%s'" % (artist.ExternalUrls.Spotify))
|
|
3286
|
+
return result
|
|
3287
|
+
html = html[idx:]
|
|
3288
|
+
_logsi.LogSource(SILevel.Verbose, "GetArtistInfo response Text (starting from '<html')", html, SISourceId.Html)
|
|
3289
|
+
|
|
3290
|
+
# log formatted html response.
|
|
3291
|
+
_logsi.LogXml(SILevel.Verbose, "GetArtistInfo response Text (formatted)", html, prettyPrint=True)
|
|
3292
|
+
|
|
3293
|
+
# load html element tree so we can parse it.
|
|
3294
|
+
doc:Element = fromstring(html)
|
|
3295
|
+
_logsi.LogObject(SILevel.Verbose, "doc Element object", doc)
|
|
3296
|
+
|
|
3297
|
+
attrXPath:str = None
|
|
3298
|
+
elm:Element = None
|
|
3299
|
+
elmChild:Element = None
|
|
3300
|
+
innerText:str = None
|
|
3301
|
+
|
|
3302
|
+
# about information - image (button / bio page):
|
|
3303
|
+
attrXPath = './/button[@type="button"]/descendant-or-self::img'
|
|
3304
|
+
_logsi.LogVerbose("Element search 'image button': '%s'" % attrXPath)
|
|
3305
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3306
|
+
for elm in attrElements:
|
|
3307
|
+
_logsi.LogObject(SILevel.Verbose, "Element match: '%s'" % attrXPath, elm)
|
|
3308
|
+
attrValue:str = elm.get('src', None)
|
|
3309
|
+
if attrValue is not None:
|
|
3310
|
+
result.ImageUrl = attrValue
|
|
3311
|
+
break
|
|
3312
|
+
|
|
3313
|
+
# about information - image (entity-id):
|
|
3314
|
+
# use the internal `_ImageUrl` attribute value to check, as the `ImageUrl` property
|
|
3315
|
+
# will return the `ImageUrlDefault` if `_ImageUrl` attribute was not set.
|
|
3316
|
+
# <img data-testid="artist-entity-image" src="https://i.scdn.co/image/ab6761610000517446196125b56397cd4e0d9c4b" />
|
|
3317
|
+
if result._ImageUrl is None:
|
|
3318
|
+
attrXPath = './/attribute::data-testid[contains(., "artist-entity-image")]/../@src'
|
|
3319
|
+
_logsi.LogVerbose("Element search 'image entity': '%s'" % attrXPath)
|
|
3320
|
+
elmChild = doc.xpath(attrXPath)
|
|
3321
|
+
if elmChild is not None:
|
|
3322
|
+
_logsi.LogVerbose("Element match: '%s'" % elmChild[0])
|
|
3323
|
+
result.ImageUrl = elmChild[0]
|
|
3324
|
+
|
|
3325
|
+
# about information - monthly listeners:
|
|
3326
|
+
# <div data-testid="monthly-listeners-label">2,707,252 monthly listeners</div>
|
|
3327
|
+
attrXPath = './/attribute::data-testid[contains(., "monthly-listeners-label")]/../text()'
|
|
3328
|
+
_logsi.LogVerbose("Element search 'monthly listeners': '%s'" % attrXPath)
|
|
3329
|
+
elmChild = doc.xpath(attrXPath)
|
|
3330
|
+
if elmChild is not None:
|
|
3331
|
+
_logsi.LogVerbose("Element match: '%s'" % elmChild[0])
|
|
3332
|
+
attrValue = elmChild[0].replace(ABOUT_MONTHLY_LISTENERS,'')
|
|
3333
|
+
attrValue = attrValue.replace(',','')
|
|
3334
|
+
attrValue = attrValue.strip()
|
|
3335
|
+
if attrValue.isnumeric:
|
|
3336
|
+
result.MonthlyListeners = int(attrValue)
|
|
3337
|
+
|
|
3338
|
+
# about information - bio:
|
|
3339
|
+
# <div data-testid="expandable-description">
|
|
3340
|
+
# <div>
|
|
3341
|
+
# <div>
|
|
3342
|
+
# <span>MercyMe is a contemporary Christian music band ...</span>
|
|
3343
|
+
# </div>
|
|
3344
|
+
# </div>
|
|
3345
|
+
# </div>
|
|
3346
|
+
attrXPath = './/attribute::data-testid[contains(., "expandable-description")]/..'
|
|
3347
|
+
_logsi.LogVerbose("Element search 'bio': '%s'" % attrXPath)
|
|
3348
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3349
|
+
for elm in attrElements:
|
|
3350
|
+
innerText = _xmlGetInnerText(elm)
|
|
3351
|
+
_logsi.LogObject(SILevel.Verbose, "Element match: '%s' (innerText=%s)" % (attrXPath, innerText), elm)
|
|
3352
|
+
result.Bio = innerText
|
|
3353
|
+
break
|
|
3354
|
+
|
|
3355
|
+
# about information - on tour dates:
|
|
3356
|
+
# <h2>On tour</h2>
|
|
3357
|
+
# <div><ul><li>
|
|
3358
|
+
# <a draggable="false" class="x" href="/concert/1plBKlFD04tlsD1ky081qL">
|
|
3359
|
+
# <time class="x" dateTime="2024-04-04T19:00-07:00">
|
|
3360
|
+
# <h5 class="x" data-encore-id="type">Apr</h5>
|
|
3361
|
+
# <h1 class="x" data-encore-id="type">5</h1>
|
|
3362
|
+
# </time>
|
|
3363
|
+
# <div class="x">
|
|
3364
|
+
# <h3 class="x" data-encore-id="type">MercyMe with David Leonard and Newsboys</h3>
|
|
3365
|
+
# <p class="x" data-encore-id="type"><time class="cR3tL5CgXmgwfJDDKQ2A">Fri, Apr 5</time>accesso ShoWare Center, Kent</p>
|
|
3366
|
+
# </div>
|
|
3367
|
+
# </a>
|
|
3368
|
+
# </li></ul></div>
|
|
3369
|
+
attrXPath = './/h2[text()="On tour"]/following-sibling::*/descendant-or-self::a'
|
|
3370
|
+
_logsi.LogVerbose("Element search 'On tour': '%s'" % attrXPath)
|
|
3371
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3372
|
+
for elm in attrElements:
|
|
3373
|
+
innerText = _xmlGetInnerText(elm)
|
|
3374
|
+
if innerText is not None:
|
|
3375
|
+
if innerText.lower() == 'see all':
|
|
3376
|
+
break
|
|
3377
|
+
_logsi.LogObject(SILevel.Verbose, "Element match: '%s' (innerText=%s)" % (attrXPath, innerText), elm)
|
|
3378
|
+
event:ArtistInfoTourEvent = ArtistInfoTourEvent()
|
|
3379
|
+
elmChild = elm.xpath('.//time/@dateTime')
|
|
3380
|
+
if elmChild is not None:
|
|
3381
|
+
try:
|
|
3382
|
+
attrValue = str(elmChild[0])
|
|
3383
|
+
idx:int = attrValue.rfind('-')
|
|
3384
|
+
if idx > 15: # drop timezone portion of the datetime.
|
|
3385
|
+
event.EventDateTime = datetime.fromisoformat(attrValue[:idx])
|
|
3386
|
+
except Exception as ex:
|
|
3387
|
+
_logsi.LogException("Element value '%s' (%s) could not be parsed to a datetime object" % (elmChild[0], attrValue), ex, logToSystemLogger=False)
|
|
3388
|
+
event.EventDateTime = None
|
|
3389
|
+
elmChild = elm.xpath('.//h3/text()')
|
|
3390
|
+
if elmChild is not None:
|
|
3391
|
+
event.Title = elmChild[0]
|
|
3392
|
+
elmChild = elm.xpath('.//p/text()')
|
|
3393
|
+
if elmChild is not None:
|
|
3394
|
+
event.VenueName = elmChild[0]
|
|
3395
|
+
result.TourEvents.append(event)
|
|
3396
|
+
_logsi.LogObject(SILevel.Verbose, '%s: "%s" (%s)' % (type(event).__name__, event.Title, event.EventDateTime), event, excludeNonPublic=True)
|
|
3397
|
+
|
|
3398
|
+
# about information - links.
|
|
3399
|
+
result.AboutUrlFacebook = self._GetArtistInfoAboutLink(doc,"Facebook")
|
|
3400
|
+
result.AboutUrlInstagram = self._GetArtistInfoAboutLink(doc,"Instagram")
|
|
3401
|
+
result.AboutUrlTwitter = self._GetArtistInfoAboutLink(doc,"Twitter")
|
|
3402
|
+
result.AboutUrlWikipedia = self._GetArtistInfoAboutLink(doc,"Wikipedia")
|
|
3403
|
+
|
|
3404
|
+
# trace.
|
|
3405
|
+
_logsi.LogObject(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(result).__name__), result, excludeNonPublic=True)
|
|
3406
|
+
return result
|
|
3407
|
+
|
|
3408
|
+
except SpotifyWebApiError: raise # pass handled exceptions on thru
|
|
3409
|
+
except SpotifyWebApiAuthenticationError: raise # pass handled exceptions on thru
|
|
3410
|
+
except Exception as ex:
|
|
3411
|
+
|
|
3412
|
+
# format unhandled exception.
|
|
3413
|
+
raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
|
|
3414
|
+
|
|
3415
|
+
finally:
|
|
3416
|
+
|
|
3417
|
+
# trace.
|
|
3418
|
+
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
3419
|
+
|
|
3420
|
+
|
|
3421
|
+
def _GetArtistInfoAboutLink(self,
|
|
3422
|
+
doc:Element,
|
|
3423
|
+
linkTitle:str,
|
|
3424
|
+
) -> str:
|
|
3425
|
+
"""
|
|
3426
|
+
Searches artist about page for the specified link title and returns the HREF
|
|
3427
|
+
attribute value if found; otherwise, null is returned.
|
|
3428
|
+
"""
|
|
3429
|
+
result:str = None
|
|
3430
|
+
|
|
3431
|
+
# about information - link.
|
|
3432
|
+
# <a href="https://facebook.com/mercyme">
|
|
3433
|
+
# <div>Facebook</div>
|
|
3434
|
+
# </a>
|
|
3435
|
+
attrXPath:str = './/div[text()="%s"]/parent::a' % linkTitle
|
|
3436
|
+
_logsi.LogVerbose("Element search '%s link': '%s'" % (linkTitle, attrXPath))
|
|
3437
|
+
attrElements:list[Element] = doc.xpath(attrXPath)
|
|
3438
|
+
for elm in attrElements:
|
|
3439
|
+
_logsi.LogObject(SILevel.Verbose, "Element match: '%s'" % attrXPath, elm)
|
|
3440
|
+
attrValue:str = elm.get('href', None)
|
|
3441
|
+
if attrValue is not None:
|
|
3442
|
+
result = attrValue
|
|
3443
|
+
break
|
|
3444
|
+
|
|
3445
|
+
return result
|
|
3446
|
+
|
|
3447
|
+
|
|
3171
3448
|
def GetArtistRelatedArtists(self,
|
|
3172
3449
|
artistId:str,
|
|
3173
3450
|
) -> list[Artist]:
|
|
@@ -5689,7 +5966,8 @@ class SpotifyClient:
|
|
|
5689
5966
|
properly handles cases of new types in the future by checking against the type field of each object.
|
|
5690
5967
|
|
|
5691
5968
|
Returns:
|
|
5692
|
-
A `PlayerPlayState` object that contains
|
|
5969
|
+
A `PlayerPlayState` object that contains player state details as well as
|
|
5970
|
+
currently playing content.
|
|
5693
5971
|
|
|
5694
5972
|
Raises:
|
|
5695
5973
|
SpotifyWebApiError:
|
|
@@ -5780,7 +6058,8 @@ class SpotifyClient:
|
|
|
5780
6058
|
properly handles cases of new types in the future by checking against the type field of each object.
|
|
5781
6059
|
|
|
5782
6060
|
Returns:
|
|
5783
|
-
A `PlayerPlayState` object that contains
|
|
6061
|
+
A `PlayerPlayState` object that contains player state details as well as
|
|
6062
|
+
currently playing content.
|
|
5784
6063
|
|
|
5785
6064
|
Raises:
|
|
5786
6065
|
SpotifyWebApiError:
|
|
@@ -8579,7 +8858,8 @@ class SpotifyClient:
|
|
|
8579
8858
|
|
|
8580
8859
|
|
|
8581
8860
|
def PlayerMediaPause(self,
|
|
8582
|
-
deviceId:str=None
|
|
8861
|
+
deviceId:str=None,
|
|
8862
|
+
delay:float=0.50
|
|
8583
8863
|
) -> None:
|
|
8584
8864
|
"""
|
|
8585
8865
|
Pause media play for the specified Spotify Connect device.
|
|
@@ -8591,7 +8871,12 @@ class SpotifyClient:
|
|
|
8591
8871
|
The id of the device this command is targeting.
|
|
8592
8872
|
If not supplied, the user's currently active device is the target.
|
|
8593
8873
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
8594
|
-
|
|
8874
|
+
delay (float):
|
|
8875
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
8876
|
+
This delay will give the spotify web api time to process the change before
|
|
8877
|
+
another command is issued.
|
|
8878
|
+
Default is 0.50; value range is 0 - 10.
|
|
8879
|
+
|
|
8595
8880
|
Raises:
|
|
8596
8881
|
SpotifyWebApiError:
|
|
8597
8882
|
If the Spotify Web API request was for a non-authorization service
|
|
@@ -8618,8 +8903,12 @@ class SpotifyClient:
|
|
|
8618
8903
|
# trace.
|
|
8619
8904
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
8620
8905
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
8906
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
8621
8907
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device pause playback", apiMethodParms)
|
|
8622
8908
|
|
|
8909
|
+
# validations.
|
|
8910
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
8911
|
+
|
|
8623
8912
|
# build spotify web api request parameters.
|
|
8624
8913
|
urlParms:dict = {}
|
|
8625
8914
|
if deviceId is not None:
|
|
@@ -8631,6 +8920,11 @@ class SpotifyClient:
|
|
|
8631
8920
|
msg.UrlParameters = urlParms
|
|
8632
8921
|
self.MakeRequest('PUT', msg)
|
|
8633
8922
|
|
|
8923
|
+
# give spotify web api time to process the change.
|
|
8924
|
+
if delay > 0:
|
|
8925
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
8926
|
+
time.sleep(delay)
|
|
8927
|
+
|
|
8634
8928
|
# process results.
|
|
8635
8929
|
# no results to process - this is pass or fail.
|
|
8636
8930
|
return
|
|
@@ -8653,7 +8947,8 @@ class SpotifyClient:
|
|
|
8653
8947
|
offsetUri:str=None,
|
|
8654
8948
|
offsetPosition:int=None,
|
|
8655
8949
|
positionMS:int=0,
|
|
8656
|
-
deviceId:str=None
|
|
8950
|
+
deviceId:str=None,
|
|
8951
|
+
delay:float=0.50
|
|
8657
8952
|
) -> None:
|
|
8658
8953
|
"""
|
|
8659
8954
|
Start playing one or more tracks of the specified context on a Spotify Connect device.
|
|
@@ -8687,6 +8982,11 @@ class SpotifyClient:
|
|
|
8687
8982
|
The id of the device this command is targeting.
|
|
8688
8983
|
If not supplied, the user's currently active device is the target.
|
|
8689
8984
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
8985
|
+
delay (float):
|
|
8986
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
8987
|
+
This delay will give the spotify web api time to process the change before
|
|
8988
|
+
another command is issued.
|
|
8989
|
+
Default is 0.50; value range is 0 - 10.
|
|
8690
8990
|
|
|
8691
8991
|
Raises:
|
|
8692
8992
|
SpotifyWebApiError:
|
|
@@ -8732,8 +9032,12 @@ class SpotifyClient:
|
|
|
8732
9032
|
apiMethodParms.AppendKeyValue("offsetPosition", offsetPosition)
|
|
8733
9033
|
apiMethodParms.AppendKeyValue("positionMS", positionMS)
|
|
8734
9034
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9035
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
8735
9036
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play context", apiMethodParms)
|
|
8736
9037
|
|
|
9038
|
+
# validations.
|
|
9039
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9040
|
+
|
|
8737
9041
|
# build spotify web api request parameters.
|
|
8738
9042
|
reqData:dict = \
|
|
8739
9043
|
{
|
|
@@ -8756,6 +9060,102 @@ class SpotifyClient:
|
|
|
8756
9060
|
msg.RequestJson = reqData
|
|
8757
9061
|
self.MakeRequest('PUT', msg)
|
|
8758
9062
|
|
|
9063
|
+
# give spotify web api time to process the change.
|
|
9064
|
+
if delay > 0:
|
|
9065
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9066
|
+
time.sleep(delay)
|
|
9067
|
+
|
|
9068
|
+
# process results.
|
|
9069
|
+
# no results to process - this is pass or fail.
|
|
9070
|
+
return
|
|
9071
|
+
|
|
9072
|
+
except SpotifyWebApiError: raise # pass handled exceptions on thru
|
|
9073
|
+
except SpotifyWebApiAuthenticationError: raise # pass handled exceptions on thru
|
|
9074
|
+
except Exception as ex:
|
|
9075
|
+
|
|
9076
|
+
# format unhandled exception.
|
|
9077
|
+
raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
|
|
9078
|
+
|
|
9079
|
+
finally:
|
|
9080
|
+
|
|
9081
|
+
# trace.
|
|
9082
|
+
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
9083
|
+
|
|
9084
|
+
|
|
9085
|
+
def PlayerMediaPlayTrackFavorites(self,
|
|
9086
|
+
deviceId:str=None,
|
|
9087
|
+
shuffle:bool=True,
|
|
9088
|
+
delay:float=0.50
|
|
9089
|
+
) -> None:
|
|
9090
|
+
"""
|
|
9091
|
+
Get a list of the tracks saved in the current Spotify user's 'Your Library'
|
|
9092
|
+
and starts playing them.
|
|
9093
|
+
|
|
9094
|
+
Args:
|
|
9095
|
+
deviceId (str):
|
|
9096
|
+
The id of the device this command is targeting.
|
|
9097
|
+
If not supplied, the user's currently active device is the target.
|
|
9098
|
+
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9099
|
+
shuffle (bool):
|
|
9100
|
+
True to set player shuffle mode to on; otherwise, False for no shuffle.
|
|
9101
|
+
delay (float):
|
|
9102
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9103
|
+
This delay will give the spotify web api time to process the change before
|
|
9104
|
+
another command is issued.
|
|
9105
|
+
Default is 0.50; value range is 0 - 10.
|
|
9106
|
+
|
|
9107
|
+
Raises:
|
|
9108
|
+
SpotifyWebApiError:
|
|
9109
|
+
If the Spotify Web API request was for a non-authorization service
|
|
9110
|
+
and the response contains error information.
|
|
9111
|
+
SpotifApiError:
|
|
9112
|
+
If the method fails for any other reason.
|
|
9113
|
+
|
|
9114
|
+
This API only works for users who have Spotify Premium.
|
|
9115
|
+
|
|
9116
|
+
This method simply calls the `GetTrackFavorites` method to retrieve the current users
|
|
9117
|
+
favorite tracks (200 max), then calls the `PlayerMediaPlayTracks` method to play them.
|
|
9118
|
+
|
|
9119
|
+
<details>
|
|
9120
|
+
<summary>Sample Code</summary>
|
|
9121
|
+
```python
|
|
9122
|
+
.. include:: ../docs/include/samplecode/SpotifyClient/PlayerMediaPlayTrackFavorites.py
|
|
9123
|
+
```
|
|
9124
|
+
</details>
|
|
9125
|
+
"""
|
|
9126
|
+
apiMethodName:str = 'PlayerMediaPlayTrackFavorites'
|
|
9127
|
+
apiMethodParms:SIMethodParmListContext = None
|
|
9128
|
+
|
|
9129
|
+
try:
|
|
9130
|
+
|
|
9131
|
+
# trace.
|
|
9132
|
+
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9133
|
+
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9134
|
+
apiMethodParms.AppendKeyValue("shuffle", shuffle)
|
|
9135
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9136
|
+
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play track favorites", apiMethodParms)
|
|
9137
|
+
|
|
9138
|
+
# validations.
|
|
9139
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9140
|
+
|
|
9141
|
+
# get current users favorite tracks.
|
|
9142
|
+
tracks:TrackPageSaved = self.GetTrackFavorites(limitTotal=200)
|
|
9143
|
+
if (tracks.ItemsCount == 0):
|
|
9144
|
+
_logsi.LogVerbose("Current user has no favorite tracks; nothing to do")
|
|
9145
|
+
return
|
|
9146
|
+
|
|
9147
|
+
# build a list of all item uri's.
|
|
9148
|
+
arrUris:list[str] = []
|
|
9149
|
+
trackSaved:TrackSaved
|
|
9150
|
+
for trackSaved in tracks.Items:
|
|
9151
|
+
arrUris.append(trackSaved.Track.Uri)
|
|
9152
|
+
|
|
9153
|
+
# play the tracks.
|
|
9154
|
+
self.PlayerMediaPlayTracks(arrUris, deviceId=deviceId, delay=delay)
|
|
9155
|
+
|
|
9156
|
+
# set desired shuffle mode.
|
|
9157
|
+
self.PlayerSetShuffleMode(shuffle, deviceId)
|
|
9158
|
+
|
|
8759
9159
|
# process results.
|
|
8760
9160
|
# no results to process - this is pass or fail.
|
|
8761
9161
|
return
|
|
@@ -8776,7 +9176,8 @@ class SpotifyClient:
|
|
|
8776
9176
|
def PlayerMediaPlayTracks(self,
|
|
8777
9177
|
uris:list[str],
|
|
8778
9178
|
positionMS:int=0,
|
|
8779
|
-
deviceId:str=None
|
|
9179
|
+
deviceId:str=None,
|
|
9180
|
+
delay:float=0.50
|
|
8780
9181
|
) -> None:
|
|
8781
9182
|
"""
|
|
8782
9183
|
Start playing one or more tracks on the specified Spotify Connect device.
|
|
@@ -8800,6 +9201,11 @@ class SpotifyClient:
|
|
|
8800
9201
|
The id of the device this command is targeting.
|
|
8801
9202
|
If not supplied, the user's currently active device is the target.
|
|
8802
9203
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9204
|
+
delay (float):
|
|
9205
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9206
|
+
This delay will give the spotify web api time to process the change before
|
|
9207
|
+
another command is issued.
|
|
9208
|
+
Default is 0.50; value range is 0 - 10.
|
|
8803
9209
|
|
|
8804
9210
|
Raises:
|
|
8805
9211
|
SpotifyWebApiError:
|
|
@@ -8829,8 +9235,12 @@ class SpotifyClient:
|
|
|
8829
9235
|
apiMethodParms.AppendKeyValue("uris", uris)
|
|
8830
9236
|
apiMethodParms.AppendKeyValue("positionMS", positionMS)
|
|
8831
9237
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9238
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
8832
9239
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play tracks", apiMethodParms)
|
|
8833
9240
|
|
|
9241
|
+
# validations.
|
|
9242
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9243
|
+
|
|
8834
9244
|
# build a list of all item uri's.
|
|
8835
9245
|
# remove any leading / trailing spaces in case user put a space between the items.
|
|
8836
9246
|
arrUris:list[str] = None
|
|
@@ -8860,6 +9270,11 @@ class SpotifyClient:
|
|
|
8860
9270
|
msg.RequestJson = reqData
|
|
8861
9271
|
self.MakeRequest('PUT', msg)
|
|
8862
9272
|
|
|
9273
|
+
# give spotify web api time to process the change.
|
|
9274
|
+
if delay > 0:
|
|
9275
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9276
|
+
time.sleep(delay)
|
|
9277
|
+
|
|
8863
9278
|
# process results.
|
|
8864
9279
|
# no results to process - this is pass or fail.
|
|
8865
9280
|
return
|
|
@@ -8878,7 +9293,8 @@ class SpotifyClient:
|
|
|
8878
9293
|
|
|
8879
9294
|
|
|
8880
9295
|
def PlayerMediaResume(self,
|
|
8881
|
-
deviceId:str=None
|
|
9296
|
+
deviceId:str=None,
|
|
9297
|
+
delay:float=0.50
|
|
8882
9298
|
) -> None:
|
|
8883
9299
|
"""
|
|
8884
9300
|
Resume media play for the specified Spotify Connect device.
|
|
@@ -8890,6 +9306,11 @@ class SpotifyClient:
|
|
|
8890
9306
|
The id of the device this command is targeting.
|
|
8891
9307
|
If not supplied, the user's currently active device is the target.
|
|
8892
9308
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9309
|
+
delay (float):
|
|
9310
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9311
|
+
This delay will give the spotify web api time to process the change before
|
|
9312
|
+
another command is issued.
|
|
9313
|
+
Default is 0.50; value range is 0 - 10.
|
|
8893
9314
|
|
|
8894
9315
|
Raises:
|
|
8895
9316
|
SpotifyWebApiError:
|
|
@@ -8917,8 +9338,13 @@ class SpotifyClient:
|
|
|
8917
9338
|
# trace.
|
|
8918
9339
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
8919
9340
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9341
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
8920
9342
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device resume playback", apiMethodParms)
|
|
8921
9343
|
|
|
9344
|
+
# validations.
|
|
9345
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9346
|
+
|
|
9347
|
+
# build spotify web api request parameters.
|
|
8922
9348
|
urlParms:dict = {}
|
|
8923
9349
|
if deviceId is not None:
|
|
8924
9350
|
urlParms['device_id'] = deviceId
|
|
@@ -8929,6 +9355,11 @@ class SpotifyClient:
|
|
|
8929
9355
|
msg.UrlParameters = urlParms
|
|
8930
9356
|
self.MakeRequest('PUT', msg)
|
|
8931
9357
|
|
|
9358
|
+
# give spotify web api time to process the change.
|
|
9359
|
+
if delay > 0:
|
|
9360
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9361
|
+
time.sleep(delay)
|
|
9362
|
+
|
|
8932
9363
|
# process results.
|
|
8933
9364
|
# no results to process - this is pass or fail.
|
|
8934
9365
|
return
|
|
@@ -8948,7 +9379,8 @@ class SpotifyClient:
|
|
|
8948
9379
|
|
|
8949
9380
|
def PlayerMediaSeek(self,
|
|
8950
9381
|
positionMS:int=0,
|
|
8951
|
-
deviceId:str=None
|
|
9382
|
+
deviceId:str=None,
|
|
9383
|
+
delay:float=0.50
|
|
8952
9384
|
) -> None:
|
|
8953
9385
|
"""
|
|
8954
9386
|
Seeks to the given position in the user's currently playing track for the specified
|
|
@@ -8966,6 +9398,11 @@ class SpotifyClient:
|
|
|
8966
9398
|
The id of the device this command is targeting.
|
|
8967
9399
|
If not supplied, the user's currently active device is the target.
|
|
8968
9400
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9401
|
+
delay (float):
|
|
9402
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9403
|
+
This delay will give the spotify web api time to process the change before
|
|
9404
|
+
another command is issued.
|
|
9405
|
+
Default is 0.50; value range is 0 - 10.
|
|
8969
9406
|
|
|
8970
9407
|
Raises:
|
|
8971
9408
|
SpotifyWebApiError:
|
|
@@ -8994,8 +9431,13 @@ class SpotifyClient:
|
|
|
8994
9431
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
8995
9432
|
apiMethodParms.AppendKeyValue("positionMS", positionMS)
|
|
8996
9433
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9434
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
8997
9435
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device seek position", apiMethodParms)
|
|
8998
9436
|
|
|
9437
|
+
# validations.
|
|
9438
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9439
|
+
|
|
9440
|
+
# build spotify web api request parameters.
|
|
8999
9441
|
urlParms:dict = \
|
|
9000
9442
|
{
|
|
9001
9443
|
'position_ms': positionMS
|
|
@@ -9009,6 +9451,11 @@ class SpotifyClient:
|
|
|
9009
9451
|
msg.UrlParameters = urlParms
|
|
9010
9452
|
self.MakeRequest('PUT', msg)
|
|
9011
9453
|
|
|
9454
|
+
# give spotify web api time to process the change.
|
|
9455
|
+
if delay > 0:
|
|
9456
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9457
|
+
time.sleep(delay)
|
|
9458
|
+
|
|
9012
9459
|
# process results.
|
|
9013
9460
|
# no results to process - this is pass or fail.
|
|
9014
9461
|
return
|
|
@@ -9027,7 +9474,8 @@ class SpotifyClient:
|
|
|
9027
9474
|
|
|
9028
9475
|
|
|
9029
9476
|
def PlayerMediaSkipNext(self,
|
|
9030
|
-
deviceId:str=None
|
|
9477
|
+
deviceId:str=None,
|
|
9478
|
+
delay:float=0.50
|
|
9031
9479
|
) -> None:
|
|
9032
9480
|
"""
|
|
9033
9481
|
Skips to next track in the user's queue for the specified Spotify Connect device.
|
|
@@ -9039,6 +9487,11 @@ class SpotifyClient:
|
|
|
9039
9487
|
The id of the device this command is targeting.
|
|
9040
9488
|
If not supplied, the user's currently active device is the target.
|
|
9041
9489
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9490
|
+
delay (float):
|
|
9491
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9492
|
+
This delay will give the spotify web api time to process the change before
|
|
9493
|
+
another command is issued.
|
|
9494
|
+
Default is 0.50; value range is 0 - 10.
|
|
9042
9495
|
|
|
9043
9496
|
Raises:
|
|
9044
9497
|
SpotifyWebApiError:
|
|
@@ -9066,8 +9519,13 @@ class SpotifyClient:
|
|
|
9066
9519
|
# trace.
|
|
9067
9520
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9068
9521
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9522
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9069
9523
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device skip next", apiMethodParms)
|
|
9070
9524
|
|
|
9525
|
+
# validations.
|
|
9526
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9527
|
+
|
|
9528
|
+
# build spotify web api request parameters.
|
|
9071
9529
|
urlParms:dict = {}
|
|
9072
9530
|
if deviceId is not None:
|
|
9073
9531
|
urlParms['device_id'] = deviceId
|
|
@@ -9078,6 +9536,11 @@ class SpotifyClient:
|
|
|
9078
9536
|
msg.UrlParameters = urlParms
|
|
9079
9537
|
self.MakeRequest('POST', msg)
|
|
9080
9538
|
|
|
9539
|
+
# give spotify web api time to process the change.
|
|
9540
|
+
if delay > 0:
|
|
9541
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9542
|
+
time.sleep(delay)
|
|
9543
|
+
|
|
9081
9544
|
# process results.
|
|
9082
9545
|
# no results to process - this is pass or fail.
|
|
9083
9546
|
return
|
|
@@ -9096,7 +9559,8 @@ class SpotifyClient:
|
|
|
9096
9559
|
|
|
9097
9560
|
|
|
9098
9561
|
def PlayerMediaSkipPrevious(self,
|
|
9099
|
-
deviceId:str=None
|
|
9562
|
+
deviceId:str=None,
|
|
9563
|
+
delay:float=0.50
|
|
9100
9564
|
) -> None:
|
|
9101
9565
|
"""
|
|
9102
9566
|
Skips to previous track in the user's queue for the specified Spotify Connect device.
|
|
@@ -9108,6 +9572,11 @@ class SpotifyClient:
|
|
|
9108
9572
|
The id of the device this command is targeting.
|
|
9109
9573
|
If not supplied, the user's currently active device is the target.
|
|
9110
9574
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9575
|
+
delay (float):
|
|
9576
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9577
|
+
This delay will give the spotify web api time to process the change before
|
|
9578
|
+
another command is issued.
|
|
9579
|
+
Default is 0.50; value range is 0 - 10.
|
|
9111
9580
|
|
|
9112
9581
|
Raises:
|
|
9113
9582
|
SpotifyWebApiError:
|
|
@@ -9135,8 +9604,13 @@ class SpotifyClient:
|
|
|
9135
9604
|
# trace.
|
|
9136
9605
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9137
9606
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9607
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9138
9608
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device skip previous", apiMethodParms)
|
|
9139
9609
|
|
|
9610
|
+
# validations.
|
|
9611
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9612
|
+
|
|
9613
|
+
# build spotify web api request parameters.
|
|
9140
9614
|
urlParms:dict = {}
|
|
9141
9615
|
if deviceId is not None:
|
|
9142
9616
|
urlParms['device_id'] = deviceId
|
|
@@ -9147,6 +9621,11 @@ class SpotifyClient:
|
|
|
9147
9621
|
msg.UrlParameters = urlParms
|
|
9148
9622
|
self.MakeRequest('POST', msg)
|
|
9149
9623
|
|
|
9624
|
+
# give spotify web api time to process the change.
|
|
9625
|
+
if delay > 0:
|
|
9626
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9627
|
+
time.sleep(delay)
|
|
9628
|
+
|
|
9150
9629
|
# process results.
|
|
9151
9630
|
# no results to process - this is pass or fail.
|
|
9152
9631
|
return
|
|
@@ -9166,7 +9645,8 @@ class SpotifyClient:
|
|
|
9166
9645
|
|
|
9167
9646
|
def PlayerSetRepeatMode(self,
|
|
9168
9647
|
state:str='off',
|
|
9169
|
-
deviceId:str=None
|
|
9648
|
+
deviceId:str=None,
|
|
9649
|
+
delay:float=0.50
|
|
9170
9650
|
) -> None:
|
|
9171
9651
|
"""
|
|
9172
9652
|
Set repeat mode for the specified Spotify Connect device.
|
|
@@ -9184,6 +9664,11 @@ class SpotifyClient:
|
|
|
9184
9664
|
The id of the device this command is targeting.
|
|
9185
9665
|
If not supplied, the user's currently active device is the target.
|
|
9186
9666
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9667
|
+
delay (float):
|
|
9668
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9669
|
+
This delay will give the spotify web api time to process the change before
|
|
9670
|
+
another command is issued.
|
|
9671
|
+
Default is 0.50; value range is 0 - 10.
|
|
9187
9672
|
|
|
9188
9673
|
Raises:
|
|
9189
9674
|
SpotifyWebApiError:
|
|
@@ -9212,8 +9697,12 @@ class SpotifyClient:
|
|
|
9212
9697
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9213
9698
|
apiMethodParms.AppendKeyValue("state", state)
|
|
9214
9699
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9700
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9215
9701
|
_logsi.LogMethodParmList(SILevel.Verbose, "Set Spotify Connect device set repeat mode", apiMethodParms)
|
|
9216
9702
|
|
|
9703
|
+
# validations.
|
|
9704
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9705
|
+
|
|
9217
9706
|
# build spotify web api request parameters.
|
|
9218
9707
|
urlParms:dict = \
|
|
9219
9708
|
{
|
|
@@ -9228,6 +9717,11 @@ class SpotifyClient:
|
|
|
9228
9717
|
msg.UrlParameters = urlParms
|
|
9229
9718
|
self.MakeRequest('PUT', msg)
|
|
9230
9719
|
|
|
9720
|
+
# give spotify web api time to process the change.
|
|
9721
|
+
if delay > 0:
|
|
9722
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9723
|
+
time.sleep(delay)
|
|
9724
|
+
|
|
9231
9725
|
# process results.
|
|
9232
9726
|
# no results to process - this is pass or fail.
|
|
9233
9727
|
return
|
|
@@ -9247,7 +9741,8 @@ class SpotifyClient:
|
|
|
9247
9741
|
|
|
9248
9742
|
def PlayerSetShuffleMode(self,
|
|
9249
9743
|
state:bool=False,
|
|
9250
|
-
deviceId:str=None
|
|
9744
|
+
deviceId:str=None,
|
|
9745
|
+
delay:float=0.50
|
|
9251
9746
|
) -> None:
|
|
9252
9747
|
"""
|
|
9253
9748
|
Set shuffle mode for the specified Spotify Connect device.
|
|
@@ -9264,6 +9759,11 @@ class SpotifyClient:
|
|
|
9264
9759
|
The id of the device this command is targeting.
|
|
9265
9760
|
If not supplied, the user's currently active device is the target.
|
|
9266
9761
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9762
|
+
delay (float):
|
|
9763
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9764
|
+
This delay will give the spotify web api time to process the change before
|
|
9765
|
+
another command is issued.
|
|
9766
|
+
Default is 0.50; value range is 0 - 10.
|
|
9267
9767
|
|
|
9268
9768
|
Raises:
|
|
9269
9769
|
SpotifyWebApiError:
|
|
@@ -9292,8 +9792,12 @@ class SpotifyClient:
|
|
|
9292
9792
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9293
9793
|
apiMethodParms.AppendKeyValue("state", state)
|
|
9294
9794
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9795
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9295
9796
|
_logsi.LogMethodParmList(SILevel.Verbose, "Set Spotify Connect device set shuffle mode", apiMethodParms)
|
|
9296
9797
|
|
|
9798
|
+
# validations.
|
|
9799
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9800
|
+
|
|
9297
9801
|
# build spotify web api request parameters.
|
|
9298
9802
|
urlParms:dict = \
|
|
9299
9803
|
{
|
|
@@ -9308,6 +9812,11 @@ class SpotifyClient:
|
|
|
9308
9812
|
msg.UrlParameters = urlParms
|
|
9309
9813
|
self.MakeRequest('PUT', msg)
|
|
9310
9814
|
|
|
9815
|
+
# give spotify web api time to process the change.
|
|
9816
|
+
if delay > 0:
|
|
9817
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9818
|
+
time.sleep(delay)
|
|
9819
|
+
|
|
9311
9820
|
# process results.
|
|
9312
9821
|
# no results to process - this is pass or fail.
|
|
9313
9822
|
return
|
|
@@ -9327,7 +9836,8 @@ class SpotifyClient:
|
|
|
9327
9836
|
|
|
9328
9837
|
def PlayerSetVolume(self,
|
|
9329
9838
|
volumePercent:int,
|
|
9330
|
-
deviceId:str=None
|
|
9839
|
+
deviceId:str=None,
|
|
9840
|
+
delay:float=0.50
|
|
9331
9841
|
) -> None:
|
|
9332
9842
|
"""
|
|
9333
9843
|
Set volume level for the specified Spotify Connect device.
|
|
@@ -9343,6 +9853,11 @@ class SpotifyClient:
|
|
|
9343
9853
|
The id of the device this command is targeting.
|
|
9344
9854
|
If not supplied, the user's currently active device is the target.
|
|
9345
9855
|
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
9856
|
+
delay (float):
|
|
9857
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9858
|
+
This delay will give the spotify web api time to process the change before
|
|
9859
|
+
another command is issued.
|
|
9860
|
+
Default is 0.50; value range is 0 - 10.
|
|
9346
9861
|
|
|
9347
9862
|
Raises:
|
|
9348
9863
|
SpotifyWebApiError:
|
|
@@ -9371,8 +9886,12 @@ class SpotifyClient:
|
|
|
9371
9886
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9372
9887
|
apiMethodParms.AppendKeyValue("volumePercent", volumePercent)
|
|
9373
9888
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9889
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9374
9890
|
_logsi.LogMethodParmList(SILevel.Verbose, "Set Spotify Connect device set volume level", apiMethodParms)
|
|
9375
9891
|
|
|
9892
|
+
# validations.
|
|
9893
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9894
|
+
|
|
9376
9895
|
# build spotify web api request parameters.
|
|
9377
9896
|
urlParms:dict = \
|
|
9378
9897
|
{
|
|
@@ -9387,6 +9906,11 @@ class SpotifyClient:
|
|
|
9387
9906
|
msg.UrlParameters = urlParms
|
|
9388
9907
|
self.MakeRequest('PUT', msg)
|
|
9389
9908
|
|
|
9909
|
+
# give spotify web api time to process the change.
|
|
9910
|
+
if delay > 0:
|
|
9911
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
9912
|
+
time.sleep(delay)
|
|
9913
|
+
|
|
9390
9914
|
# process results.
|
|
9391
9915
|
# no results to process - this is pass or fail.
|
|
9392
9916
|
return
|
|
@@ -9407,6 +9931,7 @@ class SpotifyClient:
|
|
|
9407
9931
|
def PlayerTransferPlayback(self,
|
|
9408
9932
|
deviceId:str=None,
|
|
9409
9933
|
play:bool=True,
|
|
9934
|
+
delay:float=0.50
|
|
9410
9935
|
) -> None:
|
|
9411
9936
|
"""
|
|
9412
9937
|
Transfer playback to a new Spotify Connect device and optionally begin playback.
|
|
@@ -9422,6 +9947,11 @@ class SpotifyClient:
|
|
|
9422
9947
|
- `True` - ensure playback happens on new device.
|
|
9423
9948
|
- `False` - keep the current playback state.
|
|
9424
9949
|
Default: `True`
|
|
9950
|
+
delay (float):
|
|
9951
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
9952
|
+
This delay will give the spotify web api time to process the change before
|
|
9953
|
+
another command is issued.
|
|
9954
|
+
Default is 0.50; value range is 0 - 10.
|
|
9425
9955
|
|
|
9426
9956
|
Raises:
|
|
9427
9957
|
SpotifyWebApiError:
|
|
@@ -9450,8 +9980,12 @@ class SpotifyClient:
|
|
|
9450
9980
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9451
9981
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9452
9982
|
apiMethodParms.AppendKeyValue("play", play)
|
|
9983
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9453
9984
|
_logsi.LogMethodParmList(SILevel.Verbose, "Transfer playback to a new Spotify Connect device", apiMethodParms)
|
|
9454
|
-
|
|
9985
|
+
|
|
9986
|
+
# validations.
|
|
9987
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
9988
|
+
|
|
9455
9989
|
# build spotify web api request parameters.
|
|
9456
9990
|
reqData:dict = \
|
|
9457
9991
|
{
|
|
@@ -9466,6 +10000,11 @@ class SpotifyClient:
|
|
|
9466
10000
|
msg.RequestJson = reqData
|
|
9467
10001
|
self.MakeRequest('PUT', msg)
|
|
9468
10002
|
|
|
10003
|
+
# give spotify web api time to process the change.
|
|
10004
|
+
if delay > 0:
|
|
10005
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
10006
|
+
time.sleep(delay)
|
|
10007
|
+
|
|
9469
10008
|
# process results.
|
|
9470
10009
|
# no results to process - this is pass or fail.
|
|
9471
10010
|
return
|
|
@@ -9486,6 +10025,7 @@ class SpotifyClient:
|
|
|
9486
10025
|
def PlayerVerifyDeviceDefault(self,
|
|
9487
10026
|
defaultDeviceId:str=None,
|
|
9488
10027
|
play:bool=False,
|
|
10028
|
+
delay:float=0.50
|
|
9489
10029
|
) -> PlayerPlayState:
|
|
9490
10030
|
"""
|
|
9491
10031
|
Checks to see if there is an active Spotify Connect device, and if not to make one active.
|
|
@@ -9505,9 +10045,15 @@ class SpotifyClient:
|
|
|
9505
10045
|
- `True` - ensure playback happens on new device.
|
|
9506
10046
|
- `False` - keep the current playback state.
|
|
9507
10047
|
Default: `False`
|
|
10048
|
+
delay (float):
|
|
10049
|
+
Time delay (in seconds) to wait AFTER issuing the command to the player.
|
|
10050
|
+
This delay will give the spotify web api time to process the change before
|
|
10051
|
+
another command is issued.
|
|
10052
|
+
Default is 0.50; value range is 0 - 10.
|
|
9508
10053
|
|
|
9509
10054
|
Returns:
|
|
9510
|
-
A `PlayerPlayState` object that contains
|
|
10055
|
+
A `PlayerPlayState` object that contains player state details as well as
|
|
10056
|
+
currently playing content.
|
|
9511
10057
|
|
|
9512
10058
|
Raises:
|
|
9513
10059
|
SpotifyWebApiError:
|
|
@@ -9533,20 +10079,24 @@ class SpotifyClient:
|
|
|
9533
10079
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
9534
10080
|
apiMethodParms.AppendKeyValue("defaultDeviceId", defaultDeviceId)
|
|
9535
10081
|
apiMethodParms.AppendKeyValue("play", play)
|
|
10082
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9536
10083
|
_logsi.LogMethodParmList(SILevel.Verbose, "Checks if a Spotify Connect device is active, and activate one if not", apiMethodParms)
|
|
9537
10084
|
|
|
10085
|
+
# validations.
|
|
10086
|
+
delay = self._ValidateDelay(delay, 0.50, 10)
|
|
10087
|
+
|
|
9538
10088
|
# get current Spotify Connect player state.
|
|
9539
10089
|
result = self.GetPlayerPlaybackState()
|
|
9540
10090
|
|
|
9541
10091
|
# is there an active device set?
|
|
9542
|
-
if (result is None) \
|
|
9543
|
-
or (result.
|
|
9544
|
-
or (result.Device is None):
|
|
10092
|
+
if (result.CurrentlyPlayingType is None and result.Context is None) \
|
|
10093
|
+
or (result.Device is None) \
|
|
10094
|
+
or (result.Device.Id is None):
|
|
9545
10095
|
|
|
9546
10096
|
_logsi.LogVerbose('No active Spotify Connect device was found - activating device "%s"' % defaultDeviceId)
|
|
9547
10097
|
|
|
9548
10098
|
# no - transfer control to the default device.
|
|
9549
|
-
self.PlayerTransferPlayback(defaultDeviceId, play)
|
|
10099
|
+
self.PlayerTransferPlayback(defaultDeviceId, play, delay)
|
|
9550
10100
|
|
|
9551
10101
|
# get current Spotify Connect player state.
|
|
9552
10102
|
result = self.GetPlayerPlaybackState()
|
|
@@ -9814,6 +10364,72 @@ class SpotifyClient:
|
|
|
9814
10364
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
9815
10365
|
|
|
9816
10366
|
|
|
10367
|
+
def RemovePlaylist(self,
|
|
10368
|
+
playlistId:str,
|
|
10369
|
+
) -> None:
|
|
10370
|
+
"""
|
|
10371
|
+
Remove a user's playlist (calls the `UnfollowPlaylist` method).
|
|
10372
|
+
|
|
10373
|
+
This method requires the `playlist-modify-public` and `playlist-modify-private` scope.
|
|
10374
|
+
|
|
10375
|
+
Args:
|
|
10376
|
+
|
|
10377
|
+
playlistId (str):
|
|
10378
|
+
The Spotify ID of the playlist.
|
|
10379
|
+
Example: `5AC9ZXA7nJ7oGWO911FuDG`
|
|
10380
|
+
|
|
10381
|
+
Raises:
|
|
10382
|
+
SpotifyWebApiError:
|
|
10383
|
+
If the Spotify Web API request was for a non-authorization service
|
|
10384
|
+
and the response contains error information.
|
|
10385
|
+
SpotifApiError:
|
|
10386
|
+
If the method fails for any other reason.
|
|
10387
|
+
|
|
10388
|
+
There is no Spotify Web API endpoint for deleting a playlist. The notion of deleting a playlist is
|
|
10389
|
+
not relevant within the Spotify playlist system. Even if you are the playlist owner and you choose
|
|
10390
|
+
to manually remove it from your own list of playlists, you are simply unfollowing it. Although this
|
|
10391
|
+
behavior may sound strange, it means that other users who are already following the playlist can keep
|
|
10392
|
+
enjoying it.
|
|
10393
|
+
|
|
10394
|
+
[Manually restoring a deleted playlist](https://www.spotify.com/us/account/recover-playlists/) through
|
|
10395
|
+
the Spotify Accounts Service is the same thing as following one of your own playlists that you have
|
|
10396
|
+
previously unfollowed.
|
|
10397
|
+
|
|
10398
|
+
<details>
|
|
10399
|
+
<summary>Sample Code</summary>
|
|
10400
|
+
```python
|
|
10401
|
+
.. include:: ../docs/include/samplecode/SpotifyClient/RemovePlaylist.py
|
|
10402
|
+
```
|
|
10403
|
+
</details>
|
|
10404
|
+
"""
|
|
10405
|
+
apiMethodName:str = 'RemovePlaylist'
|
|
10406
|
+
apiMethodParms:SIMethodParmListContext = None
|
|
10407
|
+
result:str = None
|
|
10408
|
+
|
|
10409
|
+
try:
|
|
10410
|
+
|
|
10411
|
+
# trace.
|
|
10412
|
+
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
10413
|
+
apiMethodParms.AppendKeyValue("playlistId", playlistId)
|
|
10414
|
+
_logsi.LogMethodParmList(SILevel.Verbose, "Remove (by unfollowing) a user's playlist", apiMethodParms)
|
|
10415
|
+
|
|
10416
|
+
# unfollow the playlist for the specified user.
|
|
10417
|
+
return self.UnfollowPlaylist(playlistId)
|
|
10418
|
+
|
|
10419
|
+
except SpotifyApiError: raise # pass handled exceptions on thru
|
|
10420
|
+
except SpotifyWebApiError: raise # pass handled exceptions on thru
|
|
10421
|
+
except SpotifyWebApiAuthenticationError: raise # pass handled exceptions on thru
|
|
10422
|
+
except Exception as ex:
|
|
10423
|
+
|
|
10424
|
+
# format unhandled exception.
|
|
10425
|
+
raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
|
|
10426
|
+
|
|
10427
|
+
finally:
|
|
10428
|
+
|
|
10429
|
+
# trace.
|
|
10430
|
+
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
10431
|
+
|
|
10432
|
+
|
|
9817
10433
|
def RemovePlaylistItems(self,
|
|
9818
10434
|
playlistId:str,
|
|
9819
10435
|
uris:str=None,
|
|
@@ -9839,6 +10455,7 @@ class SpotifyClient:
|
|
|
9839
10455
|
The API will validate that the specified items exist and in the specified positions and
|
|
9840
10456
|
make the changes, even if more recent changes have been made to the playlist.
|
|
9841
10457
|
If null, the current playlist is updated.
|
|
10458
|
+
Example: `MTk3LGEzMjUwZGYwODljNmI5ZjAxZTRjZThiOGI4NzZhM2U5M2IxOWUyMDQ`
|
|
9842
10459
|
Default is null.
|
|
9843
10460
|
|
|
9844
10461
|
Returns:
|
|
@@ -10126,6 +10743,7 @@ class SpotifyClient:
|
|
|
10126
10743
|
snapshotId (str):
|
|
10127
10744
|
The playlist's snapshot ID against which you want to make the changes.
|
|
10128
10745
|
If null, the current playlist is updated.
|
|
10746
|
+
Example: `MTk3LGEzMjUwZGYwODljNmI5ZjAxZTRjZThiOGI4NzZhM2U5M2IxOWUyMDQ`
|
|
10129
10747
|
Default is null.
|
|
10130
10748
|
|
|
10131
10749
|
Returns:
|