spotifywebapiPython 1.0.173__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.173.dist-info → spotifywebapiPython-1.0.175.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.173.dist-info → spotifywebapiPython-1.0.175.dist-info}/RECORD +23 -23
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +16 -4
  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 +158 -70
  20. spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +10 -8
  21. {spotifywebapiPython-1.0.173.dist-info → spotifywebapiPython-1.0.175.dist-info}/LICENSE +0 -0
  22. {spotifywebapiPython-1.0.173.dist-info → spotifywebapiPython-1.0.175.dist-info}/WHEEL +0 -0
  23. {spotifywebapiPython-1.0.173.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
@@ -8752,6 +8790,10 @@ class SpotifyClient:
8752
8790
  apiMethodParms.AppendKeyValue("limitTotal", limitTotal)
8753
8791
  _logsi.LogMethodParmList(SILevel.Verbose, "Get full details of the items of a playlist owned by a Spotify user", apiMethodParms)
8754
8792
 
8793
+ # are spotify web player credentials configured? if so, then we will use them to create
8794
+ # an elevated authorization access token for the Spotify Web API endpoint call.
8795
+ accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue()
8796
+
8755
8797
  # validations.
8756
8798
  if limit is None:
8757
8799
  limit = 20
@@ -8796,7 +8838,7 @@ class SpotifyClient:
8796
8838
 
8797
8839
  # execute spotify web api request.
8798
8840
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/playlists/{id}/tracks'.format(id=playlistId))
8799
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
8841
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
8800
8842
  msg.UrlParameters = urlParms
8801
8843
  self.MakeRequest('GET', msg)
8802
8844
 
@@ -8891,13 +8933,17 @@ class SpotifyClient:
8891
8933
  If the Spotify Web API request was for a non-authorization service
8892
8934
  and the response contains error information.
8893
8935
  SpotifyApiError:
8894
- 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>
8895
8946
 
8896
- As of 2024/11/27, the Spotify Web API will only return user-defined playlists in the results. It will
8897
- not return any Spotify Algorithmic playlists (e.g. "Daily Mix 1", "Discover Weekly", and other "Made For You"
8898
- playlists). It will also not return playlist folders. More information can be found on the [Spotify Developer
8899
- Forum Blog post](https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api) that was conveyed on November 27, 2024.
8900
-
8901
8947
  <details>
8902
8948
  <summary>Sample Code - Manual Paging</summary>
8903
8949
  ```python
@@ -11753,7 +11799,7 @@ class SpotifyClient:
11753
11799
  This API only works for users who have Spotify Premium.
11754
11800
 
11755
11801
  The order of execution is not guaranteed when you use this API with other Player API endpoints.
11756
-
11802
+
11757
11803
  <details>
11758
11804
  <summary>Sample Code</summary>
11759
11805
  ```python
@@ -11778,9 +11824,14 @@ class SpotifyClient:
11778
11824
  # resolve the device object from the device id.
11779
11825
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
11780
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
+
11781
11832
  # is this an active Sonos device?
11782
11833
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
11783
- if (scDevice is not None) and (scDevice.IsSonos):
11834
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
11784
11835
 
11785
11836
  _logsi.LogVerbose("Issuing command to Sonos device %s: PAUSE" % (scDevice.Title))
11786
11837
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -11799,7 +11850,7 @@ class SpotifyClient:
11799
11850
 
11800
11851
  # execute spotify web api request.
11801
11852
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/pause')
11802
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
11853
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
11803
11854
  msg.UrlParameters = urlParms
11804
11855
  self.MakeRequest('PUT', msg)
11805
11856
 
@@ -12470,9 +12521,14 @@ class SpotifyClient:
12470
12521
  # we activate the device here, as it may have been paused for a long time.
12471
12522
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, True)
12472
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
+
12473
12529
  # is this an active Sonos device?
