spotifywebapiPython 1.0.48__py3-none-any.whl → 1.0.50__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.48.dist-info → spotifywebapiPython-1.0.50.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.48.dist-info → spotifywebapiPython-1.0.50.dist-info}/RECORD +24 -24
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +20 -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 +319 -3
- spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +20 -61
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/userprofile.py +24 -0
- spotifywebapipython/spotifyclient.py +505 -8
- spotifywebapipython/spotifydiscovery.py +32 -80
- spotifywebapipython/zeroconfapi/zeroconfconnect.py +10 -0
- spotifywebapipython/zeroconfapi/zeroconfgetinfo.py +21 -3
- {spotifywebapiPython-1.0.48.dist-info → spotifywebapiPython-1.0.50.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.48.dist-info → spotifywebapiPython-1.0.50.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.48.dist-info → spotifywebapiPython-1.0.50.dist-info}/top_level.txt +0 -0
|
@@ -8,9 +8,9 @@ from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo, ServiceStateChange,
|
|
|
8
8
|
|
|
9
9
|
# our package imports.
|
|
10
10
|
from .saappmessages import SAAppMessages
|
|
11
|
+
from .spotifyapierror import SpotifyApiError
|
|
11
12
|
from .sautils import export
|
|
12
13
|
from .models.device import Device
|
|
13
|
-
from .spotifyclient import SpotifyClient, SpotifyApiError
|
|
14
14
|
from .models import ZeroconfDiscoveryResult, ZeroconfProperty
|
|
15
15
|
|
|
16
16
|
# get smartinspect logger reference; create a new session for this module name.
|
|
@@ -32,43 +32,40 @@ class SpotifyDiscovery:
|
|
|
32
32
|
Click the **Sample Code** links in the individual methods for sample code examples.
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
def __init__(
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
zeroconfClient:Zeroconf=None,
|
|
38
|
+
printToConsole:bool=False
|
|
39
|
+
) -> None:
|
|
36
40
|
"""
|
|
37
41
|
Initializes a new instance of the class.
|
|
38
42
|
|
|
39
43
|
Args:
|
|
40
|
-
|
|
41
|
-
A
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
areDevicesVerified (bool):
|
|
45
|
-
True to create a `SpotifyClient` instance for discovered devices, which
|
|
46
|
-
verifies that the device can be accessed and basic information obtained
|
|
47
|
-
about its capabilities; otherwise, False to just identify the IPV4 Address,
|
|
48
|
-
Port, and Device Name.
|
|
49
|
-
Default is False.
|
|
44
|
+
zeroconfClient (Zeroconf)
|
|
45
|
+
A Zeroconf client instance that will be used to discover Spotify Connect devices,
|
|
46
|
+
or null to create a new instance of Zeroconf.
|
|
47
|
+
Default is null.
|
|
50
48
|
printToConsole (bool):
|
|
51
49
|
True to print discovered device information to the console as the devices
|
|
52
50
|
are discovered; otherwise, False to not print anything to the console.
|
|
53
51
|
Default is False.
|
|
54
|
-
|
|
55
|
-
Specify False for the `areDevicesVerified` argument if you want to speed up
|
|
56
|
-
device discovery, as it takes extra time to verify device connections as they
|
|
57
|
-
are discovered.
|
|
58
52
|
"""
|
|
59
53
|
# validations.
|
|
60
|
-
if
|
|
61
|
-
raise SpotifyApiError(SAAppMessages.ARGUMENT_TYPE_ERROR % ("__init__", '
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
if zeroconfClient is not None and (not isinstance(zeroconfClient, Zeroconf)):
|
|
55
|
+
raise SpotifyApiError(SAAppMessages.ARGUMENT_TYPE_ERROR % ("__init__", 'zeroconfClient', 'Zeroconf', type(zeroconfClient).__name__), logsi=_logsi)
|
|
56
|
+
|
|
57
|
+
# create the zeroconf client if one was not specified.
|
|
58
|
+
if zeroconfClient is None:
|
|
59
|
+
_logsi.LogVerbose("Creating new Zeroconf instance for discovery")
|
|
60
|
+
zeroconfClient = Zeroconf()
|
|
61
|
+
else:
|
|
62
|
+
_logsi.LogObject(SILevel.Verbose, "Using existing Zeroconf instance for discovery", zeroconfClient)
|
|
64
63
|
|
|
65
64
|
# initialize instance properties.
|
|
66
|
-
self._AreDevicesVerified:bool = areDevicesVerified
|
|
67
65
|
self._DiscoveredDeviceNames:dict = {}
|
|
68
66
|
self._DiscoveryResults:list[ZeroconfDiscoveryResult] = []
|
|
69
67
|
self._PrintToConsole:bool = printToConsole
|
|
70
|
-
self.
|
|
71
|
-
self._VerifiedDevices:dict = {}
|
|
68
|
+
self._ZeroconfClient = zeroconfClient
|
|
72
69
|
|
|
73
70
|
|
|
74
71
|
def __getitem__(self, key):
|
|
@@ -92,21 +89,6 @@ class SpotifyDiscovery:
|
|
|
92
89
|
return self.ToString()
|
|
93
90
|
|
|
94
91
|
|
|
95
|
-
@property
|
|
96
|
-
def AreDevicesVerified(self) -> bool:
|
|
97
|
-
"""
|
|
98
|
-
Determines if a `Device` object is created for devices that are
|
|
99
|
-
discovered. This property is set by what is passed to the class constructor.
|
|
100
|
-
|
|
101
|
-
If False, then the `VerifiedDevices` property will be empty;
|
|
102
|
-
|
|
103
|
-
If True, then the `VerifiedDevices` property will contain a `Device`
|
|
104
|
-
instance for each device that was detected as part of the discovery process
|
|
105
|
-
and was found in the Spotify User's player devices list.
|
|
106
|
-
"""
|
|
107
|
-
return self._AreDevicesVerified
|
|
108
|
-
|
|
109
|
-
|
|
110
92
|
@property
|
|
111
93
|
def DiscoveredDeviceNames(self) -> dict:
|
|
112
94
|
"""
|
|
@@ -132,21 +114,12 @@ class SpotifyDiscovery:
|
|
|
132
114
|
|
|
133
115
|
|
|
134
116
|
@property
|
|
135
|
-
def
|
|
136
|
-
"""
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
This property is only populated if the `AreDevicesVerified` property is True.
|
|
140
|
-
|
|
141
|
-
Dictionary keys will be in the form of "address:port", where "address" is the device
|
|
142
|
-
ipv4 address and the "port" is the ipv4 port number the Spotify Connect device
|
|
143
|
-
is listening on.
|
|
144
|
-
|
|
145
|
-
Dictionary values will be `Device` instances that represent the discovered
|
|
146
|
-
device.
|
|
117
|
+
def ZeroconfClient(self) -> Zeroconf:
|
|
118
|
+
"""
|
|
119
|
+
Zeroconf client instance that will be used to discover Spotify Connect devices.
|
|
147
120
|
"""
|
|
148
|
-
return self.
|
|
149
|
-
|
|
121
|
+
return self._ZeroconfClient
|
|
122
|
+
|
|
150
123
|
|
|
151
124
|
def _OnServiceStateChange(self,
|
|
152
125
|
zeroconf:Zeroconf,
|
|
@@ -230,19 +203,6 @@ class SpotifyDiscovery:
|
|
|
230
203
|
# add the device result to the list.
|
|
231
204
|
self._DiscoveryResults.append(result)
|
|
232
205
|
|
|
233
|
-
# are we verifying devices connections? if so, then create a SpotifyClient
|
|
234
|
-
# object, which will verify the connection and gather basic capabilities of the device.
|
|
235
|
-
# we will also add the SpotifyClient instance using the same key as the device name list.
|
|
236
|
-
if self._AreDevicesVerified == True:
|
|
237
|
-
|
|
238
|
-
if deviceKey not in self._VerifiedDevices.keys():
|
|
239
|
-
|
|
240
|
-
# get Spotify Connect player device by it's Name value.
|
|
241
|
-
device:Device = self._SpotifyClient.GetPlayerDevice(deviceName)
|
|
242
|
-
if device is not None:
|
|
243
|
-
self._VerifiedDevices[deviceKey] = device
|
|
244
|
-
result.SpotifyConnectIsInDeviceList = True
|
|
245
|
-
|
|
246
206
|
# trace.
|
|
247
207
|
_logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device result: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result, excludeNonPublic=True)
|
|
248
208
|
#_logsi.LogArray(SILevel.Verbose, "Discovered Spotify Connect device result.Properties: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result.Properties)
|
|
@@ -256,19 +216,19 @@ class SpotifyDiscovery:
|
|
|
256
216
|
pass
|
|
257
217
|
|
|
258
218
|
|
|
259
|
-
def DiscoverDevices(self, timeout:
|
|
219
|
+
def DiscoverDevices(self, timeout:float=2) -> dict:
|
|
260
220
|
"""
|
|
261
221
|
Discover Spotify Connect devices on the local network via the
|
|
262
222
|
ZeroConf (aka MDNS) service.
|
|
263
223
|
|
|
264
224
|
Args:
|
|
265
|
-
timeout (
|
|
225
|
+
timeout (float):
|
|
266
226
|
Maximum amount of time to wait (in seconds) for the
|
|
267
227
|
discovery to complete.
|
|
268
|
-
Default is
|
|
228
|
+
Default is 2 seconds.
|
|
269
229
|
|
|
270
230
|
Returns:
|
|
271
|
-
A dictionary of
|
|
231
|
+
A dictionary of `ZeroconfDiscoveryResult` objects.
|
|
272
232
|
|
|
273
233
|
<details>
|
|
274
234
|
<summary>Sample Code</summary>
|
|
@@ -280,12 +240,9 @@ class SpotifyDiscovery:
|
|
|
280
240
|
# using Queue as a timer (timeout functionality).
|
|
281
241
|
discoveryQueueTimer = Queue()
|
|
282
242
|
|
|
283
|
-
# create the zeroconf service and our listener callback.
|
|
284
|
-
zeroconf:Zeroconf = Zeroconf()
|
|
285
|
-
|
|
286
243
|
# create the zeroconf service browser that will start device discovery.
|
|
287
244
|
_logsi.LogVerbose("Discovery of Spotify Connect devices via Zeroconf is starting ...")
|
|
288
|
-
ServiceBrowser(
|
|
245
|
+
ServiceBrowser(self._ZeroconfClient, "_spotify-connect._tcp.local.", handlers=[self._OnServiceStateChange])
|
|
289
246
|
|
|
290
247
|
try:
|
|
291
248
|
# give the ServiceBrowser time to discover
|
|
@@ -296,7 +253,7 @@ class SpotifyDiscovery:
|
|
|
296
253
|
# this is not really an exception, but more of an indicator that
|
|
297
254
|
# the timeout has been reached.
|
|
298
255
|
_logsi.LogVerbose("Discovery of Spotify Connect devices via Zeroconf has ended.")
|
|
299
|
-
|
|
256
|
+
|
|
300
257
|
return self._DiscoveredDeviceNames
|
|
301
258
|
|
|
302
259
|
|
|
@@ -315,11 +272,6 @@ class SpotifyDiscovery:
|
|
|
315
272
|
if includeItems == True:
|
|
316
273
|
|
|
317
274
|
for key, deviceName in self._DiscoveredDeviceNames.items():
|
|
318
|
-
|
|
319
|
-
verifiedStatus:str = ''
|
|
320
|
-
device = self._VerifiedDevices.get(key, None)
|
|
321
|
-
if device is not None:
|
|
322
|
-
verifiedStatus = ' (verified - ID=%s)' % device.Id
|
|
323
|
-
msg = "%s\n- %s - %s %s" % (msg, key, deviceName, verifiedStatus)
|
|
275
|
+
msg = "%s\n- %s - %s" % (msg, key, deviceName)
|
|
324
276
|
|
|
325
277
|
return msg
|
|
@@ -71,6 +71,8 @@ class ZeroconfConnect:
|
|
|
71
71
|
# validations.
|
|
72
72
|
if (version is None):
|
|
73
73
|
version = '1.0'
|
|
74
|
+
if (useSSL is None) or (not isinstance(useSSL,bool)):
|
|
75
|
+
useSSL = False
|
|
74
76
|
|
|
75
77
|
# initialize storage.
|
|
76
78
|
self._CPath:str = cpath
|
|
@@ -271,6 +273,8 @@ class ZeroconfConnect:
|
|
|
271
273
|
Args:
|
|
272
274
|
username (str):
|
|
273
275
|
Spotify Connect user name to login with.
|
|
276
|
+
This MUST match the account name (or one of them) that was used to configure Spotify Connect
|
|
277
|
+
on the manufacturer device.
|
|
274
278
|
password (str):
|
|
275
279
|
Spotify Connect user password to login with.
|
|
276
280
|
|
|
@@ -289,6 +293,12 @@ class ZeroconfConnect:
|
|
|
289
293
|
is able to perform the login attempt. You should issue a call to the Spotify Web API `Get Available Devices`
|
|
290
294
|
endpoint to check the current device list to ensure that the device id was successfully added or not.
|
|
291
295
|
|
|
296
|
+
Note that if you don't have a password setup for your Spotify account (e.g. you utilize the "Continue with Google"
|
|
297
|
+
or other non-password methods for login), then you will need to define a "device password" in order to use the
|
|
298
|
+
ZeroConf Connect service; use the [Spotify Set Device Password](https://www.spotify.com/uk/account/set-device-password/)
|
|
299
|
+
page to define a device password. You will then use your Spotify username and the device password to Connect
|
|
300
|
+
to the device.
|
|
301
|
+
|
|
292
302
|
<details>
|
|
293
303
|
<summary>Sample Code</summary>
|
|
294
304
|
```python
|
|
@@ -26,7 +26,7 @@ class ZeroconfGetInfo(ZeroconfResponse):
|
|
|
26
26
|
|
|
27
27
|
# initialize our attributes.
|
|
28
28
|
self._AccountReq:str = None
|
|
29
|
-
self._ActiveUser:str =
|
|
29
|
+
self._ActiveUser:str = ""
|
|
30
30
|
self._Aliases:list[ZeroconfGetInfoAlias] = []
|
|
31
31
|
self._Availability:str = None
|
|
32
32
|
self._BrandDisplayName:str = None
|
|
@@ -54,7 +54,7 @@ class ZeroconfGetInfo(ZeroconfResponse):
|
|
|
54
54
|
else:
|
|
55
55
|
|
|
56
56
|
self._AccountReq = root.get('accountReq', None)
|
|
57
|
-
self._ActiveUser = root.get('activeUser',
|
|
57
|
+
self._ActiveUser = root.get('activeUser', "")
|
|
58
58
|
self._Availability = root.get('availability', None)
|
|
59
59
|
self._BrandDisplayName = root.get('brandDisplayName', None)
|
|
60
60
|
self._ClientId = root.get('clientID', None)
|
|
@@ -106,7 +106,7 @@ class ZeroconfGetInfo(ZeroconfResponse):
|
|
|
106
106
|
"""
|
|
107
107
|
Canonical username of the logged in user (e.g. "31l77y2123456789012345678901").
|
|
108
108
|
|
|
109
|
-
This value will be
|
|
109
|
+
This value will be an empty string if there is no user logged into the device.
|
|
110
110
|
"""
|
|
111
111
|
return self._ActiveUser
|
|
112
112
|
|
|
@@ -192,6 +192,24 @@ class ZeroconfGetInfo(ZeroconfResponse):
|
|
|
192
192
|
return self._GroupStatus
|
|
193
193
|
|
|
194
194
|
|
|
195
|
+
@property
|
|
196
|
+
def HasActiveUser(self) -> bool:
|
|
197
|
+
"""
|
|
198
|
+
Returns True if the device has an active user account specified;
|
|
199
|
+
otherwise, False.
|
|
200
|
+
"""
|
|
201
|
+
return (self._ActiveUser != "")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def HasAliases(self) -> bool:
|
|
206
|
+
"""
|
|
207
|
+
Returns True if the device has alias entries defined;
|
|
208
|
+
otherwise, False.
|
|
209
|
+
"""
|
|
210
|
+
return (len(self._Aliases) > 0)
|
|
211
|
+
|
|
212
|
+
|
|
195
213
|
@property
|
|
196
214
|
def LibraryVersion(self) -> str:
|
|
197
215
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|