spotifywebapiPython 1.0.73__py3-none-any.whl → 1.0.74__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 (27) hide show
  1. {spotifywebapiPython-1.0.73.dist-info → spotifywebapiPython-1.0.74.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.73.dist-info → spotifywebapiPython-1.0.74.dist-info}/RECORD +27 -26
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +12 -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 +2 -2
  14. spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +2 -2
  15. spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/models/spotifyconnectdevices.py +53 -27
  18. spotifywebapipython/spotifyclient.py +7 -4
  19. spotifywebapipython/zeroconfapi/__init__.py +2 -0
  20. spotifywebapipython/zeroconfapi/authenticationtypes.py +38 -0
  21. spotifywebapipython/zeroconfapi/blobbuilder.py +6 -0
  22. spotifywebapipython/zeroconfapi/credentials.py +18 -2
  23. spotifywebapipython/zeroconfapi/zeroconfconnect.py +47 -35
  24. spotifywebapipython/zeroconfapi/zeroconfgetinfo.py +69 -18
  25. {spotifywebapiPython-1.0.73.dist-info → spotifywebapiPython-1.0.74.dist-info}/LICENSE +0 -0
  26. {spotifywebapiPython-1.0.73.dist-info → spotifywebapiPython-1.0.74.dist-info}/WHEEL +0 -0
  27. {spotifywebapiPython-1.0.73.dist-info → spotifywebapiPython-1.0.74.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,17 @@
1
+ # our package imports.
2
+ from .authenticationtypes import AuthenticationTypes
3
+
1
4
  class Credentials:
2
5
  """
3
6
  Credentials used by the Spotify SpConnectionLoginBlob method.
4
7
  """
5
8
 
6
- def __init__(self, username: str, password: str):
9
+ def __init__(
10
+ self,
11
+ username:str,
12
+ password:str,
13
+ authenticationType:AuthenticationTypes=AuthenticationTypes.USER_PASS,
14
+ ):
7
15
  """
8
16
  Initializes a new instance of the class.
9
17
 
@@ -12,7 +20,15 @@ class Credentials:
12
20
  Spotify Connect user name to login with.
13
21
  password (str):
14
22
  Spotify Connect user password to login with.
23
+ authenticationType (AuthenticationTypes):
24
+ Authentication type.
15
25
  """
26
+ # validations.
27
+ if (authenticationType is None) or (not isinstance(authenticationType, AuthenticationTypes)):
28
+ authenticationType = AuthenticationTypes.USER_PASS
29
+
30
+ # initialize storage.
16
31
  self.username: bytes = bytes(username, 'ascii')
17
32
  self.password: bytes = bytes(password, 'ascii')
18
- self.auth_type: int = 0x00
33
+ self.auth_type: AuthenticationTypes = authenticationType
34
+
@@ -6,7 +6,7 @@ import time
6
6
 
7
7
  # our package imports.
8
8
  from .blobbuilder import BlobBuilder
9
- from .credentials import Credentials
9
+ from .credentials import Credentials, AuthenticationTypes
10
10
  from .helpers import int_to_b64str
11
11
  from .spotifyzeroconfapierror import SpotifyZeroconfApiError
12
12
  from .zeroconfresponse import ZeroconfResponse
@@ -42,6 +42,12 @@ class ZeroconfConnect:
42
42
  GitHub repository.
43
43
  """
44
44
 
45
+ SPOTIFY_CONNECT_CLIENT_VERSION_DEFAULT:str = '2.7.1'
46
+ """
47
+ Spotify Connect default version string ('2.7.1').
48
+ """
49
+
50
+
45
51
  def __init__(self,
46
52
  hostIpAddress:str,
47
53
  hostIpPort:int,
@@ -72,6 +78,8 @@ class ZeroconfConnect:
72
78
  # validations.
73
79
  if (useSSL is None) or (not isinstance(useSSL,bool)):
74
80
  useSSL = False
81
+ if (version is None) or (not isinstance(version,str)):
82
+ version = ZeroconfConnect.SPOTIFY_CONNECT_CLIENT_VERSION_DEFAULT
75
83
 
76
84
  # initialize storage.
77
85
  self._CPath:str = cpath
@@ -208,10 +216,15 @@ class ZeroconfConnect:
208
216
  responseUTF8 = response.content.decode('utf-8')
209
217
  _logsi.LogText(SILevel.Error, "ZeroconfConnect http response [%s-%s]: '%s' (utf-8)" % (response.status_code, response.reason, endpoint), responseUTF8)
210
218
 
211
- # at this point we don't know what Spotify Web Api returned, so let's
212
- # just raise a new exception with the non-JSON response data.
213
- raise SpotifyZeroconfApiError(response.status_code, responseUTF8, methodName, response.reason, _logsi)
214
-
219
+ # at this point we don't know what Spotify Web Api returned!
220
+ # it's been found that some devices (Sonos, etc) do not return a proper JSON response.
221
+ # raise a new exception with the non-JSON response data if the http status is not 200 (OK).
222
+ if (response.status_code != 200):
223
+ raise SpotifyZeroconfApiError(response.status_code, responseUTF8, methodName, response.reason, _logsi)
224
+
225
+ # trace.
226
+ _logsi.LogVerbose("Spotify Connect Device (ip=%s) returned an invalid JSON response, but the http status code was 200 so we will ignore it" % (self._HostIpAddress))
227
+
215
228
  # at this point we know that the response contains JSON data, as
216
229
  # we would have raised an exception before this if we could not
217
230
  # convert the response to JSON!
@@ -335,14 +348,6 @@ class ZeroconfConnect:
335
348
  # get the current device id from the device via Spotify ZeroConf API `getInfo` endpoint.
336
349
  info:ZeroconfGetInfo = self.GetInformation()
337
350
 
338
- # are we including origin device information (e.g. deviceName, deviceId)?
339
- # TODO - maybe tailor this to info.BrandDisplayName and info.ModelDisplayName instead?
340
- includeOriginDeviceInfo:bool = False
341
- if (info.PublicKey == 'SU5WQUxJRA==') and (info.Availability == 'NOT-LOADED'):
342
- includeOriginDeviceInfo = True
343
- elif info.TokenType == 'authorization_code':
344
- includeOriginDeviceInfo = True
345
-
346
351
  # execute the Spotify Zeroconf API addUser request.
347
352
  responseData:dict = self._ConnectAddUser(
348
353
  info,
@@ -350,7 +355,6 @@ class ZeroconfConnect:
350
355
  password,
351
356
  loginId,
352
357
  apiMethodName,
353
- includeOriginDeviceInfo=includeOriginDeviceInfo
354
358
  )
355
359
 
356
360
  # process results.
@@ -402,7 +406,6 @@ class ZeroconfConnect:
402
406
  password,
403
407
  loginId,
404
408
  apiMethodName,
405
- includeOriginDeviceInfo=includeOriginDeviceInfo
406
409
  )
407
410
 
408
411
  # process results.
@@ -447,7 +450,6 @@ class ZeroconfConnect:
447
450
  password:str,
448
451
  loginId:str,
449
452
  apiMethodName:str,
450
- includeOriginDeviceInfo:bool=False,
451
453
  ) -> dict:
452
454
  """
453
455
  Execute the `addUser` request.
@@ -459,19 +461,14 @@ class ZeroconfConnect:
459
461
 
460
462
  # trace.
461
463
  apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
462
- apiMethodParms.AppendKeyValue("includeOriginDeviceInfo", includeOriginDeviceInfo)
463
464
  apiMethodParms.AppendKeyValue("info.Availability", info.Availability)
464
465
  apiMethodParms.AppendKeyValue("info.PublicKey", info.PublicKey)
465
466
  apiMethodParms.AppendKeyValue("info.DeviceId", info.DeviceId)
466
467
  apiMethodParms.AppendKeyValue("info.RemoteName", info.RemoteName)
467
468
  _logsi.LogMethodParmList(SILevel.Verbose, "Issuing Spotify Connect Zeroconf addUser request (ip=%s)" % self._HostIpAddress, apiMethodParms)
468
469
 
469
- # validations.
470
- if (includeOriginDeviceInfo is None):
471
- includeOriginDeviceInfo = False
472
-
473
470
  # formulate the blob.
474
- credentials:Credentials = Credentials(username, password)
471
+ credentials:Credentials = Credentials(username, password, AuthenticationTypes.USER_PASS)
475
472
  builder = BlobBuilder(credentials, info.DeviceId, info.PublicKey)
476
473
  blob = builder.build()
477
474
 
@@ -484,22 +481,36 @@ class ZeroconfConnect:
484
481
  'Connection': 'close'
485
482
  }
