spotifywebapiPython 1.0.59__py3-none-any.whl → 1.0.61__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 (25) hide show
  1. {spotifywebapiPython-1.0.59.dist-info → spotifywebapiPython-1.0.61.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.59.dist-info → spotifywebapiPython-1.0.61.dist-info}/RECORD +25 -25
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +18 -2
  5. spotifywebapipython/docs/index.html +2 -2
  6. spotifywebapipython/docs/search.js +1 -1
  7. spotifywebapipython/docs/spotifywebapiPython/const.html +3 -3
  8. spotifywebapipython/docs/spotifywebapiPython/saappmessages.html +2 -2
  9. spotifywebapipython/docs/spotifywebapiPython/sautils.html +2 -2
  10. spotifywebapipython/docs/spotifywebapiPython/spotifyapierror.html +2 -2
  11. spotifywebapipython/docs/spotifywebapiPython/spotifyapimessage.html +2 -2
  12. spotifywebapipython/docs/spotifywebapiPython/spotifyauthtoken.html +2 -2
  13. spotifywebapipython/docs/spotifywebapiPython/spotifyclient.html +4 -3
  14. spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +5 -5
  15. spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/models/spotifyconnectdevices.py +2 -1
  18. spotifywebapipython/models/zeroconfdiscoveryresult.py +36 -12
  19. spotifywebapipython/spotifyclient.py +39 -9
  20. spotifywebapipython/spotifydiscovery.py +52 -56
  21. spotifywebapipython/zeroconfapi/zeroconfconnect.py +38 -50
  22. spotifywebapipython/zeroconfapi/zeroconfgetinfo.py +26 -0
  23. {spotifywebapiPython-1.0.59.dist-info → spotifywebapiPython-1.0.61.dist-info}/LICENSE +0 -0
  24. {spotifywebapiPython-1.0.59.dist-info → spotifywebapiPython-1.0.61.dist-info}/WHEEL +0 -0
  25. {spotifywebapiPython-1.0.59.dist-info → spotifywebapiPython-1.0.61.dist-info}/top_level.txt +0 -0
