spotifywebapiPython 1.0.111__py3-none-any.whl → 1.0.113__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.111.dist-info → spotifywebapiPython-1.0.113.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.111.dist-info → spotifywebapiPython-1.0.113.dist-info}/RECORD +25 -25
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +17 -2
- spotifywebapipython/docs/index.html +2 -2
- spotifywebapipython/docs/search.js +1 -1
- spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +62 -4
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/artistinfotourevent.py +2 -1
- spotifywebapipython/models/pageobject.py +28 -0
- spotifywebapipython/models/playerqueueinfo.py +34 -4
- spotifywebapipython/models/trackpage.py +40 -0
- spotifywebapipython/spotifyclient.py +215 -19
- spotifywebapipython/zeroconfapi/zeroconfconnect.py +23 -23
- {spotifywebapiPython-1.0.111.dist-info → spotifywebapiPython-1.0.113.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.111.dist-info → spotifywebapiPython-1.0.113.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.111.dist-info → spotifywebapiPython-1.0.113.dist-info}/top_level.txt +0 -0
|
@@ -58,6 +58,46 @@ class TrackPage(PageObject):
|
|
|
58
58
|
return self._Items
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def GetUris(self, afterUri:str=None) -> str:
|
|
62
|
+
"""
|
|
63
|
+
Gets a comma-delimited list of uri's contained in the underlying `Items` list.
|
|
64
|
+
|
|
65
|
+
args:
|
|
66
|
+
afterUri (str):
|
|
67
|
+
If specified, the process will look for this uri before adding subsequent
|
|
68
|
+
uri's to the returned results. This allows you to skip uri's that have
|
|
69
|
+
recently been played.
|
|
70
|
+
|
|
71
|
+
This is a convenience method so one does not have to loop through the `Items`
|
|
72
|
+
array of PlayHistory objects to get the list of uri's.
|
|
73
|
+
"""
|
|
74
|
+
result:str = ""
|
|
75
|
+
addUri:bool = False
|
|
76
|
+
item:Track
|
|
77
|
+
|
|
78
|
+
# add uri's from the beginning if no `afterUri` argument was passed.
|
|
79
|
+
if (afterUri is None) or (len(afterUri.strip()) == 0):
|
|
80
|
+
addUri = True
|
|
81
|
+
|
|
82
|
+
for item in self._Items:
|
|
83
|
+
|
|
84
|
+
if (not addUri):
|
|
85
|
+
|
|
86
|
+
# check for `afterUri` match; if found, we will start adding uri's
|
|
87
|
+
# to the list with the next item.
|
|
88
|
+
if (afterUri == item.Uri):
|
|
89
|
+
addUri = True
|
|
90
|
+
|
|
91
|
+
elif (addUri):
|
|
92
|
+
|
|
93
|
+
# add item uri to the list.
|
|
94
|
+
if (len(result) > 0):
|
|
95
|
+
result += ","
|
|
96
|
+
result += item.Uri
|
|
97
|
+
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
|
|
61
101
|
def ToString(self, includeItems:bool=False) -> str:
|
|
62
102
|
"""
|
|
63
103
|
Returns a displayable string representation of the class.
|
|
@@ -9,7 +9,8 @@ import time
|
|
|
9
9
|
from typing import Tuple, Callable
|
|
10
10
|
from urllib3 import PoolManager, Timeout, HTTPResponse
|
|
11
11
|
from urllib.parse import urlencode
|
|
12
|
-
from lxml.etree import fromstring, Element
|
|
12
|
+
from lxml.etree import fromstring, Element, tostring
|
|
13
|
+
from lxml import etree
|
|
13
14
|
from zeroconf import Zeroconf
|
|
14
15
|
|
|
15
16
|
# our package imports.
|
|
@@ -838,6 +839,8 @@ class SpotifyClient:
|
|
|
838
839
|
deviceId:str=None
|
|
839
840
|
) -> None:
|
|
840
841
|
"""
|
|
842
|
+
DEPRECATED - use `AddPlayerQueueItems` method instead.
|
|
843
|
+
|
|
841
844
|
Add an item to the end of the user's current playback queue.
|
|
842
845
|
|
|
843
846
|
This method requires the `user-modify-playback-state` scope.
|
|
@@ -862,13 +865,6 @@ class SpotifyClient:
|
|
|
862
865
|
This API only works for users who have Spotify Premium.
|
|
863
866
|
|
|
864
867
|
The order of execution is not guaranteed when you use this API with other Player API endpoints.
|
|
865
|
-
|
|
866
|
-
<details>
|
|
867
|
-
<summary>Sample Code</summary>
|
|
868
|
-
```python
|
|
869
|
-
.. include:: ../docs/include/samplecode/SpotifyClient/AddPlayerQueueItem.py
|
|
870
|
-
```
|
|
871
|
-
</details>
|
|
872
868
|
"""
|
|
873
869
|
apiMethodName:str = 'AddPlayerQueueItem'
|
|
874
870
|
apiMethodParms:SIMethodParmListContext = None
|
|
@@ -920,6 +916,139 @@ class SpotifyClient:
|
|
|
920
916
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
921
917
|
|
|
922
918
|
|
|
919
|
+
def AddPlayerQueueItems(
|
|
920
|
+
self,
|
|
921
|
+
uris:str,
|
|
922
|
+
deviceId:str=None,
|
|
923
|
+
verifyDeviceId:bool=True,
|
|
924
|
+
delay:float=0.15,
|
|
925
|
+
) -> None:
|
|
926
|
+
"""
|
|
927
|
+
Add one or more items to the end of the user's current playback queue.
|
|
928
|
+
|
|
929
|
+
This method requires the `user-modify-playback-state` scope.
|
|
930
|
+
|
|
931
|
+
Args:
|
|
932
|
+
uris (str):
|
|
933
|
+
A list of Spotify track or episode URIs to add to the queue; can be track or episode URIs.
|
|
934
|
+
All URIs must be of the same type - you cannot mix and match tracks and episodes!
|
|
935
|
+
Example: [`spotify:track:6zd8T1PBe9JFHmuVnurdRp` ,`spotify:track:1kWUud3vY5ij5r62zxpTRy`].
|
|
936
|
+
It can also be specified as a comma-delimited string.
|
|
937
|
+
Example: `spotify:track:6zd8T1PBe9JFHmuVnurdRp,spotify:track:1kWUud3vY5ij5r62zxpTRy`.
|
|
938
|
+
A maximum of 50 items can be added in one request.
|
|
939
|
+
deviceId (str):
|
|
940
|
+
The id or name of the device this command is targeting.
|
|
941
|
+
If not supplied, the user's currently active device is the target.
|
|
942
|
+
Example: `0d1841b0976bae2a3a310dd74c0f3df354899bc8`
|
|
943
|
+
Example: `Web Player (Chrome)`
|
|
944
|
+
verifyDeviceId (bool):
|
|
945
|
+
True to verify a device id is active; otherwise, false to assume that a
|
|
946
|
+
device id is already active.
|
|
947
|
+
Default is True.
|
|
948
|
+
delay (float):
|
|
949
|
+
Time delay (in seconds) to wait AFTER issuing the add request. This delay will give the
|
|
950
|
+
Spotify web api time to process the queue change before another command is issued.
|
|
951
|
+
Default is 0.15; value range is 0 - 10.
|
|
952
|
+
|
|
953
|
+
Raises:
|
|
954
|
+
SpotifyWebApiError:
|
|
955
|
+
If the Spotify Web API request was for a non-authorization service
|
|
956
|
+
and the response contains error information.
|
|
957
|
+
SpotifApiError:
|
|
958
|
+
If the method fails for any other reason.
|
|
959
|
+
|
|
960
|
+
This API only works for users who have Spotify Premium.
|
|
961
|
+
|
|
962
|
+
The order of execution is not guaranteed when you use this API with other Player API endpoints.
|
|
963
|
+
|
|
964
|
+
The Spotify Web API endpoint is called to add each item individually.
|
|
965
|
+
The Spotify Web API does not currently support adding more than 1 item to the queue at a time.
|
|
966
|
+
|
|
967
|
+
<details>
|
|
968
|
+
<summary>Sample Code</summary>
|
|
969
|
+
```python
|
|
970
|
+
.. include:: ../docs/include/samplecode/SpotifyClient/AddPlayerQueueItems.py
|
|
971
|
+
```
|
|
972
|
+
</details>
|
|
973
|
+
"""
|
|
974
|
+
apiMethodName:str = 'AddPlayerQueueItems'
|
|
975
|
+
apiMethodParms:SIMethodParmListContext = None
|
|
976
|
+
|
|
977
|
+
try:
|
|
978
|
+
|
|
979
|
+
# trace.
|
|
980
|
+
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
981
|
+
apiMethodParms.AppendKeyValue("uris", uris)
|
|
982
|
+
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
983
|
+
apiMethodParms.AppendKeyValue("verifyDeviceId", verifyDeviceId)
|
|
984
|
+
apiMethodParms.AppendKeyValue("delay", delay)
|
|
985
|
+
_logsi.LogMethodParmList(SILevel.Verbose, "Add items to playback queue", apiMethodParms)
|
|
986
|
+
|
|
987
|
+
# validations.
|
|
988
|
+
delay = validateDelay(delay, 0.15, 10)
|
|
989
|
+
if (verifyDeviceId is None):
|
|
990
|
+
verifyDeviceId = True
|
|
991
|
+
|
|
992
|
+
# build a list of all item uri's.
|
|
993
|
+
# remove any leading / trailing spaces in case user put a space between the items.
|
|
994
|
+
arrUris:list[str] = None
|
|
995
|
+
if isinstance(uris, list):
|
|
996
|
+
arrUris = uris
|
|
997
|
+
else:
|
|
998
|
+
arrUris = uris.split(',')
|
|
999
|
+
for idx in range(0, len(arrUris)):
|
|
1000
|
+
arrUris[idx] = arrUris[idx].strip()
|
|
1001
|
+
|
|
1002
|
+
# if deviceId was not specified, then verify that there is an active player device.
|
|
1003
|
+
if verifyDeviceId:
|
|
1004
|
+
if deviceId is None:
|
|
1005
|
+
deviceId:str = self.VerifyPlayerActiveDeviceId()
|
|
1006
|
+
|
|
1007
|
+
# check for device name; convert to an id if a name was supplied.
|
|
1008
|
+
if verifyDeviceId:
|
|
1009
|
+
deviceId = self.PlayerConvertDeviceNameToId(deviceId)
|
|
1010
|
+
|
|
1011
|
+
# process all uri's.
|
|
1012
|
+
for idx in range(0, len(arrUris)):
|
|
1013
|
+
|
|
1014
|
+
# build spotify web api request parameters.
|
|
1015
|
+
urlParms:dict = \
|
|
1016
|
+
{
|
|
1017
|
+
'uri': arrUris[idx]
|
|
1018
|
+
}
|
|
1019
|
+
if deviceId is not None:
|
|
1020
|
+
urlParms['device_id'] = deviceId
|
|
1021
|
+
|
|
1022
|
+
# execute spotify web api request.
|
|
1023
|
+
msg:SpotifyApiMessage = SpotifyApiMessage(apiMethodName, '/me/player/queue')
|
|
1024
|
+
msg.RequestHeaders[self.AuthToken.HeaderKey] = self.AuthToken.HeaderValue
|
|
1025
|
+
msg.UrlParameters = urlParms
|
|
1026
|
+
self.MakeRequest('POST', msg)
|
|
1027
|
+
|
|
1028
|
+
# give spotify web api time to process the change.
|
|
1029
|
+
if delay > 0:
|
|
1030
|
+
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
1031
|
+
time.sleep(delay)
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
# process results.
|
|
1035
|
+
# no results to process - this is pass or fail.
|
|
1036
|
+
return
|
|
1037
|
+
|
|
1038
|
+
except SpotifyApiError: raise # pass handled exceptions on thru
|
|
1039
|
+
except SpotifyWebApiError: raise # pass handled exceptions on thru
|
|
1040
|
+
except SpotifyWebApiAuthenticationError: raise # pass handled exceptions on thru
|
|
1041
|
+
except Exception as ex:
|
|
1042
|
+
|
|
1043
|
+
# format unhandled exception.
|
|
1044
|
+
raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
|
|
1045
|
+
|
|
1046
|
+
finally:
|
|
1047
|
+
|
|
1048
|
+
# trace.
|
|
1049
|
+
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
1050
|
+
|
|
1051
|
+
|
|
923
1052
|
def AddPlaylistCoverImage(self,
|
|
924
1053
|
playlistId:str,
|
|
925
1054
|
imagePath:str
|
|
@@ -2727,7 +2856,8 @@ class SpotifyClient:
|
|
|
2727
2856
|
|
|
2728
2857
|
# update result object with final paging details.
|
|
2729
2858
|
result.Total = pageObj.Total
|
|
2730
|
-
|
|
2859
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
2860
|
+
|
|
2731
2861
|
# sort result items.
|
|
2732
2862
|
if (len(result.Items) > 0):
|
|
2733
2863
|
if (sortResult is True):
|
|
@@ -2894,6 +3024,7 @@ class SpotifyClient:
|
|
|
2894
3024
|
|
|
2895
3025
|
# update result object with final paging details.
|
|
2896
3026
|
result.Total = pageObj.Total
|
|
3027
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
2897
3028
|
|
|
2898
3029
|
# sort result items.
|
|
2899
3030
|
if (len(result.Items) > 0):
|
|
@@ -3161,6 +3292,7 @@ class SpotifyClient:
|
|
|
3161
3292
|
|
|
3162
3293
|
# update result object with final paging details.
|
|
3163
3294
|
result.Total = pageObj.Total
|
|
3295
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
3164
3296
|
|
|
3165
3297
|
# do not sort items, as they are in playable order.
|
|
3166
3298
|
|
|
@@ -3428,6 +3560,7 @@ class SpotifyClient:
|
|
|
3428
3560
|
|
|
3429
3561
|
# update result object with final paging details.
|
|
3430
3562
|
result.Total = pageObj.Total
|
|
3563
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
3431
3564
|
|
|
3432
3565
|
# sort result items.
|
|
3433
3566
|
if (len(result.Items) > 0):
|
|
@@ -3638,12 +3771,21 @@ class SpotifyClient:
|
|
|
3638
3771
|
for elm in attrElements:
|
|
3639
3772
|
innerText = _xmlGetInnerText(elm)
|
|
3640
3773
|
if innerText is not None:
|
|
3641
|
-
|
|
3774
|
+
innerTextLCase = innerText.lower()
|
|
3775
|
+
if innerTextLCase == 'see all':
|
|
3642
3776
|
break
|
|
3643
|
-
|
|
3777
|
+
if innerTextLCase.startswith('view all upcoming concerts'):
|
|
3778
|
+
break;
|
|
3779
|
+
|
|
3780
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
3781
|
+
xml_string = etree.tostring(elm, pretty_print=True, encoding="utf-8")
|
|
3782
|
+
_logsi.LogXml(SILevel.Verbose, "Element match: '%s' (html)" % (attrXPath), xml_string.decode("utf-8"))
|
|
3783
|
+
|
|
3644
3784
|
event:ArtistInfoTourEvent = ArtistInfoTourEvent()
|
|
3645
|
-
|
|
3646
|
-
|
|
3785
|
+
|
|
3786
|
+
attrXPath = './/time/@dateTime'
|
|
3787
|
+
elmChild = elm.xpath(attrXPath)
|
|
3788
|
+
if (elmChild is not None) and (len(elmChild) > 0):
|
|
3647
3789
|
try:
|
|
3648
3790
|
attrValue = str(elmChild[0])
|
|
3649
3791
|
idx:int = attrValue.rfind('-')
|
|
@@ -3652,12 +3794,17 @@ class SpotifyClient:
|
|
|
3652
3794
|
except Exception as ex:
|
|
3653
3795
|
_logsi.LogException("Element value '%s' (%s) could not be parsed to a datetime object" % (elmChild[0], attrValue), ex, logToSystemLogger=False)
|
|
3654
3796
|
event.EventDateTime = None
|
|
3655
|
-
|
|
3656
|
-
|
|
3797
|
+
|
|
3798
|
+
attrXPath = './/h3/text()'
|
|
3799
|
+
elmChild = elm.xpath(attrXPath)
|
|
3800
|
+
if (elmChild is not None) and (len(elmChild) > 0):
|
|
3657
3801
|
event.Title = elmChild[0]
|
|
3658
|
-
|
|
3659
|
-
|
|
3802
|
+
|
|
3803
|
+
attrXPath = './/p/text()'
|
|
3804
|
+
elmChild = elm.xpath(attrXPath)
|
|
3805
|
+
if (elmChild is not None) and (len(elmChild) > 0):
|
|
3660
3806
|
event.VenueName = elmChild[0]
|
|
3807
|
+
|
|
3661
3808
|
result.TourEvents.append(event)
|
|
3662
3809
|
_logsi.LogObject(SILevel.Verbose, '%s: "%s" (%s)' % (type(event).__name__, event.Title, event.EventDateTime), event, excludeNonPublic=True)
|
|
3663
3810
|
|
|
@@ -4006,6 +4153,7 @@ class SpotifyClient:
|
|
|
4006
4153
|
# update result object with final paging details.
|
|
4007
4154
|
result.Total = pageObj.Total
|
|
4008
4155
|
result.CursorAfter = pageObj.CursorAfter
|
|
4156
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
4009
4157
|
|
|
4010
4158
|
# sort result items.
|
|
4011
4159
|
if (len(result.Items) > 0):
|
|
@@ -4386,6 +4534,7 @@ class SpotifyClient:
|
|
|
4386
4534
|
|
|
4387
4535
|
# update result object with final paging details.
|
|
4388
4536
|
result.Total = pageObj.Total
|
|
4537
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
4389
4538
|
|
|
4390
4539
|
# no sorting, as chapters are in playable order.
|
|
4391
4540
|
|
|
@@ -4538,6 +4687,7 @@ class SpotifyClient:
|
|
|
4538
4687
|
|
|
4539
4688
|
# update result object with final paging details.
|
|
4540
4689
|
result.Total = pageObj.Total
|
|
4690
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
4541
4691
|
|
|
4542
4692
|
# sort result items.
|
|
4543
4693
|
if (len(result.Items) > 0):
|
|
@@ -4931,6 +5081,7 @@ class SpotifyClient:
|
|
|
4931
5081
|
|
|
4932
5082
|
# update result object with final paging details.
|
|
4933
5083
|
result.Total = pageObj.Total
|
|
5084
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
4934
5085
|
|
|
4935
5086
|
# sort result items.
|
|
4936
5087
|
if (len(result.Items) > 0):
|
|
@@ -5216,6 +5367,7 @@ class SpotifyClient:
|
|
|
5216
5367
|
|
|
5217
5368
|
# update result object with final paging details.
|
|
5218
5369
|
result.Total = pageObj.Total
|
|
5370
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
5219
5371
|
|
|
5220
5372
|
# sort result items.
|
|
5221
5373
|
if (len(result.Items) > 0):
|
|
@@ -5677,6 +5829,7 @@ class SpotifyClient:
|
|
|
5677
5829
|
|
|
5678
5830
|
# update result object with final paging details.
|
|
5679
5831
|
result.Total = pageObj.Total
|
|
5832
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
5680
5833
|
|
|
5681
5834
|
# sort result items.
|
|
5682
5835
|
if (len(result.Items) > 0):
|
|
@@ -5969,6 +6122,7 @@ class SpotifyClient:
|
|
|
5969
6122
|
|
|
5970
6123
|
# update result object with final paging details.
|
|
5971
6124
|
result.Total = pageObj.Total
|
|
6125
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
5972
6126
|
|
|
5973
6127
|
# sort result items.
|
|
5974
6128
|
if (len(result.Items) > 0):
|
|
@@ -7376,6 +7530,7 @@ class SpotifyClient:
|
|
|
7376
7530
|
result.Total = result.ItemsCount
|
|
7377
7531
|
result.CursorAfter = pageObj.CursorAfter
|
|
7378
7532
|
result.CursorBefore = pageObj.CursorBefore
|
|
7533
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
7379
7534
|
|
|
7380
7535
|
# trace.
|
|
7381
7536
|
_logsi.LogObject(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(result).__name__), result, excludeNonPublic=True)
|
|
@@ -7799,6 +7954,7 @@ class SpotifyClient:
|
|
|
7799
7954
|
|
|
7800
7955
|
# update result object with final paging details.
|
|
7801
7956
|
result.Total = pageObj.Total
|
|
7957
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
7802
7958
|
|
|
7803
7959
|
# do not sort this list, as it is in play order.
|
|
7804
7960
|
|
|
@@ -7951,6 +8107,7 @@ class SpotifyClient:
|
|
|
7951
8107
|
|
|
7952
8108
|
# update result object with final paging details.
|
|
7953
8109
|
result.Total = pageObj.Total
|
|
8110
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
7954
8111
|
|
|
7955
8112
|
# sort result items.
|
|
7956
8113
|
if (len(result.Items) > 0):
|
|
@@ -8112,6 +8269,7 @@ class SpotifyClient:
|
|
|
8112
8269
|
|
|
8113
8270
|
# update result object with final paging details.
|
|
8114
8271
|
result.Total = pageObj.Total
|
|
8272
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
8115
8273
|
|
|
8116
8274
|
# sort result items.
|
|
8117
8275
|
if (len(result.Items) > 0):
|
|
@@ -8392,6 +8550,7 @@ class SpotifyClient:
|
|
|
8392
8550
|
|
|
8393
8551
|
# update result object with final paging details.
|
|
8394
8552
|
result.Total = pageObj.Total
|
|
8553
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
8395
8554
|
|
|
8396
8555
|
# no sort, as items are in playable order.
|
|
8397
8556
|
|
|
@@ -8571,6 +8730,7 @@ class SpotifyClient:
|
|
|
8571
8730
|
|
|
8572
8731
|
# update result object with final paging details.
|
|
8573
8732
|
result.Total = pageObj.Total
|
|
8733
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
8574
8734
|
|
|
8575
8735
|
# sort result items.
|
|
8576
8736
|
if (len(result.Items) > 0):
|
|
@@ -8927,9 +9087,16 @@ class SpotifyClient:
|
|
|
8927
9087
|
# we will bypass disconnects for librespot devices (e.g. spotifyd, etc), as the
|
|
8928
9088
|
# Spotify Connect Zeroconf `resetUsers` endpoint is not implemented and will
|
|
8929
9089
|
# generate a 404 request response.
|
|
8930
|
-
if (info.BrandDisplayName != 'librespot'):
|
|
9090
|
+
if (info.BrandDisplayName != 'librespot'):
|
|
9091
|
+
|
|
8931
9092
|
_logsi.LogVerbose("Issuing Disconnect to Spotify Connect device '%s' for current user context '%s'" % (deviceResult.Title, deviceActiveUser))
|
|
8932
9093
|
zcfResult = zconn.Disconnect(delay)
|
|
9094
|
+
|
|
9095
|
+
# delay just a little to give the device time to process the disconnect.
|
|
9096
|
+
# some devices only support a single connection on the spotify connect
|
|
9097
|
+
# zeroconf webserver, so we have to give it a little time to process the
|
|
9098
|
+
# disconnect command before we try to call the Connect method.
|
|
9099
|
+
time.sleep(0.350)
|
|
8933
9100
|
|
|
8934
9101
|
# connect the device to OUR Spotify Connect user context.
|
|
8935
9102
|
# note that the result here only indicates that the connect was submitted - NOT that it was successful!
|
|
@@ -9540,6 +9707,7 @@ class SpotifyClient:
|
|
|
9540
9707
|
|
|
9541
9708
|
# update result object with final paging details.
|
|
9542
9709
|
result.Total = pageObj.Total
|
|
9710
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
9543
9711
|
|
|
9544
9712
|
# sort result items.
|
|
9545
9713
|
if (len(result.Items) > 0):
|
|
@@ -10468,6 +10636,7 @@ class SpotifyClient:
|
|
|
10468
10636
|
|
|
10469
10637
|
# update result object with final paging details.
|
|
10470
10638
|
result.Total = pageObj.Total
|
|
10639
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
10471
10640
|
|
|
10472
10641
|
# sort result items.
|
|
10473
10642
|
if (len(result.Items) > 0):
|
|
@@ -10634,6 +10803,7 @@ class SpotifyClient:
|
|
|
10634
10803
|
|
|
10635
10804
|
# update result object with final paging details.
|
|
10636
10805
|
result.Total = pageObj.Total
|
|
10806
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
10637
10807
|
|
|
10638
10808
|
# sort result items.
|
|
10639
10809
|
if (len(result.Items) > 0):
|
|
@@ -11262,6 +11432,25 @@ class SpotifyClient:
|
|
|
11262
11432
|
# if so, then use the found device id in case a device name was specified.
|
|
11263
11433
|
if scDevice is not None:
|
|
11264
11434
|
deviceId = scDevice.Id
|
|
11435
|
+
|
|
11436
|
+
# left this here in case we want to implement it in the future.
|
|
11437
|
+
|
|
11438
|
+
# # are we backfilling the player queue? if so, we can only have 50 uris total.
|
|
11439
|
+
# if (backfillUrisPageOffset > -1) and (len(arrUris) < 50):
|
|
11440
|
+
|
|
11441
|
+
# # are we playing tracks?
|
|
11442
|
+
# if (len(arrUris) > 0) and (SpotifyClient.GetTypeFromUri(arrUris[0]) == 'track'):
|
|
11443
|
+
|
|
11444
|
+
# _logsi.LogVerbose("Backfilling player queue items from users top tracks")
|
|
11445
|
+
|
|
11446
|
+
# # get list of users top tracks (50 max items).
|
|
11447
|
+
# pageObj:TrackPage = self.GetUsersTopTracks(timeRange='medium_term', offset=backfillUrisPageOffset, limit=(50 - len(arrUris)), sortResult=False)
|
|
11448
|
+
|
|
11449
|
+
# # add them to the player queue; no need to validate the device id, since we
|
|
11450
|
+
# # know there is an active device id by this point.
|
|
11451
|
+
# item:Track
|
|
11452
|
+
# for item in pageObj.Items:
|
|
11453
|
+
# arrUris.append(item.Uri)
|
|
11265
11454
|
|
|
11266
11455
|
# build spotify web api request parameters.
|
|
11267
11456
|
reqData:dict = \
|
|
@@ -11286,7 +11475,7 @@ class SpotifyClient:
|
|
|
11286
11475
|
if delay > 0:
|
|
11287
11476
|
_logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
|
|
11288
11477
|
time.sleep(delay)
|
|
11289
|
-
|
|
11478
|
+
|
|
11290
11479
|
# process results.
|
|
11291
11480
|
# no results to process - this is pass or fail.
|
|
11292
11481
|
return
|
|
@@ -14007,6 +14196,7 @@ class SpotifyClient:
|
|
|
14007
14196
|
|
|
14008
14197
|
# update result object with final paging details.
|
|
14009
14198
|
result.Total = pageObj.Total
|
|
14199
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
14010
14200
|
|
|
14011
14201
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
14012
14202
|
|
|
@@ -14199,6 +14389,7 @@ class SpotifyClient:
|
|
|
14199
14389
|
|
|
14200
14390
|
# update result object with final paging details.
|
|
14201
14391
|
result.Total = pageObj.Total
|
|
14392
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
14202
14393
|
|
|
14203
14394
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
14204
14395
|
|
|
@@ -14393,6 +14584,7 @@ class SpotifyClient:
|
|
|
14393
14584
|
|
|
14394
14585
|
# update result object with final paging details.
|
|
14395
14586
|
result.Total = pageObj.Total
|
|
14587
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
14396
14588
|
|
|
14397
14589
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
14398
14590
|
|
|
@@ -14585,6 +14777,7 @@ class SpotifyClient:
|
|
|
14585
14777
|
|
|
14586
14778
|
# update result object with final paging details.
|
|
14587
14779
|
result.Total = pageObj.Total
|
|
14780
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
14588
14781
|
|
|
14589
14782
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
14590
14783
|
|
|
@@ -14777,6 +14970,7 @@ class SpotifyClient:
|
|
|
14777
14970
|
|
|
14778
14971
|
# update result object with final paging details.
|
|
14779
14972
|
result.Total = pageObj.Total
|
|
14973
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
14780
14974
|
|
|
14781
14975
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
14782
14976
|
|
|
@@ -14969,6 +15163,7 @@ class SpotifyClient:
|
|
|
14969
15163
|
|
|
14970
15164
|
# update result object with final paging details.
|
|
14971
15165
|
result.Total = pageObj.Total
|
|
15166
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
14972
15167
|
|
|
14973
15168
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
14974
15169
|
|
|
@@ -15161,6 +15356,7 @@ class SpotifyClient:
|
|
|
15161
15356
|
|
|
15162
15357
|
# update result object with final paging details.
|
|
15163
15358
|
result.Total = pageObj.Total
|
|
15359
|
+
result.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
15164
15360
|
|
|
15165
15361
|
# do not sort, as spotify uses intelligent AI to return results in its order.
|
|
15166
15362
|
|
|
@@ -543,8 +543,8 @@ class ZeroconfConnect:
|
|
|
543
543
|
|
|
544
544
|
# set request headers.
|
|
545
545
|
reqHeaders:dict = {
|
|
546
|
-
|
|
547
|
-
|
|
546
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
547
|
+
"Connection": "close"
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
# set defaults used by most manufacturers - we will tailor these in the
|
|
@@ -552,14 +552,14 @@ class ZeroconfConnect:
|
|
|
552
552
|
# - exclude origin device information from the addUser request.
|
|
553
553
|
# - tokenType should be set to 'default'.
|
|
554
554
|
includeOriginDeviceInfo:bool = False
|
|
555
|
-
tokenType:str =
|
|
555
|
+
tokenType:str = "default"
|
|
556
556
|
|
|
557
557
|
# include the origin device information if PublicKey="INVALID" (base64 encoded) and availability="NOT-LOADED".
|
|
558
|
-
if (info.PublicKey ==
|
|
558
|
+
if (info.PublicKey == "SU5WQUxJRA==") and (info.Availability == "NOT-LOADED"):
|
|
559
559
|
includeOriginDeviceInfo = True
|
|
560
560
|
|
|
561
561
|
# special processing for librespot, spotifyd.
|
|
562
|
-
if info.ModelDisplayName ==
|
|
562
|
+
if info.ModelDisplayName == "librespot":
|
|
563
563
|
|
|
564
564
|
# trace.
|
|
565
565
|
_logsi.LogVerbose("Spotify Connect token type is '%s' - using librespot authorization credentials to connect: '%s' (ip=%s)" % (info.TokenType, info.RemoteName, self._HostIpAddress))
|
|
@@ -649,20 +649,20 @@ class ZeroconfConnect:
|
|
|
649
649
|
# - tokenType should be set to 'default'.
|
|
650
650
|
clientKey:str = builder.dh_keys.PublicKeyBase64String
|
|
651
651
|
includeOriginDeviceInfo = False
|
|
652
|
-
tokenType =
|
|
652
|
+
tokenType = "default"
|
|
653
653
|
|
|
654
654
|
# formulate the blob.
|
|
655
655
|
blob = builder.build()
|
|
656
656
|
|
|
657
657
|
# set request parameters.
|
|
658
658
|
reqData={
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
659
|
+
"action": "addUser",
|
|
660
|
+
"version": self.Version, # info.Version,
|
|
661
|
+
"tokenType": tokenType,
|
|
662
|
+
"clientKey": clientKey,
|
|
663
|
+
"loginId": loginId or '', # canonical login id (e.g. "31l77fd87g8h9j00k89f07jf87ge")
|
|
664
|
+
"userName": credentials.username.decode("ascii"), # canonical user name (e.g. "31l77fd87g8h9j00k89f07jf87ge")
|
|
665
|
+
"blob": blob,
|
|
666
666
|
}
|
|
667
667
|
|
|
668
668
|
# are we including origin device information (e.g. deviceName, deviceId)?
|
|
@@ -1028,21 +1028,21 @@ class ZeroconfConnect:
|
|
|
1028
1028
|
delay = validateDelay(delay, 0.50, 10)
|
|
1029
1029
|
|
|
1030
1030
|
# set request endpoint.
|
|
1031
|
-
endpoint:str = self.GetEndpoint(
|
|
1031
|
+
endpoint:str = self.GetEndpoint("resetUsers")
|
|
1032
1032
|
if ignoreStatusResult is None:
|
|
1033
1033
|
ignoreStatusResult = False
|
|
1034
1034
|
|
|
1035
1035
|
# set request headers.
|
|
1036
1036
|
reqHeaders:dict = {
|
|
1037
|
-
|
|
1038
|
-
|
|
1037
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1038
|
+
"Connection": "close"
|
|
1039
1039
|
}
|
|
1040
1040
|
_logsi.LogDictionary(SILevel.Debug, "ZeroconfConnect http request: '%s' (headers)" % (endpoint), reqHeaders)
|
|
1041
1041
|
|
|
1042
1042
|
# set request parameters.
|
|
1043
1043
|
reqData={
|
|
1044
|
-
|
|
1045
|
-
|
|
1044
|
+
"action": "resetUsers",
|
|
1045
|
+
"version": self.Version,
|
|
1046
1046
|
}
|
|
1047
1047
|
_logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (data)" % (endpoint), reqData)
|
|
1048
1048
|
|
|
@@ -1195,19 +1195,19 @@ class ZeroconfConnect:
|
|
|
1195
1195
|
_logsi.LogMethodParmList(SILevel.Verbose, "Get information from Spotify Connect Zeroconf API (ip=%s)" % self._HostIpAddress, apiMethodParms)
|
|
1196
1196
|
|
|
1197
1197
|
# set request endpoint.
|
|
1198
|
-
endpoint:str = self.GetEndpoint(
|
|
1198
|
+
endpoint:str = self.GetEndpoint("getInfo")
|
|
1199
1199
|
|
|
1200
1200
|
# set request headers.
|
|
1201
1201
|
reqHeaders:dict = {
|
|
1202
|
-
|
|
1203
|
-
|
|
1202
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1203
|
+
"Connection": "close"
|
|
1204
1204
|
}
|
|
1205
1205
|
_logsi.LogDictionary(SILevel.Debug, "ZeroconfConnect http request: '%s' (headers)" % (endpoint), reqHeaders)
|
|
1206
1206
|
|
|
1207
1207
|
# set request parameters.
|
|
1208
1208
|
reqParams={
|
|
1209
|
-
|
|
1210
|
-
|
|
1209
|
+
"action": "getInfo",
|
|
1210
|
+
"version": self.Version,
|
|
1211
1211
|
}
|
|
1212
1212
|
_logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (params)" % (endpoint), reqParams)
|
|
1213
1213
|
|
|
File without changes
|
|
File without changes
|
{spotifywebapiPython-1.0.111.dist-info → spotifywebapiPython-1.0.113.dist-info}/top_level.txt
RENAMED
|
File without changes
|