486
483
  _logsi.LogDictionary(SILevel.Debug, "ZeroconfConnect http request: '%s' (headers)" % (endpoint), reqHeaders)
487
-
488
- # set tokenType based on getInfo token type.
489
- # if TokenType is 'athorization_code', then use it as-is;
490
- # if TokenType is 'accesstoken', then use `default`.
491
- # otherwise, use 'default'.
484
+
485
+ # set defaults used by most manufacturers - we will tailor these in the
486
+ # logic below for specific manufacturers / devices as required:
487
+ # - clientKey should be the getInfo response PublicKey (base64 encoded).
488
+ # - exclude origin device information from the addUser request.
489
+ # - tokenType should be set to 'default'.
490
+ clientKey:str = builder.dh_keys.PublicKeyBase64String
491
+ includeOriginDeviceInfo:bool = False
492
492
  tokenType:str = 'default'
493
- if info.TokenType == 'authorization_code':
494
- tokenType = info.TokenType
495
493
 
496
- # set clientKey based on getInfo token type.
497
- # if TokenType is 'athorization_code', then clientKey should be null.
498
- # otherwise, clientKey should be the PublicKey (base64 encoded).
499
- clientKey:str = builder.dh_keys.PublicKeyBase64String
500
- if info.TokenType == 'authorization_code':
494
+ # include the origin device information if PublicKey="INVALID" (base64 encoded) and availability="NOT-LOADED".
495
+ if (info.PublicKey == 'SU5WQUxJRA==') and (info.Availability == 'NOT-LOADED'):
496
+ includeOriginDeviceInfo = True
497
+
498
+ # special processing for tokenType "authorization_code":
499
+ # - include origin device information in the addUser request.
500
+ # - set the tokenType to 'authorization_code'.
501
+ # - set the clientKey value to null, as it is contained in the token.
502
+ if info.TokenType == 'authorization_code':
501
503
  clientKey = ''