@@ -94,9 +94,9 @@ class SpotifyDiscovery:
94
94
  """
95
95
  A dictionary of discovered device names that were detected by the discovery process.
96
96
 
97
- Dictionary keys will be in the form of "address:port", where "address" is the device
98
- ipv4 address and the "port" is the ipv4 port number the Spotify Connect device
99
- is listening on.
97
+ Dictionary keys will be in the form of "'name' (address:port)", where "name" is the
98
+ device name, "address" is the device server address and the "port" is the ipv4 port
99
+ number the Spotify Connect device is listening on.
100
100
 
101
101
  Dictionary values will be the device names (e.g. "Web Player (Chrome)", etc.). This SHOULD
102
102
  match the name of the device as displayed in the Spotify App, but is not guaranteed.
@@ -152,60 +152,56 @@ class SpotifyDiscovery:
152
152
  if (ipAddressList is None):
153
153
  return
154
154
 
155
- # create device instances for each ipv4 address found.
156
- for deviceIpAddress in ipAddressList:
155
+ # create new result instance.
156
+ result:ZeroconfDiscoveryResult = ZeroconfDiscoveryResult()
157
+ result.DeviceName = deviceName
158
+ result.Domain = '.local'
159
+ for item in ipAddressList:
160
+ result.HostIpAddresses.append(item)
161
+ result.HostIpPort = serviceInfo.port
162
+ result.HostTTL = serviceInfo.host_ttl
163
+ result.Key = serviceInfo.key
164
+ result.Name = serviceInfo.name
165
+ result.OtherTTL = serviceInfo.other_ttl
166
+ result.Priority = serviceInfo.priority
167
+ result.Server = serviceInfo.server
168
+ result.ServerKey = serviceInfo.server_key
169
+ result.ServiceInfo = serviceInfo
170
+ result.ServiceType = serviceType
171
+ result.Weight = serviceInfo.weight
157
172
 
158
- # create new result instance.
159
- result:ZeroconfDiscoveryResult = ZeroconfDiscoveryResult()
160
- result.DeviceName = deviceName
161
- result.Domain = '.local'
162
- result.HostIpv4Address = deviceIpAddress
163
- result.HostIpPort = serviceInfo.port
164
- result.HostTTL = serviceInfo.host_ttl
165
- result.Key = serviceInfo.key
166
- result.Name = serviceInfo.name
167
- result.OtherTTL = serviceInfo.other_ttl
168
- result.Priority = serviceInfo.priority
169
- result.Server = serviceInfo.server
170
- result.ServerKey = serviceInfo.server_key
171
- result.ServiceInfo = serviceInfo
172
- result.ServiceType = serviceType
173
- result.Weight = serviceInfo.weight
173
+ # formulate a unique key for this device.
174
+ deviceKey:str = "'%s' (%s:%i)" % (deviceName, result.HostIpAddress, result.HostIpPort)
175
+ _logsi.LogVerbose("Discovered Spotify Connect device: %s %s" % (deviceKey, result.HostIpAddresses))
176
+ if (self._PrintToConsole == True):
177
+ print("Discovered Spotify Connect device: %s [%s]" % (deviceKey, result.HostIpAddresses))
178
+ _logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo: %s" % (deviceKey), serviceInfo)
174
179
 
175
- devicePort:int = serviceInfo.port
176
- deviceKey:str = '%s:%i' % (deviceIpAddress, devicePort)
177
- _logsi.LogVerbose("Discovered Spotify Connect device: %s - %s" % (deviceKey, deviceName))
178
- if (self._PrintToConsole == True):
179
- print("Discovered Spotify Connect device: %s - %s" % (deviceKey, deviceName))
180
- _logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo: %s - %s" % (deviceKey, deviceName), serviceInfo)
181
-
182
- # process service info propertys.
183
- # note that the property keys and values must first be decoded to utf-8 encoding.
184
- if serviceInfo.properties is not None:
185
- dspProperties:dict = {}
186
- for key, value in serviceInfo.properties.items():
187
- keyStr = key.decode('utf-8')
188
- valueStr = value.decode('utf-8')
189
- result.Properties.append(ZeroconfProperty(keyStr, valueStr))
190
- dspProperties[keyStr] = valueStr
191
- # check for Spotify Connect common properties.
192
- if keyStr.lower() == 'cpath':
193
- result.SpotifyConnectCPath = valueStr
194
- elif keyStr.lower() == 'version':
195
- result.SpotifyConnectVersion = valueStr
196
- _logsi.LogDictionary(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo.Properties: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), dspProperties)
197
-
198
- # add the device name to the list (if not already added) using its
199
- # ipv4 address and port number as the key.
200
- if deviceKey not in self._DiscoveredDeviceNames.keys():
201
- self._DiscoveredDeviceNames[deviceKey] = deviceName
202
-
203
- # add the device result to the list.
204
- self._DiscoveryResults.append(result)
205
-
206
- # trace.
207
- _logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device result: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result, excludeNonPublic=True)
208
- #_logsi.LogArray(SILevel.Verbose, "Discovered Spotify Connect device result.Properties: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result.Properties)
180
+ # process service info propertys.
181
+ # note that the property keys and values must first be decoded to utf-8 encoding.
182
+ if serviceInfo.properties is not None:
183
+ dspProperties:dict = {}
184
+ for key, value in serviceInfo.properties.items():
185
+ keyStr = key.decode('utf-8')
186
+ valueStr = value.decode('utf-8')
187
+ result.Properties.append(ZeroconfProperty(keyStr, valueStr))
188
+ dspProperties[keyStr] = valueStr
189
+ # check for Spotify Connect common properties.
190
+ if keyStr.lower() == 'cpath':
191
+ result.SpotifyConnectCPath = valueStr
192
+ elif keyStr.lower() == 'version':
193
+ result.SpotifyConnectVersion = valueStr
194
+ _logsi.LogDictionary(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo.Properties: %s" % (deviceKey), dspProperties)
195
+
196
+ # add the device name to the list (if not already added).
197
+ if deviceKey not in self._DiscoveredDeviceNames.keys():
198
+ self._DiscoveredDeviceNames[deviceKey] = deviceName
199
+
200
+ # add the device result to the list.
201
+ self._DiscoveryResults.append(result)
202
+
203
+ # trace.
204
+ _logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device result: %s" % (deviceKey), result, excludeNonPublic=True)
209
205
 
210
206
  elif (serviceStateChange is ServiceStateChange.Removed):
211
207
  _logsi.LogVerbose("Discovered Spotify Connect device removal (ignored): '%s' (%s:%s)", serviceName, serviceType, serviceStateChange)
@@ -272,6 +268,6 @@ class SpotifyDiscovery:
272
268
  if includeItems == True:
273
269
 
274
270
  for key, deviceName in self._DiscoveredDeviceNames.items():
275
- msg = "%s\n- %s - %s" % (msg, key, deviceName)
271
+ msg = "%s\n- %s" % (msg, key)
276
272
 
277
273
  return msg
@@ -14,6 +14,7 @@ from .zeroconfgetinfo import ZeroconfGetInfo
14
14
  from ..saappmessages import SAAppMessages
15
15
  from ..sautils import export, validateDelay
16
16
  from ..spotifyapierror import SpotifyApiError
17
+ from ..spotifywebapierror import SpotifyWebApiError
17
18
  from ..const import (
18
19
  TRACE_MSG_DELAY_DEVICE,
19
20
  )
@@ -41,8 +42,8 @@ class ZeroconfConnect:
41
42
  """
