spotifywebapiPython 1.0.174__py3-none-any.whl → 1.0.175__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.174.dist-info → spotifywebapiPython-1.0.175.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.175.dist-info}/RECORD +23 -23
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +9 -3
  5. spotifywebapipython/docs/index.html +3 -3
  6. spotifywebapipython/docs/search.js +1 -1
  7. spotifywebapipython/docs/spotifywebapipython/const.html +4 -4
  8. spotifywebapipython/docs/spotifywebapipython/saappmessages.html +37 -3
  9. spotifywebapipython/docs/spotifywebapipython/sautils.html +3 -3
  10. spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +3 -3
  11. spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +3 -3
  12. spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +3 -3
  13. spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +43 -21
  14. spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +3 -3
  15. spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +3 -3
  16. spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +3 -3
  17. spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +3 -3
  18. spotifywebapipython/saappmessages.py +10 -0
  19. spotifywebapipython/spotifyclient.py +153 -69
  20. spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +10 -8
  21. {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.175.dist-info}/LICENSE +0 -0
  22. {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.175.dist-info}/WHEEL +0 -0
  23. {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.175.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ from soco.core import (
10
10
  )
11
11
  from soco.plugins.sharelink import ShareLinkPlugin
12
12
  import time
13
+ import threading
13
14
  from typing import Tuple, Callable, Union
14
15
  from urllib3 import PoolManager, Timeout, HTTPResponse
15
16
  from urllib.parse import urlencode
@@ -208,6 +209,8 @@ class SpotifyClient:
208
209
  self._SpotifyConnectDiscoveryTimeout:float = float(spotifyConnectDiscoveryTimeout)
209
210
  self._SpotifyConnectDirectory:SpotifyConnectDirectoryTask = None
210
211
  self._SpotifyConnectDirectoryEnabled:bool = spotifyConnectDirectoryEnabled
212
+ self._SpotifyWebPlayerToken:SpotifyWebPlayerToken = None
213
+ self._SpotifyWebPlayerToken_RLock:threading.RLock = threading.RLock()
211
214
  self._TokenStorageDir:str = tokenStorageDir
212
215
  self._TokenStorageFile:str = tokenStorageFile
213
216
  self._TokenUpdater:Callable = tokenUpdater
@@ -797,44 +800,54 @@ class SpotifyClient:
797
800
  credentials are then converted into an authorization access token which is used by
798
801
  Spotify Web API endpoints to start playback on the specified device id.
799
802
  """
800
- result:SpotifyWebPlayerToken = None
801
-
802
803
  # are spotify web player credentials configured? if not, then don't bother!
803
804
  if (not self._HasSpotifyWebPlayerCredentials):
804
- return result
805
+ return None
805
806
 
806
- # was a device argument passed? if not, then just create the token.
807
- if (scDevice is None):
807
+ # make the following thread-safe, so we don't retrieve multiple tokens.
808
+ with self._SpotifyWebPlayerToken_RLock:
808
809
 
809
- # get spotify web player access token info from spotify web player cookie credentials.
810
- _logsi.LogVerbose("Converting Spotify Web Player cookie credentials to an access token for loginId \"%s\"" % (self._SpotifyConnectLoginId))
811
- tokenWP = SpotifyWebPlayerToken(profileId=self._SpotifyConnectLoginId,
812
- tokenStorageDir=self._TokenStorageDir,
813
- tokenStorageFile=self._TokenStorageFile)
814
- if tokenWP is not None:
815
- result = tokenWP.HeaderValue
816
- _logsi.LogVerbose("Spotify Web Player authorization access token will be used")
810
+ # if token already retrieved and has not expired, then use it.
811
+ if (self._SpotifyWebPlayerToken is not None) and (not self._SpotifyWebPlayerToken.IsExpired):
812
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_INUSE, colorValue=SIColors.Gold)
813
+ return self._SpotifyWebPlayerToken.HeaderValue
817
814
 
818
- else:
815
+ # was a device argument passed? if not, then just refresh the token.
816
+ if (scDevice is None):
819
817
 
820
- # was the device resolved? if not, then don't bother!
821
- if (not isinstance(scDevice, SpotifyConnectDevice)):
822
- return result
818
+ # get spotify web player access token info from spotify web player cookie credentials.
819
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_REFRESHED % (self._SpotifyConnectLoginId), colorValue=SIColors.Gold)
820
+ self._SpotifyWebPlayerToken = SpotifyWebPlayerToken(
821
+ profileId=self._SpotifyConnectLoginId,
822
+ tokenStorageDir=self._TokenStorageDir,
823
+ tokenStorageFile=self._TokenStorageFile)
823
824
 
824
- # is the device restricted or Sonos?
825
- if (scDevice.IsSonos or scDevice.IsRestricted):
825
+ if self._SpotifyWebPlayerToken is not None:
826
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_INUSE, colorValue=SIColors.Gold)
827
+ return self._SpotifyWebPlayerToken.HeaderValue
826
828
 
827
- # get spotify web player access token info from spotify web player cookie credentials.
828
- _logsi.LogVerbose("Converting Spotify Web Player cookie credentials to an access token for loginId \"%s\"" % (self._SpotifyConnectLoginId))
829
- tokenWP = SpotifyWebPlayerToken(profileId=self._SpotifyConnectLoginId,
830
- tokenStorageDir=self._TokenStorageDir,
831
- tokenStorageFile=self._TokenStorageFile)
832
- if tokenWP is not None:
833
- result = tokenWP.HeaderValue
834
- _logsi.LogVerbose("Playback will be started using Spotify Web Player authorization access token for device: %s" % (scDevice.Title))
829
+ else:
830
+
831
+ # was the device resolved? if not, then don't bother!
832
+ if (not isinstance(scDevice, SpotifyConnectDevice)):
833
+ return None
834
+
835
+ # is the device restricted or Sonos?
836
+ if (scDevice.IsSonos or scDevice.IsRestricted):
837
+
838
+ # get spotify web player access token info from spotify web player cookie credentials.
839
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_REFRESHED % (self._SpotifyConnectLoginId), colorValue=SIColors.Gold)
840
+ self._SpotifyWebPlayerToken = SpotifyWebPlayerToken(
841
+ profileId=self._SpotifyConnectLoginId,
842
+ tokenStorageDir=self._TokenStorageDir,
843
+ tokenStorageFile=self._TokenStorageFile)
844
+
845
+ if self._SpotifyWebPlayerToken is not None:
846
+ _logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_INUSE, colorValue=SIColors.Gold)
847
+ return self._SpotifyWebPlayerToken.HeaderValue
835
848
 
836
849
  # return result to caller.
837
- return result
850
+ return None
838
851
 
839
852
 
840
853
 
@@ -1388,9 +1401,14 @@ class SpotifyClient:
1388
1401
  # resolve the device object from the device id.
1389
1402
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
1390
1403
 
1404
+ # are spotify web player credentials configured? if so, then we will use them to create
1405
+ # an elevated authorization access token for the Spotify Web API endpoint call.
1406
+ # note that the token is only used to control "restricted" devices (such as Sonos).
1407
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
1408
+
1391
1409
  # is this an active Sonos device?
1392
1410
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
1393
- if (scDevice is not None) and (scDevice.IsSonos):
1411
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
1394
1412
 
1395
1413
  # trace.
1396
1414
  _logsi.LogVerbose("Items will be added to Sonos local queue for device: %s" % (scDevice.Title))
@@ -1429,7 +1447,7 @@ class SpotifyClient:
1429
1447
 
1430
1448
  # execute spotify web api request.
1431
1449
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/queue')
1432
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
1450
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
1433
1451
  msg.UrlParameters = urlParms
1434
1452
  self.MakeRequest('POST', msg)
1435
1453
 
@@ -8463,7 +8481,16 @@ class SpotifyClient:
8463
8481
  If the Spotify Web API request was for a non-authorization service
8464
8482
  and the response contains error information.
8465
8483
  SpotifyApiError:
8466
- If the method fails for any other reason.
8484
+ If the method fails for any other reason.
8485
+
8486
+ <span class="warning">
8487
+ <b>DEPRECATED functionality as of 2024/11/27</b>
8488
+ For unauthorized Spotify Developer Applications, the api endpoint will return a `404 - Resource Not Found` exception if a Spotify Algorithmic playlist is specified.<br/>
8489
+ For authorized Spotify Developer Applications, the api endpoint will return details if a Spotify Algorithmic playlist is specified.<br/>
8490
+ More information about the deprecated functionality can be found on the
8491
+ <a href="https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api" target="_blank">Spotify Developer Forum Blog</a>
8492
+ page.
8493
+ <span>
8467
8494
 
8468
8495
  <details>
8469
8496
  <summary>Sample Code</summary>
@@ -8712,16 +8739,27 @@ class SpotifyClient:
8712
8739
  If the Spotify Web API request was for a non-authorization service
8713
8740
  and the response contains error information.
8714
8741
  SpotifyApiError:
8715
- If the method fails for any other reason.
8742
+ If the method fails for any other reason.
8743
+
8744
+ <span class="warning">
8745
+ <b>Notes regarding `limit_total` processing</b>
8746
+ There appears to be a bug in the Spotify Web API that forces you to use manual paging if
8747
+ utilizing the `fields` argument. It has been found that the API will only return a value
8748
+ of 50 (maximum) or less in the page `total` value if the `fields` argument is supplied.
8749
+ The API will return the total number of playlist items in the page `total` value if the `fields`
8750
+ argument is NOT supplied. A good playlist id to test this on is `1XhVM7jWPrGLTiNiAy97Za`,
8751
+ which is the largest playlist on spotify (4700+ items).
8752
+ <span>
8753
+
8754
+ <span class="warning">
8755
+ <b>DEPRECATED functionality as of 2024/11/27</b>
8756
+ Spotify Algorithmic playlists (e.g. "Made For You", "Daily Mix n", etc) will no longer be included
8757
+ in the returned results for unauthorized Spotify Developer Applications; they will be included in
8758
+ the results for authorized Spotify Developer Applications. More information about the deprecated
8759
+ functionality can be found on the <a href="https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api"
8760
+ target="_blank">Spotify Developer Forum Blog</a> page.
8761
+ <span>
8716
8762
 
8717
- * Notes regarding `limit_total` processing.
8718
- There appears to be a bug in the Spotify Web API that forces you to use manual paging if
8719
- utilizing the `fields` argument. It has been found that the API will only return a value
8720
- of 50 (maximum) or less in the page `total` value if the `fields` argument is supplied.
8721
- The API will return the total number of playlist items in the page `total` value if the `fields`
8722
- argument is NOT supplied. A good playlist id to test this on is `1XhVM7jWPrGLTiNiAy97Za`,
8723
- which is the largest playlist on spotify (4700+ items).
8724
-
8725
8763
  <details>
8726
8764
  <summary>Sample Code - Manual Paging</summary>
8727
8765
  ```python
@@ -8895,13 +8933,17 @@ class SpotifyClient:
8895
8933
  If the Spotify Web API request was for a non-authorization service
8896
8934
  and the response contains error information.
8897
8935
  SpotifyApiError:
8898
- If the method fails for any other reason.
8936
+ If the method fails for any other reason.
8937
+
8938
+ <span class="warning">
8939
+ <b>DEPRECATED functionality as of 2024/11/27</b>
8940
+ Spotify Algorithmic playlists (e.g. "Made For You", "Daily Mix n", etc) will no longer be included
8941
+ in the returned results for unauthorized Spotify Developer Applications; they will be included in
8942
+ the results for authorized Spotify Developer Applications. More information about the deprecated
8943
+ functionality can be found on the <a href="https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api"
8944
+ target="_blank">Spotify Developer Forum Blog</a> page.
8945
+ <span>
8899
8946
 
8900
- As of 2024/11/27, the Spotify Web API will only return user-defined playlists in the results. It will
8901
- not return any Spotify Algorithmic playlists (e.g. "Daily Mix 1", "Discover Weekly", and other "Made For You"
8902
- playlists). It will also not return playlist folders. More information can be found on the [Spotify Developer
8903
- Forum Blog post](https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api) that was conveyed on November 27, 2024.
8904
-
8905
8947
  <details>
8906
8948
  <summary>Sample Code - Manual Paging</summary>
8907
8949
  ```python
@@ -11757,7 +11799,7 @@ class SpotifyClient:
11757
11799
  This API only works for users who have Spotify Premium.
11758
11800
 
11759
11801
  The order of execution is not guaranteed when you use this API with other Player API endpoints.
11760
-
11802
+
11761
11803
  <details>
11762
11804
  <summary>Sample Code</summary>
11763
11805
  ```python
@@ -11782,9 +11824,14 @@ class SpotifyClient:
11782
11824
  # resolve the device object from the device id.
11783
11825
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
11784
11826
 
11827
+ # are spotify web player credentials configured? if so, then we will use them to create
11828
+ # an elevated authorization access token for the Spotify Web API endpoint call.
11829
+ # note that the token is only used to control "restricted" devices (such as Sonos).
11830
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
11831
+
11785
11832
  # is this an active Sonos device?
11786
11833
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
11787
- if (scDevice is not None) and (scDevice.IsSonos):
11834
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
11788
11835
 
11789
11836
  _logsi.LogVerbose("Issuing command to Sonos device %s: PAUSE" % (scDevice.Title))
11790
11837
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -11803,7 +11850,7 @@ class SpotifyClient:
11803
11850
 
11804
11851
  # execute spotify web api request.
11805
11852
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/pause')
11806
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
11853
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
11807
11854
  msg.UrlParameters = urlParms
11808
11855
  self.MakeRequest('PUT', msg)
11809
11856
 
@@ -12474,9 +12521,14 @@ class SpotifyClient:
12474
12521
  # we activate the device here, as it may have been paused for a long time.
12475
12522
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, True)
12476
12523
 
12524
+ # are spotify web player credentials configured? if so, then we will use them to create
12525
+ # an elevated authorization access token for the Spotify Web API endpoint call.
12526
+ # note that the token is only used to control "restricted" devices (such as Sonos).
12527
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
12528
+
12477
12529
  # is this an active Sonos device?
12478
12530
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12479
- if (scDevice is not None) and (scDevice.IsSonos):
12531
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12480
12532
 
12481
12533
  _logsi.LogVerbose("Issuing command to Sonos device %s: PLAY (RESUME)" % (scDevice.Title))
12482
12534
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -12495,7 +12547,7 @@ class SpotifyClient:
12495
12547
 
12496
12548
  # execute spotify web api request.
12497
12549
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play')
12498
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12550
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12499
12551
  msg.UrlParameters = urlParms
12500
12552
  self.MakeRequest('PUT', msg)
12501
12553
 
@@ -12631,9 +12683,14 @@ class SpotifyClient:
12631
12683
  newPositionMS = 0
12632
12684
  positionMS = newPositionMS
12633
12685
 
12686
+ # are spotify web player credentials configured? if so, then we will use them to create
12687
+ # an elevated authorization access token for the Spotify Web API endpoint call.
12688
+ # note that the token is only used to control "restricted" devices (such as Sonos).
12689
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
12690
+
12634
12691
  # is this an active Sonos device?
12635
12692
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12636
- if (scDevice is not None) and (scDevice.IsSonos):
12693
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12637
12694
 
12638
12695
  sonosPosition:str = mediaPositionHMS_fromSeconds(positionMS / 1000) # convert from milliseconds to Sonos H:MM:SS format
12639
12696
  _logsi.LogVerbose("Issuing command to Sonos device %s: SEEK (sonosPosition=%s)" % (scDevice.Title, sonosPosition))
@@ -12656,7 +12713,7 @@ class SpotifyClient:
12656
12713
 
12657
12714
  # execute spotify web api request.
12658
12715
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/seek')
12659
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12716
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12660
12717
  msg.UrlParameters = urlParms
12661
12718
  self.MakeRequest('PUT', msg)
12662
12719
 
@@ -12743,9 +12800,14 @@ class SpotifyClient:
12743
12800
  # resolve the device object from the device id.
12744
12801
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12745
12802
 
12803
+ # are spotify web player credentials configured? if so, then we will use them to create
12804
+ # an elevated authorization access token for the Spotify Web API endpoint call.
12805
+ # note that the token is only used to control "restricted" devices (such as Sonos).
12806
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
12807
+
12746
12808
  # is this an active Sonos device?
12747
12809
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12748
- if (scDevice is not None) and (scDevice.IsSonos):
12810
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12749
12811
 
12750
12812
  _logsi.LogVerbose("Issuing command to Sonos device %s: NEXT" % (scDevice.Title))
12751
12813
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -12764,7 +12826,7 @@ class SpotifyClient:
12764
12826
 
12765
12827
  # execute spotify web api request.
12766
12828
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/next')
12767
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12829
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12768
12830
  msg.UrlParameters = urlParms
12769
12831
  self.MakeRequest('POST', msg)
12770
12832
 
@@ -12851,9 +12913,14 @@ class SpotifyClient:
12851
12913
  # resolve the device object from the device id.
12852
12914
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12853
12915
 
12916
+ # are spotify web player credentials configured? if so, then we will use them to create
12917
+ # an elevated authorization access token for the Spotify Web API endpoint call.
12918
+ # note that the token is only used to control "restricted" devices (such as Sonos).
12919
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
12920
+
12854
12921
  # is this an active Sonos device?
12855
12922
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12856
- if (scDevice is not None) and (scDevice.IsSonos):
12923
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12857
12924
 
12858
12925
  _logsi.LogVerbose("Issuing command to Sonos device %s: PREVIOUS" % (scDevice.Title))
12859
12926
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -12872,7 +12939,7 @@ class SpotifyClient:
12872
12939
 
12873
12940
  # execute spotify web api request.
12874
12941
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/previous')
12875
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12942
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12876
12943
  msg.UrlParameters = urlParms
12877
12944
  self.MakeRequest('POST', msg)
12878
12945
 
@@ -12967,9 +13034,14 @@ class SpotifyClient:
12967
13034
  # resolve the device object from the device id.
12968
13035
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12969
13036
 
13037
+ # are spotify web player credentials configured? if so, then we will use them to create
13038
+ # an elevated authorization access token for the Spotify Web API endpoint call.
13039
+ # note that the token is only used to control "restricted" devices (such as Sonos).
13040
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
13041
+
12970
13042
  # is this an active Sonos device?
12971
13043
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12972
- if (scDevice is not None) and (scDevice.IsSonos):
13044
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12973
13045
 
12974
13046
  # get current Spotify Connect player state.
12975
13047
  playerState = self.GetPlayerPlaybackStateSonos(scDevice)
@@ -13008,7 +13080,7 @@ class SpotifyClient:
13008
13080
 
13009
13081
  # execute spotify web api request.
13010
13082
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/repeat')
13011
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13083
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13012
13084
  msg.UrlParameters = urlParms
13013
13085
  self.MakeRequest('PUT', msg)
13014
13086
 
@@ -13102,9 +13174,14 @@ class SpotifyClient:
13102
13174
  # resolve the device object from the device id.
13103
13175
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
13104
13176
 
13177
+ # are spotify web player credentials configured? if so, then we will use them to create
13178
+ # an elevated authorization access token for the Spotify Web API endpoint call.
13179
+ # note that the token is only used to control "restricted" devices (such as Sonos).
13180
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
13181
+
13105
13182
  # is this an active Sonos device?
13106
13183
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
13107
- if (scDevice is not None) and (scDevice.IsSonos):
13184
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
13108
13185
 
13109
13186
  # get current Spotify Connect player state.
13110
13187
  playerState = self.GetPlayerPlaybackStateSonos(scDevice)
@@ -13144,7 +13221,7 @@ class SpotifyClient:
13144
13221
 
13145
13222
  # execute spotify web api request.
13146
13223
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/shuffle')
13147
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13224
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13148
13225
  msg.UrlParameters = urlParms
13149
13226
  self.MakeRequest('PUT', msg)
13150
13227
 
@@ -13237,9 +13314,14 @@ class SpotifyClient:
13237
13314
  # resolve the device object from the device id.
13238
13315
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
13239
13316
 
13317
+ # are spotify web player credentials configured? if so, then we will use them to create
13318
+ # an elevated authorization access token for the Spotify Web API endpoint call.
13319
+ # note that the token is only used to control "restricted" devices (such as Sonos).
13320
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
13321
+
13240
13322
  # is this an active Sonos device?
13241
13323
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
13242
- if (scDevice is not None) and (scDevice.IsSonos):
13324
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
13243
13325
 
13244
13326
  _logsi.LogVerbose("Issuing command to Sonos device %s: VOLUME = %s" % (scDevice.Title, volumePercent))
13245
13327
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -13261,7 +13343,7 @@ class SpotifyClient:
13261
13343
 
13262
13344
  # execute spotify web api request.
13263
13345
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/volume')
13264
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13346
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13265
13347
  msg.UrlParameters = urlParms
13266
13348
  self.MakeRequest('PUT', msg)
13267
13349
 
@@ -13411,12 +13493,16 @@ class SpotifyClient:
13411
13493
  # if not, then raise an exception as the Spotify Web API request will fail anyway.
13412
13494
  self._CheckForDeviceNotFound(scDevice, deviceId)
13413
13495
 
13496
+ # are spotify web player credentials configured? if so, then we will use them to create
13497
+ # an elevated authorization access token for the Spotify Web API endpoint call.
13498
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue()
13499
+
13414
13500
  # is this a Sonos device?
13415
13501
  # if so, then control was automatically transferred by activating the device.
13416
13502
  # note that the Spotify Web API cannot stop nor start play on the device either,
13417
13503
  # as it's considered a restricted device and the Spotify Web API cannot control it!
13418
13504
  # all requests to control the device will be made via the Sonos Controller API.
13419
- if (scDevice.IsSonos):
13505
+ if (scDevice.IsSonos) and (accessTokenHeaderValue is None):
13420
13506
 
13421
13507
  # get Sonos Controller instance for the device.
13422
13508
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -13431,13 +13517,11 @@ class SpotifyClient:
13431
13517
  wasCmdIssued:bool = False
13432
13518
  if currentTransportState == 'PLAYING':
13433
13519
  if play == False:
13434
- _logsi.LogVerbose("Issuing command to Sonos device %s: PAUSE" % (scDevice.Title))
13435
- sonosPlayer.pause()
13520
+ self.PlayerMediaPause(scDevice)
13436
13521
  wasCmdIssued = True
13437
13522
  elif currentTransportState in ['PAUSED_PLAYBACK','STOPPED','TRANSITIONING']:
13438
13523
  if play == True:
13439
- _logsi.LogVerbose("Issuing command to Sonos device %s: PLAY" % (scDevice.Title))
13440
- sonosPlayer.play()
13524
+ self.PlayerMediaResume(scDevice)
13441
13525
  wasCmdIssued = True
13442
13526
 
13443
13527
  # give Sonos Controller time to process the change.
@@ -13480,7 +13564,7 @@ class SpotifyClient:
13480
13564
 
13481
13565
  # execute spotify web api request.
13482
13566
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player')
13483
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13567
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13484
13568
  msg.RequestJson = reqData
13485
13569
  self.MakeRequest('PUT', msg)
13486
13570
 
@@ -1057,18 +1057,20 @@ class SpotifyConnectDirectoryTask(threading.Thread):
1057
1057
 
1058
1058
  # if Sonos Controller instance is the group coordinator then we are done.
1059
1059
  if (sonosPlayer.is_coordinator):
1060
- _logsi.LogVerbose("Sonos Controller instance is a group coordinator for device: %s" % (device.Title))
1060
+ if _logsi.IsOn(SILevel.Verbose):
1061
+ _logsi.LogVerbose("Sonos Controller instance is a group coordinator for device: %s" % (device.Title))
1062
+ _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s" % (device.Title), sonosPlayer)
1063
+ _logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (device.Title), sonosPlayer.speaker_info)
1064
+ _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (group)" % (device.Title), sonosPlayer.group)
1065
+ _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (zone_group_state)" % (device.Title), sonosPlayer.zone_group_state)
1066
+ return sonosPlayer
1067
+
1068
+ # trace.
1069
+ if _logsi.IsOn(SILevel.Verbose):
1061
1070
  _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s" % (device.Title), sonosPlayer)
1062
1071
  _logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (device.Title), sonosPlayer.speaker_info)
1063
1072
  _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (group)" % (device.Title), sonosPlayer.group)
1064
1073
  _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (zone_group_state)" % (device.Title), sonosPlayer.zone_group_state)
1065
- return sonosPlayer
1066
-
1067
- # trace.
1068
- _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s" % (device.Title), sonosPlayer)
1069
- _logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (device.Title), sonosPlayer.speaker_info)
1070
- _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (group)" % (device.Title), sonosPlayer.group)
1071
- _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (zone_group_state)" % (device.Title), sonosPlayer.zone_group_state)
1072
1074
 
1073
1075
  # is the device part of a group? if so, then use the group coordinator.
1074
1076
  if (sonosPlayer.group is not None):