502
-
504
+ includeOriginDeviceInfo = True
505
+ tokenType = info.TokenType
506
+
507
+ # TODO - not sure of the blob credentials layout for AuthenticationTypes.SPOTIFY_TOKEN requests.
508
+ # get a Spotify WebServices API auth token that the Sonos device can use.
509
+ #credentials:Credentials = Credentials(username, authToken, AuthenticationTypes.SPOTIFY_TOKEN) # authToken is the password
510
+ #credentials:Credentials = Credentials('', authToken, AuthenticationTypes.SPOTIFY_TOKEN) # authToken is the password
511
+ #builder = BlobBuilder(credentials, info.DeviceId, info.PublicKey)
512
+ #blob = builder.build()
513
+
503
514
  # set request parameters.
504
515
  reqData={
505
516
  'action': 'addUser',
@@ -650,6 +661,7 @@ class ZeroconfConnect:
650
661
 
651
662
  # was a good http status code returned?
652
663
  if response.status_code == 200:
664
+
653
665
  # yes - default status results if they are not set.
654
666
  result.Status = 101
655
667
  if result.StatusString is None:
@@ -133,7 +133,7 @@ class ZeroconfGetInfo(ZeroconfResponse):
133
133
 
134
134
  Device aliases will show up as separate devices in the Spotify app.
135
135
 
136
- Note
136
+ A maximum of 8 aliases are supported (SP_MAX_DEVICE_ALIASES).
137
137
 
138
138
  Please refer to the `RemoteName` property for more information.
139
139
  """
@@ -146,9 +146,12 @@ class ZeroconfGetInfo(ZeroconfResponse):
146
146
  The SpZeroConfVars availability field returned by SpZeroConfGetVars.
147
147
 
148
148
  The following are values that I have encountered thus far:
149
- - "" = Device is available and ready for use.
149
+ - "" - Device is available and ready for use.
150
150
  - "UNAVAILABLE" - Device is unavailable, and should probably be rebooted.
151
- - "NOT-LOADED" - Spotify SDK / API is not loaded.
151
+ - "NOT-LOADED" - Spotify SDK / API is not loaded.
152
+
153
+ The maximum length of the availability string (SP_MAX_AVAILABILITY_LENGTH)
154
+ is 15 characters (not counting terminating NULL).
152
155
  """
153
156
  return self._Availability
154
157
 
@@ -157,6 +160,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
157
160
  def BrandDisplayName(self) -> str:
158
161
  """
159
162
  A UTF-8-encoded brand name of the hardware device, for hardware integrations (e.g. "Bose", "Onkyo", etc).
163
+
164
+ The maximum length of the brand display name (SP_MAX_BRAND_NAME_LENGTH)
165
+ is 32 characters (not counting terminating NULL).
160
166
  """
161
167
  return self._BrandDisplayName
162
168
 
@@ -165,6 +171,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
165
171
  def ClientId(self) -> str:
166
172
  """
167
173
  Client id of the application (e.g. "79ebcb219e8e4e123456789000123456").
174
+
175
+ The maximum length of the client ID value (SP_MAX_CLIENT_ID_LENGTH)
176
+ is 32 characters (not counting terminating NULL).
168
177
  """
169
178
  return self._ClientId
170
179
 
@@ -173,6 +182,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
173
182
  def DeviceId(self) -> str:
174
183
  """
175
184
  Unique device ID used for ZeroConf logins (e.g. "30fbc80e35598f3c242f2120413c943dfd9715fe").
185
+
186
+ The maximum length of the device ID value used for ZeroConf logins (SP_MAX_DEVICE_ID_LENGTH)
187
+ is 64 characters (not counting terminating NULL).
176
188
  """
177
189
  return self._DeviceId
178
190
 
@@ -191,20 +203,23 @@ class ZeroconfGetInfo(ZeroconfResponse):
191
203
  Type of the device (e.g. "SPEAKER", "AVR", etc).
192
204
 
193
205
  Can be any of the following `SpDeviceType` devices:
194
- - kSpDeviceTypeComputer Laptop or desktop computer device.
195
- - kSpDeviceTypeTablet Tablet PC device.
196
- - kSpDeviceTypeSmartphone Smartphone device.
197
- - kSpDeviceTypeSpeaker Speaker device.
198
- - kSpDeviceTypeTV Television device.
199
- - kSpDeviceTypeAVR Audio/Video receiver device.
200
- - kSpDeviceTypeSTB Set-Top Box device.
201
- - kSpDeviceTypeAudioDongle Audio dongle device.
202
- - kSpDeviceTypeGameConsole Game console device.
203
- - kSpDeviceTypeCastVideo Chromecast Video.
204
- - kSpDeviceTypeCastAudio Chromecast Audio.
205
- - kSpDeviceTypeAutomobile Automobile.
206
- - kSpDeviceTypeSmartwatch Smartwatch.
207
- - kSpDeviceTypeChromebook Chromebook.
206
+ - Computer Laptop or desktop computer device.
207
+ - Tablet Tablet PC device.
208
+ - Smartphone Smartphone device.
209
+ - Speaker Speaker device.
210
+ - TV Television device.
211
+ - AVR Audio/Video receiver device.
212
+ - STB Set-Top Box device.
213
+ - AudioDongle Audio dongle device.
214
+ - GameConsole Game console device.
215
+ - CastVideo Chromecast Video.
216
+ - CastAudio Chromecast Audio.
217
+ - Automobile Automobile.
218
+ - Smartwatch Smartwatch.
219
+ - Chromebook Chromebook.
220
+
221
+ The maximum length of the device type string (SP_MAX_DEVICE_TYPE_LENGTH)
222
+ is 15 characters (not counting terminating NULL).
208
223
  """
209
224
  return self._DeviceType
210
225
 
@@ -221,6 +236,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
221
236
  def GroupStatus(self) -> str:
222
237
  """
223
238
  The SpZeroConfVars group_status field returned by SpZeroConfGetVars (e.g. "NONE").
239
+
240
+ The maximum length of the group status string (SP_MAX_GROUP_STATUS_LENGTH)
241
+ is 15 characters (not counting terminating NULL).
224
242
  """
225
243
  return self._GroupStatus
226
244
 
@@ -255,6 +273,19 @@ class ZeroconfGetInfo(ZeroconfResponse):
255
273
  return False
256
274
 
257
275
 
276
+ @property
277
+ def IsBrandSonos(self) -> bool:
278
+ """
279
+ Returns True if the device is a 'Sonos' branded device; otherwise, False.
280
+
281
+ Determination is made based upon the `BrandDisplayName` property value.
282
+ """
283
+ result:bool = False
284
+ if (isinstance(self._BrandDisplayName, str)) and (self._BrandDisplayName.lower().strip() == 'sonos'):
285
+ result = True
286
+ return result
287
+
288
+
258
289
  @property
259
290
  def LibraryVersion(self) -> str:
260
291
  """
@@ -267,6 +298,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
267
298
  def ModelDisplayName(self) -> str:
268
299
  """
269
300
  A UTF-8-encoded model name of the hardware device, for hardware integrations (e.g. "Soundtouch").
301
+
302
+ The maximum length of the model display name (SP_MAX_MODEL_NAME_LENGTH)
303
+ is 30 characters (not counting terminating NULL).
270
304
  """
271
305
  return self._ModelDisplayName
272
306
 
@@ -283,6 +317,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
283
317
  def PublicKey(self) -> str:
284
318
  """
285
319
  Public key used in ZeroConf logins (e.g. "G+ZM4irhc...").
320
+
321
+ The maximum length of the public key used in ZeroConf logins (SP_MAX_PUBLIC_KEY_LENGTH)
322
+ is 149 characters (not counting terminating NULL).
286
323
  """
287
324
  return self._PublicKey
288
325
 
@@ -328,6 +365,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
328
365
  def Scope(self) -> str:
329
366
  """
330
367
  OAuth scope requested when authenticating with the Spotify backend (e.g. "streaming").
368
+
369
+ The maximum length of the token type string (SP_MAX_SCOPE_LENGTH)
370
+ is 64 characters (not counting terminating NULL).
331
371
  """
332
372
  return self._Scope
333
373
 
@@ -344,6 +384,8 @@ class ZeroconfGetInfo(ZeroconfResponse):
344
384
  def SupportedDrmMediaFormats(self) -> list:
345
385
  """
346
386
  The SpZeroConfVars supported_drm_media_formats field returned by SpZeroConfGetVars (e.g. []).
387
+
388
+ A maximum of 8 formats are supported (SP_MAX_SUPPORTED_FORMATS).
347
389
  """
348
390
  return self._SupportedDrmMediaFormats
349
391
 
@@ -351,7 +393,13 @@ class ZeroconfGetInfo(ZeroconfResponse):
351
393
  @property
352
394
  def TokenType(self) -> str:
353
395
  """
354
- Token type provided by the client (e.g. "accesstoken", "authorization_code", etc).
396
+ Token type provided by the client:
397
+ - "accesstoken" Access token.
398
+ - "authorization_code" OAuth Authorization Code token.
399
+ - "default" Default access token.
400
+
401
+ The maximum length of the token type string (SP_MAX_TOKEN_TYPE_LENGTH)
402
+ is 30 characters (not counting terminating NULL).
355
403
  """
356
404
  return self._TokenType
357
405
 
@@ -360,6 +408,9 @@ class ZeroconfGetInfo(ZeroconfResponse):
360
408
  def Version(self) -> str:
361
409
  """
362
410
  ZeroConf API version number (e.g. "2.10.0").
411
+
412
+ The maximum length of the library version string (SP_MAX_VERSION_LENGTH)
413
+ is 30 characters (not counting terminating NULL).
363
414
  """
364
415
  return self._Version
365
416