42
43
 
43
44
  def __init__(self,
44
- hostIpv4Address:str,
45
- hostIpPort:str,
45
+ hostIpAddress:str,
46
+ hostIpPort:int,
46
47
  cpath:str,
47
48
  version:str='1.0',
48
49
  useSSL:bool=False
@@ -51,9 +52,9 @@ class ZeroconfConnect:
51
52
  Initializes a new instance of the class.
52
53
 
53
54
  Args:
54
- hostIpv4Address (str):
55
- IPV4 address (as a string) at which the Spotify Connect Zeroconf API can be reached
56
- on the Spotify Connect device (e.g. "192.168.1.81").
55
+ hostIpAddress (str):
56
+ IP address or alias at which the Spotify Connect Zeroconf API can be reached
57
+ on the Spotify Connect device (e.g. "192.168.1.81", "Bose-SM2-341513fbeeae.local.", etc).
57
58
  hostIpPort (int):
58
59
  Port number (as an integer) at which the Spotify Connect Zeroconf API can be reached
59
60
  on the Spotify Connect device (e.g. "8200").
@@ -80,7 +81,7 @@ class ZeroconfConnect:
80
81
  # initialize storage.
81
82
  self._CPath:str = cpath
82
83
  self._HostIpPort:int = hostIpPort
83
- self._HostIpv4Address:str = hostIpv4Address
84
+ self._HostIpAddress:str = hostIpAddress
84
85
  self._UseSSL:bool = useSSL
85
86
  self._Version:str = version
86
87
 
@@ -97,12 +98,12 @@ class ZeroconfConnect:
97
98
 
98
99
 
99
100
  @property
100
- def HostIpv4Address(self) -> str:
101
+ def HostIpAddress(self) -> str:
101
102
  """
102
- IPV4 address (as a string) at which the Spotify Connect Zeroconf API can be reached
103
- on the Spotify Connect device (e.g. "192.168.1.81").
103
+ IP address or alias at which the Spotify Connect Zeroconf API can be reached
104
+ on the Spotify Connect device (e.g. "192.168.1.81", "Bose-SM2-341513fbeeae.local.", etc).
104
105
  """
105
- return self._HostIpv4Address
106
+ return self._HostIpAddress
106
107
 
107
108
 
108
109
  @property
@@ -191,43 +192,30 @@ class ZeroconfConnect:
191
192
  if (response.headers):
192
193
  _logsi.LogCollection(SILevel.Debug, "ZeroconfConnect http response [%s-%s]: '%s' (headers)" % (response.status_code, response.reason, responseUrl), response.headers.items())
193
194
 
194
- if response.content is not None:
195
+ # do we have response data?
196
+ if (response.content is not None) and (len(response.content) > 0):
195
197
 
196
- # do response headers contain a content-type value?
197
- # if so, we will use it to determine how to convert the response data.
198
- if response.headers:
199
- if 'content-type' in response.headers:
200
- contentType = response.headers['content-type']
201
-
202
- # do we have response data?
203
- if len(response.content) == 0:
204
-
205
- # some requests will not return a response, which is ok.
206
- responseData = None
207
- _logsi.LogVerbose("ZeroconfConnect http response [%s-%s]: '%s' (no data)" % (response.status_code, response.reason, responseUrl))
208
-
209
- elif (contentType is not None) and (contentType.find('json') > -1):
210
-
211
- # response is json.
212
- # do not use the "response.json()" method to parse JSON responses, as it is unreliable!
213
- data = response.content.decode('utf-8')
214
- responseData = json.loads(data)
215
-
216
- if _logsi.IsOn(SILevel.Verbose):
217
- if isinstance(responseData, dict):
218
- _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json dict)" % (response.status_code, response.reason, responseUrl), responseData)
219
- elif isinstance(responseData, list):
220
- _logsi.LogArray(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json array)" % (response.status_code, response.reason, responseUrl), responseData)
221
- else:
222
- _logsi.LogObject(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json object)" % (response.status_code, response.reason, responseUrl), responseData)
223
-
224
- else:
198
+ # convert response to JSON object.
199
+ # ALL Spotify Connect Zeroconf API responses SHOULD be JSON format!
200
+ # do not use the "response.json()" method to parse JSON responses, as it is unreliable!
201
+ data = response.content.decode('utf-8')
202
+ responseData = json.loads(data)
225
203
 