12474
12530
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12475
- if (scDevice is not None) and (scDevice.IsSonos):
12531
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12476
12532
 
12477
12533
  _logsi.LogVerbose("Issuing command to Sonos device %s: PLAY (RESUME)" % (scDevice.Title))
12478
12534
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -12491,7 +12547,7 @@ class SpotifyClient:
12491
12547
 
12492
12548
  # execute spotify web api request.
12493
12549
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play')
12494
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12550
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12495
12551
  msg.UrlParameters = urlParms
12496
12552
  self.MakeRequest('PUT', msg)
12497
12553
 
@@ -12627,9 +12683,14 @@ class SpotifyClient:
12627
12683
  newPositionMS = 0
12628
12684
  positionMS = newPositionMS
12629
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
+
12630
12691
  # is this an active Sonos device?
12631
12692
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12632
- if (scDevice is not None) and (scDevice.IsSonos):
12693
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12633
12694
 
12634
12695
  sonosPosition:str = mediaPositionHMS_fromSeconds(positionMS / 1000) # convert from milliseconds to Sonos H:MM:SS format
12635
12696
  _logsi.LogVerbose("Issuing command to Sonos device %s: SEEK (sonosPosition=%s)" % (scDevice.Title, sonosPosition))
@@ -12652,7 +12713,7 @@ class SpotifyClient:
12652
12713
 
12653
12714
  # execute spotify web api request.
12654
12715
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/seek')
12655
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12716
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12656
12717
  msg.UrlParameters = urlParms
12657
12718
  self.MakeRequest('PUT', msg)
12658
12719
 
@@ -12739,9 +12800,14 @@ class SpotifyClient:
12739
12800
  # resolve the device object from the device id.
12740
12801
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12741
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
+
12742
12808
  # is this an active Sonos device?
12743
12809
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12744
- if (scDevice is not None) and (scDevice.IsSonos):
12810
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12745
12811
 
12746
12812
  _logsi.LogVerbose("Issuing command to Sonos device %s: NEXT" % (scDevice.Title))
12747
12813
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -12760,7 +12826,7 @@ class SpotifyClient:
12760
12826
 
12761
12827
  # execute spotify web api request.
12762
12828
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/next')
12763
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12829
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12764
12830
  msg.UrlParameters = urlParms
12765
12831
  self.MakeRequest('POST', msg)
12766
12832
 
@@ -12847,9 +12913,14 @@ class SpotifyClient:
12847
12913
  # resolve the device object from the device id.
12848
12914
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12849
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
+
12850
12921
  # is this an active Sonos device?
12851
12922
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12852
- if (scDevice is not None) and (scDevice.IsSonos):
12923
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12853
12924
 
12854
12925
  _logsi.LogVerbose("Issuing command to Sonos device %s: PREVIOUS" % (scDevice.Title))
12855
12926
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -12868,7 +12939,7 @@ class SpotifyClient:
12868
12939
 
12869
12940
  # execute spotify web api request.
12870
12941
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/previous')
12871
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
12942
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
12872
12943
  msg.UrlParameters = urlParms
12873
12944
  self.MakeRequest('POST', msg)
12874
12945
 
@@ -12963,9 +13034,14 @@ class SpotifyClient:
12963
13034
  # resolve the device object from the device id.
12964
13035
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12965
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
+
12966
13042
  # is this an active Sonos device?
12967
13043
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
12968
- if (scDevice is not None) and (scDevice.IsSonos):
13044
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
12969
13045
 
12970
13046
  # get current Spotify Connect player state.
12971
13047
  playerState = self.GetPlayerPlaybackStateSonos(scDevice)
@@ -13004,7 +13080,7 @@ class SpotifyClient:
13004
13080
 
13005
13081
  # execute spotify web api request.
13006
13082
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/repeat')
13007
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13083
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13008
13084
  msg.UrlParameters = urlParms
13009
13085
  self.MakeRequest('PUT', msg)
