spotifywebapiPython 1.0.174__py3-none-any.whl → 1.0.176__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.174.dist-info → spotifywebapiPython-1.0.176.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.176.dist-info}/RECORD +23 -23
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +15 -3
- spotifywebapipython/docs/index.html +3 -3
- spotifywebapipython/docs/search.js +1 -1
- spotifywebapipython/docs/spotifywebapipython/const.html +4 -4
- spotifywebapipython/docs/spotifywebapipython/saappmessages.html +37 -3
- spotifywebapipython/docs/spotifywebapipython/sautils.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +45 -23
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +3 -3
- spotifywebapipython/saappmessages.py +10 -0
- spotifywebapipython/spotifyclient.py +148 -80
- spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +50 -23
- {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.176.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.176.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.176.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
|
|
@@ -366,8 +369,8 @@ class SpotifyClient:
|
|
|
366
369
|
@property
|
|
367
370
|
def HasSpotifyWebPlayerCredentials(self) -> bool:
|
|
368
371
|
"""
|
|
369
|
-
Returns true if Spotify Web Player credentials have been configured in the Token Cache File
|
|
370
|
-
otherwise, False.
|
|
372
|
+
Returns true if Spotify Web Player credentials have been configured in the Token Cache File
|
|
373
|
+
for the current UserProfile loginid; otherwise, False.
|
|
371
374
|
"""
|
|
372
375
|
return self._HasSpotifyWebPlayerCredentials
|
|
373
376
|
|
|
@@ -790,51 +793,58 @@ class SpotifyClient:
|
|
|
790
793
|
otherwise, None.
|
|
791
794
|
|
|
792
795
|
The value returned will be a `Bearer: token` string that can be used in a request header
|
|
793
|
-
authorization key for Spotify Web API endpoints that start playback on a
|
|
796
|
+
authorization key for Spotify Web API endpoints that start playback on a device.
|
|
794
797
|
|
|
795
798
|
The Token Cache File is queried for a key value of "SpotifyWebPlayerCookieCredentials/Shared/YOUR_SPOTIFY_LOGIN_ID",
|
|
796
799
|
which will contain the `sp_key` and `sp_dc` values of the cookie credentials. These
|
|
797
800
|
credentials are then converted into an authorization access token which is used by
|
|
798
|
-
Spotify Web API endpoints
|
|
801
|
+
Spotify Web API endpoints.
|
|
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
|
|
805
|
+
return None
|
|
805
806
|
|
|
806
|
-
#
|
|
807
|
-
|
|
807
|
+
# make the following thread-safe, so we don't retrieve multiple tokens.
|
|
808
|
+
with self._SpotifyWebPlayerToken_RLock:
|
|
808
809
|
|
|
809
|
-
#
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
815
|
+
# was a device argument passed? if not, then just refresh the token.
|
|
816
|
+
if (scDevice is None):
|
|
819
817
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
825
|
-
|
|
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
|
|
828
|
+
|
|
829
|
+
else:
|
|
830
|
+
|
|
831
|
+
# was the device resolved? if not, then don't bother!
|
|
832
|
+
if (not isinstance(scDevice, SpotifyConnectDevice)):
|
|
833
|
+
return None
|
|
826
834
|
|
|
827
835
|
# get spotify web player access token info from spotify web player cookie credentials.
|
|
828
|
-
_logsi.LogVerbose(
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
836
|
+
_logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_REFRESHED % (self._SpotifyConnectLoginId), colorValue=SIColors.Gold)
|
|
837
|
+
self._SpotifyWebPlayerToken = SpotifyWebPlayerToken(
|
|
838
|
+
profileId=self._SpotifyConnectLoginId,
|
|
839
|
+
tokenStorageDir=self._TokenStorageDir,
|
|
840
|
+
tokenStorageFile=self._TokenStorageFile)
|
|
841
|
+
|
|
842
|
+
if self._SpotifyWebPlayerToken is not None:
|
|
843
|
+
_logsi.LogVerbose(SAAppMessages.MSG_SPOTIFY_WEB_PLAYER_TOKEN_INUSE, colorValue=SIColors.Gold)
|
|
844
|
+
return self._SpotifyWebPlayerToken.HeaderValue
|
|
835
845
|
|
|
836
846
|
# return result to caller.
|
|
837
|
-
return
|
|
847
|
+
return None
|
|
838
848
|
|
|
839
849
|
|
|
840
850
|
|
|
@@ -1388,9 +1398,13 @@ class SpotifyClient:
|
|
|
1388
1398
|
# resolve the device object from the device id.
|
|
1389
1399
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
1390
1400
|
|
|
1401
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
1402
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
1403
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
1404
|
+
|
|
1391
1405
|
# is this an active Sonos device?
|
|
1392
1406
|
# 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):
|
|
1407
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
1394
1408
|
|
|
1395
1409
|
# trace.
|
|
1396
1410
|
_logsi.LogVerbose("Items will be added to Sonos local queue for device: %s" % (scDevice.Title))
|
|
@@ -1429,7 +1443,7 @@ class SpotifyClient:
|
|
|
1429
1443
|
|
|
1430
1444
|
# execute spotify web api request.
|
|
1431
1445
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/queue')
|
|
1432
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
1446
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
1433
1447
|
msg.UrlParameters = urlParms
|
|
1434
1448
|
self.MakeRequest('POST', msg)
|
|
1435
1449
|
|
|
@@ -8463,7 +8477,16 @@ class SpotifyClient:
|
|
|
8463
8477
|
If the Spotify Web API request was for a non-authorization service
|
|
8464
8478
|
and the response contains error information.
|
|
8465
8479
|
SpotifyApiError:
|
|
8466
|
-
If the method fails for any other reason.
|
|
8480
|
+
If the method fails for any other reason.
|
|
8481
|
+
|
|
8482
|
+
<span class="warning">
|
|
8483
|
+
<b>DEPRECATED functionality as of 2024/11/27</b>
|
|
8484
|
+
For unauthorized Spotify Developer Applications, the api endpoint will return a `404 - Resource Not Found` exception if a Spotify Algorithmic playlist is specified.<br/>
|
|
8485
|
+
For authorized Spotify Developer Applications, the api endpoint will return details if a Spotify Algorithmic playlist is specified.<br/>
|
|
8486
|
+
More information about the deprecated functionality can be found on the
|
|
8487
|
+
<a href="https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api" target="_blank">Spotify Developer Forum Blog</a>
|
|
8488
|
+
page.
|
|
8489
|
+
<span>
|
|
8467
8490
|
|
|
8468
8491
|
<details>
|
|
8469
8492
|
<summary>Sample Code</summary>
|
|
@@ -8712,16 +8735,27 @@ class SpotifyClient:
|
|
|
8712
8735
|
If the Spotify Web API request was for a non-authorization service
|
|
8713
8736
|
and the response contains error information.
|
|
8714
8737
|
SpotifyApiError:
|
|
8715
|
-
If the method fails for any other reason.
|
|
8738
|
+
If the method fails for any other reason.
|
|
8739
|
+
|
|
8740
|
+
<span class="warning">
|
|
8741
|
+
<b>Notes regarding `limit_total` processing</b>
|
|
8742
|
+
There appears to be a bug in the Spotify Web API that forces you to use manual paging if
|
|
8743
|
+
utilizing the `fields` argument. It has been found that the API will only return a value
|
|
8744
|
+
of 50 (maximum) or less in the page `total` value if the `fields` argument is supplied.
|
|
8745
|
+
The API will return the total number of playlist items in the page `total` value if the `fields`
|
|
8746
|
+
argument is NOT supplied. A good playlist id to test this on is `1XhVM7jWPrGLTiNiAy97Za`,
|
|
8747
|
+
which is the largest playlist on spotify (4700+ items).
|
|
8748
|
+
<span>
|
|
8749
|
+
|
|
8750
|
+
<span class="warning">
|
|
8751
|
+
<b>DEPRECATED functionality as of 2024/11/27</b>
|
|
8752
|
+
Spotify Algorithmic playlists (e.g. "Made For You", "Daily Mix n", etc) will no longer be included
|
|
8753
|
+
in the returned results for unauthorized Spotify Developer Applications; they will be included in
|
|
8754
|
+
the results for authorized Spotify Developer Applications. More information about the deprecated
|
|
8755
|
+
functionality can be found on the <a href="https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api"
|
|
8756
|
+
target="_blank">Spotify Developer Forum Blog</a> page.
|
|
8757
|
+
<span>
|
|
8716
8758
|
|
|
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
8759
|
<details>
|
|
8726
8760
|
<summary>Sample Code - Manual Paging</summary>
|
|
8727
8761
|
```python
|
|
@@ -8895,13 +8929,17 @@ class SpotifyClient:
|
|
|
8895
8929
|
If the Spotify Web API request was for a non-authorization service
|
|
8896
8930
|
and the response contains error information.
|
|
8897
8931
|
SpotifyApiError:
|
|
8898
|
-
If the method fails for any other reason.
|
|
8932
|
+
If the method fails for any other reason.
|
|
8933
|
+
|
|
8934
|
+
<span class="warning">
|
|
8935
|
+
<b>DEPRECATED functionality as of 2024/11/27</b>
|
|
8936
|
+
Spotify Algorithmic playlists (e.g. "Made For You", "Daily Mix n", etc) will no longer be included
|
|
8937
|
+
in the returned results for unauthorized Spotify Developer Applications; they will be included in
|
|
8938
|
+
the results for authorized Spotify Developer Applications. More information about the deprecated
|
|
8939
|
+
functionality can be found on the <a href="https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api"
|
|
8940
|
+
target="_blank">Spotify Developer Forum Blog</a> page.
|
|
8941
|
+
<span>
|
|
8899
8942
|
|
|
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
8943
|
<details>
|
|
8906
8944
|
<summary>Sample Code - Manual Paging</summary>
|
|
8907
8945
|
```python
|
|
@@ -11757,7 +11795,7 @@ class SpotifyClient:
|
|
|
11757
11795
|
This API only works for users who have Spotify Premium.
|
|
11758
11796
|
|
|
11759
11797
|
The order of execution is not guaranteed when you use this API with other Player API endpoints.
|
|
11760
|
-
|
|
11798
|
+
|
|
11761
11799
|
<details>
|
|
11762
11800
|
<summary>Sample Code</summary>
|
|
11763
11801
|
```python
|
|
@@ -11782,9 +11820,13 @@ class SpotifyClient:
|
|
|
11782
11820
|
# resolve the device object from the device id.
|
|
11783
11821
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
11784
11822
|
|
|
11823
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
11824
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
11825
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
11826
|
+
|
|
11785
11827
|
# is this an active Sonos device?
|
|
11786
11828
|
# 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):
|
|
11829
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
11788
11830
|
|
|
11789
11831
|
_logsi.LogVerbose("Issuing command to Sonos device %s: PAUSE" % (scDevice.Title))
|
|
11790
11832
|
sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
|
|
@@ -11803,7 +11845,7 @@ class SpotifyClient:
|
|
|
11803
11845
|
|
|
11804
11846
|
# execute spotify web api request.
|
|
11805
11847
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/pause')
|
|
11806
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
11848
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
11807
11849
|
msg.UrlParameters = urlParms
|
|
11808
11850
|
self.MakeRequest('PUT', msg)
|
|
11809
11851
|
|
|
@@ -11944,10 +11986,8 @@ class SpotifyClient:
|
|
|
11944
11986
|
# resolve the device object from the device idl activate if it's dormant.
|
|
11945
11987
|
scDevice = self._ResolveDeviceObject(deviceId, True)
|
|
11946
11988
|
|
|
11947
|
-
# are spotify web player credentials configured?
|
|
11948
|
-
#
|
|
11949
|
-
# Spotify Web API endpoint that starts playback on the device; this token has the
|
|
11950
|
-
# required access to control "restricted" devices (such as Sonos).
|
|
11989
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
11990
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
11951
11991
|
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
11952
11992
|
|
|
11953
11993
|
# is this an active Sonos device?
|
|
@@ -12303,10 +12343,8 @@ class SpotifyClient:
|
|
|
12303
12343
|
# resolve the device object from the device id; activate if it's dormant.
|
|
12304
12344
|
scDevice = self._ResolveDeviceObject(deviceId, True)
|
|
12305
12345
|
|
|
12306
|
-
# are spotify web player credentials configured?
|
|
12307
|
-
#
|
|
12308
|
-
# Spotify Web API endpoint that starts playback on the device; this token has the
|
|
12309
|
-
# required access to control "restricted" devices (such as Sonos).
|
|
12346
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
12347
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
12310
12348
|
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
12311
12349
|
|
|
12312
12350
|
# is this an active Sonos device?
|
|
@@ -12474,9 +12512,13 @@ class SpotifyClient:
|
|
|
12474
12512
|
# we activate the device here, as it may have been paused for a long time.
|
|
12475
12513
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, True)
|
|
12476
12514
|
|
|
12515
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
12516
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
12517
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
12518
|
+
|
|
12477
12519
|
# is this an active Sonos device?
|
|
12478
12520
|
# 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):
|
|
12521
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
12480
12522
|
|
|
12481
12523
|
_logsi.LogVerbose("Issuing command to Sonos device %s: PLAY (RESUME)" % (scDevice.Title))
|
|
12482
12524
|
sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
|
|
@@ -12495,7 +12537,7 @@ class SpotifyClient:
|
|
|
12495
12537
|
|
|
12496
12538
|
# execute spotify web api request.
|
|
12497
12539
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/play')
|
|
12498
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
12540
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
12499
12541
|
msg.UrlParameters = urlParms
|
|
12500
12542
|
self.MakeRequest('PUT', msg)
|
|
12501
12543
|
|
|
@@ -12631,9 +12673,13 @@ class SpotifyClient:
|
|
|
12631
12673
|
newPositionMS = 0
|
|
12632
12674
|
positionMS = newPositionMS
|
|
12633
12675
|
|
|
12676
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
12677
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
12678
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
12679
|
+
|
|
12634
12680
|
# is this an active Sonos device?
|
|
12635
12681
|
# 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):
|
|
12682
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
12637
12683
|
|
|
12638
12684
|
sonosPosition:str = mediaPositionHMS_fromSeconds(positionMS / 1000) # convert from milliseconds to Sonos H:MM:SS format
|
|
12639
12685
|
_logsi.LogVerbose("Issuing command to Sonos device %s: SEEK (sonosPosition=%s)" % (scDevice.Title, sonosPosition))
|
|
@@ -12656,7 +12702,7 @@ class SpotifyClient:
|
|
|
12656
12702
|
|
|
12657
12703
|
# execute spotify web api request.
|
|
12658
12704
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/seek')
|
|
12659
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
12705
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
12660
12706
|
msg.UrlParameters = urlParms
|
|
12661
12707
|
self.MakeRequest('PUT', msg)
|
|
12662
12708
|
|
|
@@ -12743,9 +12789,13 @@ class SpotifyClient:
|
|
|
12743
12789
|
# resolve the device object from the device id.
|
|
12744
12790
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
12745
12791
|
|
|
12792
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
12793
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
12794
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
12795
|
+
|
|
12746
12796
|
# is this an active Sonos device?
|
|
12747
12797
|
# 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):
|
|
12798
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
12749
12799
|
|
|
12750
12800
|
_logsi.LogVerbose("Issuing command to Sonos device %s: NEXT" % (scDevice.Title))
|
|
12751
12801
|
sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
|
|
@@ -12764,7 +12814,7 @@ class SpotifyClient:
|
|
|
12764
12814
|
|
|
12765
12815
|
# execute spotify web api request.
|
|
12766
12816
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/next')
|
|
12767
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
12817
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
12768
12818
|
msg.UrlParameters = urlParms
|
|
12769
12819
|
self.MakeRequest('POST', msg)
|
|
12770
12820
|
|
|
@@ -12851,9 +12901,13 @@ class SpotifyClient:
|
|
|
12851
12901
|
# resolve the device object from the device id.
|
|
12852
12902
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
12853
12903
|
|
|
12904
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
12905
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
12906
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
12907
|
+
|
|
12854
12908
|
# is this an active Sonos device?
|
|
12855
12909
|
# 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):
|
|
12910
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
12857
12911
|
|
|
12858
12912
|
_logsi.LogVerbose("Issuing command to Sonos device %s: PREVIOUS" % (scDevice.Title))
|
|
12859
12913
|
sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
|
|
@@ -12872,7 +12926,7 @@ class SpotifyClient:
|
|
|
12872
12926
|
|
|
12873
12927
|
# execute spotify web api request.
|
|
12874
12928
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/previous')
|
|
12875
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
12929
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
12876
12930
|
msg.UrlParameters = urlParms
|
|
12877
12931
|
self.MakeRequest('POST', msg)
|
|
12878
12932
|
|
|
@@ -12967,9 +13021,13 @@ class SpotifyClient:
|
|
|
12967
13021
|
# resolve the device object from the device id.
|
|
12968
13022
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
12969
13023
|
|
|
13024
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
13025
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
13026
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
13027
|
+
|
|
12970
13028
|
# is this an active Sonos device?
|
|
12971
13029
|
# 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):
|
|
13030
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
12973
13031
|
|
|
12974
13032
|
# get current Spotify Connect player state.
|
|
12975
13033
|
playerState = self.GetPlayerPlaybackStateSonos(scDevice)
|
|
@@ -13008,7 +13066,7 @@ class SpotifyClient:
|
|
|
13008
13066
|
|
|
13009
13067
|
# execute spotify web api request.
|
|
13010
13068
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/repeat')
|
|
13011
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
13069
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
13012
13070
|
msg.UrlParameters = urlParms
|
|
13013
13071
|
self.MakeRequest('PUT', msg)
|
|
13014
13072
|
|
|
@@ -13102,9 +13160,13 @@ class SpotifyClient:
|
|
|
13102
13160
|
# resolve the device object from the device id.
|
|
13103
13161
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
13104
13162
|
|
|
13163
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
13164
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
13165
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
13166
|
+
|
|
13105
13167
|
# is this an active Sonos device?
|
|
13106
13168
|
# 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):
|
|
13169
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
13108
13170
|
|
|
13109
13171
|
# get current Spotify Connect player state.
|
|
13110
13172
|
playerState = self.GetPlayerPlaybackStateSonos(scDevice)
|
|
@@ -13144,7 +13206,7 @@ class SpotifyClient:
|
|
|
13144
13206
|
|
|
13145
13207
|
# execute spotify web api request.
|
|
13146
13208
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/shuffle')
|
|
13147
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
13209
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
13148
13210
|
msg.UrlParameters = urlParms
|
|
13149
13211
|
self.MakeRequest('PUT', msg)
|
|
13150
13212
|
|
|
@@ -13237,9 +13299,13 @@ class SpotifyClient:
|
|
|
13237
13299
|
# resolve the device object from the device id.
|
|
13238
13300
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
13239
13301
|
|
|
13302
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
13303
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
13304
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue(scDevice)
|
|
13305
|
+
|
|
13240
13306
|
# is this an active Sonos device?
|
|
13241
13307
|
# 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):
|
|
13308
|
+
if (scDevice is not None) and (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
13243
13309
|
|
|
13244
13310
|
_logsi.LogVerbose("Issuing command to Sonos device %s: VOLUME = %s" % (scDevice.Title, volumePercent))
|
|
13245
13311
|
sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
|
|
@@ -13261,7 +13327,7 @@ class SpotifyClient:
|
|
|
13261
13327
|
|
|
13262
13328
|
# execute spotify web api request.
|
|
13263
13329
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/volume')
|
|
13264
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
13330
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
13265
13331
|
msg.UrlParameters = urlParms
|
|
13266
13332
|
self.MakeRequest('PUT', msg)
|
|
13267
13333
|
|
|
@@ -13411,12 +13477,16 @@ class SpotifyClient:
|
|
|
13411
13477
|
# if not, then raise an exception as the Spotify Web API request will fail anyway.
|
|
13412
13478
|
self._CheckForDeviceNotFound(scDevice, deviceId)
|
|
13413
13479
|
|
|
13480
|
+
# are spotify web player credentials configured? if so, then we will use them to create
|
|
13481
|
+
# an elevated authorization access token for the Spotify Web API endpoint call.
|
|
13482
|
+
accessTokenHeaderValue:str = self._GetSpotifyWebPlayerTokenHeaderValue()
|
|
13483
|
+
|
|
13414
13484
|
# is this a Sonos device?
|
|
13415
13485
|
# if so, then control was automatically transferred by activating the device.
|
|
13416
13486
|
# note that the Spotify Web API cannot stop nor start play on the device either,
|
|
13417
13487
|
# as it's considered a restricted device and the Spotify Web API cannot control it!
|
|
13418
13488
|
# all requests to control the device will be made via the Sonos Controller API.
|
|
13419
|
-
if (scDevice.IsSonos):
|
|
13489
|
+
if (scDevice.IsSonos) and (accessTokenHeaderValue is None):
|
|
13420
13490
|
|
|
13421
13491
|
# get Sonos Controller instance for the device.
|
|
13422
13492
|
sonosPlayer:SoCo = self.SpotifyConnectDirectory.GetSonosPlayer(scDevice)
|
|
@@ -13431,13 +13501,11 @@ class SpotifyClient:
|
|
|
13431
13501
|
wasCmdIssued:bool = False
|
|
13432
13502
|
if currentTransportState == 'PLAYING':
|
|
13433
13503
|
if play == False:
|
|
13434
|
-
|
|
13435
|
-
sonosPlayer.pause()
|
|
13504
|
+
self.PlayerMediaPause(scDevice)
|
|
13436
13505
|
wasCmdIssued = True
|
|
13437
13506
|
elif currentTransportState in ['PAUSED_PLAYBACK','STOPPED','TRANSITIONING']:
|
|
13438
13507
|
if play == True:
|
|
13439
|
-
|
|
13440
|
-
sonosPlayer.play()
|
|
13508
|
+
self.PlayerMediaResume(scDevice)
|
|
13441
13509
|
wasCmdIssued = True
|
|
13442
13510
|
|
|
13443
13511
|
# give Sonos Controller time to process the change.
|
|
@@ -13480,7 +13548,7 @@ class SpotifyClient:
|
|
|
13480
13548
|
|
|
13481
13549
|
# execute spotify web api request.
|
|
13482
13550
|
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player')
|
|
13483
|
-
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
13551
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = accessTokenHeaderValue or self.AuthToken.HeaderValue
|
|
13484
13552
|
msg.RequestJson = reqData
|
|
13485
13553
|
self.MakeRequest('PUT', msg)
|
|
13486
13554
|
|
|
@@ -1048,35 +1048,45 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1048
1048
|
if (sonosPlayer is None):
|
|
1049
1049
|
raise SpotifyApiError("Could not find Sonos Controller instance for device: %s" % (device.Title), None, logsi=_logsi)
|
|
1050
1050
|
|
|
1051
|
+
# trace.
|
|
1052
|
+
_logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (device.Title), sonosPlayer.speaker_info)
|
|
1053
|
+
|
|
1051
1054
|
# does caller want the group coordinator?
|
|
1052
1055
|
if (returnCoordinator):
|
|
1053
1056
|
|
|
1054
1057
|
# TEST TODO - use to test Sonos coordinator functionality.
|
|
1055
1058
|
# _logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s" % (device.Title), sonosPlayer, colorValue=SIColors.Red)
|
|
1056
|
-
#
|
|
1059
|
+
#sonosPlayer._is_coordinator = False
|
|
1057
1060
|
|
|
1058
|
-
#
|
|
1061
|
+
# is Sonos Controller instance the group coordinator?
|
|
1059
1062
|
if (sonosPlayer.is_coordinator):
|
|
1060
|
-
_logsi.LogVerbose("Sonos Controller instance is a group coordinator for device: %s" % (device.Title))
|
|
1061
|
-
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s" % (device.Title), sonosPlayer)
|
|
1062
|
-
_logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (device.Title), sonosPlayer.speaker_info)
|
|
1063
|
-
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (group)" % (device.Title), sonosPlayer.group)
|
|
1064
|
-
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (zone_group_state)" % (device.Title), sonosPlayer.zone_group_state)
|
|
1065
|
-
return sonosPlayer
|
|
1066
1063
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (zone_group_state)" % (device.Title), sonosPlayer.zone_group_state)
|
|
1072
|
-
|
|
1073
|
-
# is the device part of a group? if so, then use the group coordinator.
|
|
1074
|
-
if (sonosPlayer.group is not None):
|
|
1075
|
-
if (sonosPlayer.group.coordinator is not None):
|
|
1076
|
-
sonosPlayerCoordinator:SoCo = sonosPlayer.group.coordinator
|
|
1077
|
-
_logsi.LogVerbose("Sonos Controller group coordinator \"%s\" (%s) will be used for device: %s" % (sonosPlayerCoordinator.player_name, sonosPlayerCoordinator.ip_address, device.Title))
|
|
1078
|
-
sonosPlayer = sonosPlayerCoordinator
|
|
1064
|
+
# yes - nothing else to do since it's already a group coordinator.
|
|
1065
|
+
_logsi.LogVerbose("Sonos Controller instance is a group coordinator")
|
|
1066
|
+
|
|
1067
|
+
else:
|
|
1079
1068
|
|
|
1069
|
+
# no - try to determine the group coordinator.
|
|
1070
|
+
_logsi.LogVerbose("Sonos Controller instance is NOT a group coordinator for device: %s" % (device.Title))
|
|
1071
|
+
|
|
1072
|
+
# is the device part of a group? if so, then use the `group.coordinator` value (if present).
|
|
1073
|
+
sonosPlayerCoordinator:SoCo = None
|
|
1074
|
+
if (sonosPlayer.group is not None):
|
|
1075
|
+
if (sonosPlayer.group.coordinator is not None):
|
|
1076
|
+
sonosPlayerCoordinator = sonosPlayer.group.coordinator
|
|
1077
|
+
sonosPlayer = sonosPlayerCoordinator
|
|
1078
|
+
_logsi.LogVerbose("Sonos Controller group coordinator \"%s\" (%s) will be used for device: %s" % (sonosPlayerCoordinator.player_name, sonosPlayerCoordinator.ip_address, device.Title))
|
|
1079
|
+
_logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (device.Title), sonosPlayerCoordinator.speaker_info)
|
|
1080
|
+
|
|
1081
|
+
# if group coordinator could not be found, then log a trace message indicating
|
|
1082
|
+
# this and that subsequent Sonos Controller operations will probably fail.
|
|
1083
|
+
# this only seems to happen for "orphaned" Sonos devices (e.g. uid="")
|
|
1084
|
+
# zone_group_state.groups = [ZoneGroup(uid='RINCON_5CAAFDF4E8DE01400:orphan', coordinator=None, members={SoCo("192.168.50.79")}), etc
|
|
1085
|
+
if (sonosPlayerCoordinator is None):
|
|
1086
|
+
_logsi.LogVerbose("Sonos Controller group coordinator could not be determined for device: %s; subsequent Sonos Controller API functions will probably fail!" % (device.Title), colorValue=SIColors.Red)
|
|
1087
|
+
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (group)" % (device.Title), sonosPlayer.group)
|
|
1088
|
+
|
|
1089
|
+
# return Sonos Controller instance to caller.
|
|
1080
1090
|
return sonosPlayer
|
|
1081
1091
|
|
|
1082
1092
|
|
|
@@ -1880,11 +1890,28 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1880
1890
|
|
|
1881
1891
|
try:
|
|
1882
1892
|
|
|
1883
|
-
#
|
|
1884
|
-
# use device ip address as the key, as Spotify Web API reports id=null for restricted devices in playerstate!
|
|
1893
|
+
# is this a Sonos device?
|
|
1885
1894
|
if (scDevice.IsSonos):
|
|
1895
|
+
|
|
1896
|
+
# create a Sonos Controller instance for the device, retrieve the Sonos speaker information,
|
|
1897
|
+
# and add it to the Sonos players collection. use device ip address as the key, as the
|
|
1898
|
+
# Spotify Web API reports "id=null" for restricted devices in playerstate!
|
|
1886
1899
|
_logsi.LogVerbose("Sonos device detected; creating Sonos Controller instance for device: %s (ip=%s)" % (scDevice.Title, zeroconfDiscoveryResult.HostIpAddress))
|
|
1887
|
-
|
|
1900
|
+
sonosPlayer:SoCo = SoCo(zeroconfDiscoveryResult.HostIpAddress)
|
|
1901
|
+
self._SonosPlayers[zeroconfDiscoveryResult.HostIpAddress] = sonosPlayer
|
|
1902
|
+
|
|
1903
|
+
try:
|
|
1904
|
+
sonosPlayer.get_speaker_info()
|
|
1905
|
+
except Exception as ex:
|
|
1906
|
+
_logsi.LogException("Could not get speaker info for Sonos Controller instance: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
1907
|
+
|
|
1908
|
+
# trace.
|
|
1909
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
1910
|
+
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s" % (scDevice.Title), sonosPlayer)
|
|
1911
|
+
_logsi.LogDictionary(SILevel.Verbose, "Sonos Controller instance for device: %s (speaker_info)" % (scDevice.Title), sonosPlayer.speaker_info)
|
|
1912
|
+
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (group)" % (scDevice.Title), sonosPlayer.group)
|
|
1913
|
+
_logsi.LogObject(SILevel.Verbose, "Sonos Controller instance for device: %s (zone_group_state)" % (scDevice.Title), sonosPlayer.zone_group_state)
|
|
1914
|
+
_logsi.LogEnumerable(SILevel.Verbose, "Sonos Controller instance for device: %s (all_zones)" % (scDevice.Title), sonosPlayer.all_zones)
|
|
1888
1915
|
|
|
1889
1916
|
except:
|
|
1890
1917
|
|
|
File without changes
|
|
File without changes
|
{spotifywebapiPython-1.0.174.dist-info → spotifywebapiPython-1.0.176.dist-info}/top_level.txt
RENAMED
|
File without changes
|