226
- # no - treat it as utf-8 encoded data.
227
- responseUTF8 = response.content.decode('utf-8')
228
- _logsi.LogText(SILevel.Error, "ZeroconfConnect http response [%s-%s]: '%s' (utf-8)" % (response.status_code, response.reason, responseUrl), responseUTF8)
229
- responseData = responseUTF8
230
-
204
+ if _logsi.IsOn(SILevel.Verbose):
205
+ if isinstance(responseData, dict):
206
+ _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json dict)" % (response.status_code, response.reason, responseUrl), responseData)
207
+ elif isinstance(responseData, list):
208
+ _logsi.LogArray(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json array)" % (response.status_code, response.reason, responseUrl), responseData)
209
+ else:
210
+ _logsi.LogObject(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json object)" % (response.status_code, response.reason, responseUrl), responseData)
211
+ pass
212
+
213
+ else:
214
+
215
+ # raise an exception, as we are expecting a JSON-formatted response!
216
+ errMessage = response.content.decode('utf-8')
217
+ raise SpotifyWebApiError(response.status_code, errMessage, methodName, response.reason, _logsi)
218
+
231
219
  except SpotifyZeroconfApiError: raise # pass handled exceptions on thru
232
220
  except SpotifyApiError: raise # pass handled exceptions on thru
233
221
  except Exception as ex:
@@ -325,7 +313,7 @@ class ZeroconfConnect:
325
313
 
326
314
  # trace.
327
315
  apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
328
- apiMethodParms.AppendKeyValue("HostIpv4Address", self._HostIpv4Address)
316
+ apiMethodParms.AppendKeyValue("HostIpAddress", self._HostIpAddress)
329
317
  apiMethodParms.AppendKeyValue("HostIpPort", self._HostIpPort)
330
318
  apiMethodParms.AppendKeyValue("CPath", self._CPath)
331
319
  apiMethodParms.AppendKeyValue("Version", self._Version)
@@ -440,7 +428,7 @@ class ZeroconfConnect:
440
428
 
441
429
  # trace.
442
430
  apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
443
- apiMethodParms.AppendKeyValue("HostIpv4Address", self._HostIpv4Address)
431
+ apiMethodParms.AppendKeyValue("HostIpAddress", self._HostIpAddress)
444
432
  apiMethodParms.AppendKeyValue("HostIpPort", self._HostIpPort)