13010
13086
 
@@ -13098,9 +13174,14 @@ class SpotifyClient:
13098
13174
  # resolve the device object from the device id.
13099
13175
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
13100
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
+
13101
13182
  # is this an active Sonos device?
13102
13183
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
13103
- if (scDevice is not None) and (scDevice.IsSonos):
13184
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
13104
13185
 
13105
13186
  # get current Spotify Connect player state.
13106
13187
  playerState = self.GetPlayerPlaybackStateSonos(scDevice)
@@ -13140,7 +13221,7 @@ class SpotifyClient:
13140
13221
 
13141
13222
  # execute spotify web api request.
13142
13223
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/shuffle')
13143
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13224
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13144
13225
  msg.UrlParameters = urlParms
13145
13226
  self.MakeRequest('PUT', msg)
13146
13227
 
@@ -13233,9 +13314,14 @@ class SpotifyClient:
13233
13314
  # resolve the device object from the device id.
13234
13315
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
13235
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
+
13236
13322
  # is this an active Sonos device?
13237
13323
  # Sonos device can still be active, even if there is no active device in Spotify playstate.
13238
- if (scDevice is not None) and (scDevice.IsSonos):
13324
+ if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
13239
13325
 
13240
13326
  _logsi.LogVerbose("Issuing command to Sonos device %s: VOLUME = %s" % (scDevice.Title, volumePercent))
13241
13327
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -13257,7 +13343,7 @@ class SpotifyClient:
13257
13343
 
13258
13344
  # execute spotify web api request.
13259
13345
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/volume')
13260
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13346
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13261
13347
  msg.UrlParameters = urlParms
13262
13348
  self.MakeRequest('PUT', msg)
13263
13349
 
@@ -13407,12 +13493,16 @@ class SpotifyClient:
13407
13493
  # if not, then raise an exception as the Spotify Web API request will fail anyway.
13408
13494
  self._CheckForDeviceNotFound(scDevice, deviceId)
13409
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
+
13410
13500
  # is this a Sonos device?
13411
13501
  # if so, then control was automatically transferred by activating the device.
13412
13502
  # note that the Spotify Web API cannot stop nor start play on the device either,
13413
13503
  # as it's considered a restricted device and the Spotify Web API cannot control it!
13414
13504
  # all requests to control the device will be made via the Sonos Controller API.
13415
- if (scDevice.IsSonos):
13505
+ if (scDevice.IsSonos) and (accessTokenHeaderValue is None):
13416
13506
 
13417
13507
  # get Sonos Controller instance for the device.
13418
13508
  sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
@@ -13427,13 +13517,11 @@ class SpotifyClient:
13427
13517
  wasCmdIssued:bool = False
13428
13518
  if currentTransportState == 'PLAYING':
13429
13519
  if play == False:
13430
- _logsi.LogVerbose("Issuing command to Sonos device %s: PAUSE" % (scDevice.Title))
13431
- sonosPlayer.pause()
13520
+ self.PlayerMediaPause(scDevice)
13432
13521
  wasCmdIssued = True
13433
13522
  elif currentTransportState in ['PAUSED_PLAYBACK','STOPPED','TRANSITIONING']:
13434
13523
  if play == True:
13435
- _logsi.LogVerbose("Issuing command to Sonos device %s: PLAY" % (scDevice.Title))
13436
- sonosPlayer.play()
13524
+ self.PlayerMediaResume(scDevice)
13437
13525
  wasCmdIssued = True
13438
13526
 
13439
13527
  # give Sonos Controller time to process the change.
@@ -13476,7 +13564,7 @@ class SpotifyClient:
13476
13564
 
13477
13565
  # execute spotify web api request.
13478
13566
  msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player')
13479
- msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
13567
+ msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
13480
13568
  msg.RequestJson = reqData
13481
13569
  self.MakeRequest('PUT', msg)
13482
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):