445
433
  apiMethodParms.AppendKeyValue("CPath", self._CPath)
446
434
  apiMethodParms.AppendKeyValue("Version", self._Version)
@@ -512,7 +500,7 @@ class ZeroconfConnect:
512
500
 
513
501
  return "{protocol}://{ip}:{port}{cpath}".format(
514
502
  protocol=protocol,
515
- ip=self._HostIpv4Address,
503
+ ip=self._HostIpAddress,
516
504
  port=self._HostIpPort,
517
505
  cpath=self._CPath,
518
506
  )
@@ -546,7 +534,7 @@ class ZeroconfConnect:
546
534
 
547
535
  # trace.
548
536
  apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
549
- apiMethodParms.AppendKeyValue("HostIpv4Address", self._HostIpv4Address)
537
+ apiMethodParms.AppendKeyValue("HostIpAddress", self._HostIpAddress)
550
538
  apiMethodParms.AppendKeyValue("HostIpPort", self._HostIpPort)
551
539
  apiMethodParms.AppendKeyValue("CPath", self._CPath)
552
540
  apiMethodParms.AppendKeyValue("Version", self._Version)
@@ -573,7 +561,7 @@ class ZeroconfConnect:
573
561
  result = ZeroconfGetInfo(root=responseData)
574
562
 
575
563
  # trace.
576
- _logsi.LogObject(SILevel.Verbose, '%s result - type="%s"' % (apiMethodName, type(result).__name__), result, excludeNonPublic=True)
564
+ _logsi.LogObject(SILevel.Verbose, '%s result (%s) - "%s" (%s)' % (apiMethodName, type(result).__name__, result.RemoteName, result.DeviceId), result, excludeNonPublic=True)
577
565
  return result
578
566
 
579
567
  except SpotifyZeroconfApiError: raise # pass handled exceptions on thru
@@ -47,6 +47,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
47
47
  self._Version:str = None
48
48
  self._VoiceSupport:str = None
49
49
 
50
+ # non-Spotify properties.
51
+ self._IsInDeviceList:bool = None
52
+
50
53
  if (root is None):
51
54
 
52
55
  pass
@@ -343,6 +346,27 @@ class ZeroconfGetInfo(ZeroconfResponse):
343
346
  return self._VoiceSupport
344
347
 
345
348
 
349
+ # non-Spotify properties.
350
+
351
+ @property
352
+ def IsInDeviceList(self) -> bool:
353
+ """
354
+ Returns the status of active device list verification:
355
+ * None - device list verification has not been performed for this device.
356
+ * True - device is a member of the active device list.
357
+ * False - device is NOT a member of the active device list.
358
+ """
359
+ return self._IsInDeviceList
360
+
361
+ @IsInDeviceList.setter
362
+ def IsInDeviceList(self, value:bool):
363
+ """
364
+ Sets the IsInDeviceList property value.
365
+ """
366
+ if isinstance(value, bool):
367
+ self._IsInDeviceList = value
368
+
369
+
346
370
  def ToDictionary(self) -> dict:
347
371
  """
348
372
  Returns a dictionary representation of the class.
@@ -374,6 +398,7 @@ class ZeroconfGetInfo(ZeroconfResponse):
374
398
  'TokenType': self._TokenType,
375
399
  'Version': self._Version,
376
400
  'VoiceSupport': self._VoiceSupport,
401
+ 'IsInDeviceList': self._IsInDeviceList,
377
402
  }
378
403
 
379
404
  # combine base class results with these results.
@@ -420,4 +445,5 @@ class ZeroconfGetInfo(ZeroconfResponse):
420
445
  if self._TokenType is not None: msg = '%s\n TokenType="%s"' % (msg, str(self._TokenType))
421
446
  if self._Version is not None: msg = '%s\n Version="%s"' % (msg, str(self._Version))
422
447
  if self._VoiceSupport is not None: msg = '%s\n VoiceSupport="%s"' % (msg, str(self._VoiceSupport))
448
+ if self._IsInDeviceList is not None: msg = '%s\n IsInDeviceList="%s"' % (msg, str(self._IsInDeviceList))
423
449
  return msg