spotifywebapiPython 1.0.130__py3-none-any.whl → 1.0.132__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 (23) hide show
  1. {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/METADATA +2 -1
  2. {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/RECORD +23 -23
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +17 -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 -77
  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/spotifyconnectdevice.py +12 -0
  18. spotifywebapipython/models/zeroconfdiscoveryresult.py +20 -1
  19. spotifywebapipython/spotifyclient.py +58 -116
  20. spotifywebapipython/zeroconfapi/zeroconfconnect.py +54 -9
  21. {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/LICENSE +0 -0
  22. {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/WHEEL +0 -0
  23. {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/top_level.txt +0 -0
@@ -49,6 +49,11 @@ from .const import (
49
49
  CACHE_SOURCE_CACHED:str = "cached"
50
50
  CACHE_SOURCE_CURRENT:str = "current"
51
51
 
52
+ DELAY_DISCONNECT:float = 0.350
53
+ """
54
+ Time to wait after a Spotify Connect Disconnect command is issued (350ms).
55
+ """
56
+
52
57
  SPOTIFY_DJ_PLAYLIST_ID = "37i9dqzf1eykqdzj48dyyq"
53
58
 
54
59
  SPOTIFY_ONLINE_LINK_PREFIX = "https://open.spotify.com"
@@ -6944,10 +6949,41 @@ class SpotifyClient:
6944
6949
  # Spotify Connect Zeroconf `resetUsers` endpoint is not implemented and will
6945
6950
  # generate a 404 request response.
6946
6951
  if (info.BrandDisplayName != 'librespot'):
6952
+
6947
6953
  _logsi.LogVerbose("Issuing Disconnect to Spotify Connect device '%s' for current user context '%s'" % (scDevice.Title, deviceActiveUser))
6948
6954
  zcfResult = zconn.Disconnect(delay)
6949
6955
  status = status + 'disconnected from user context "%s"; ' % deviceActiveUser
6956
+
6957
+ # delay just a little to give the device time to process the disconnect.
6958
+ # some devices only support a single connection on the spotify connect
6959
+ # zeroconf webserver, so we have to give it a little time to process the
6960
+ # disconnect command before we try to call the Connect method.
6961
+ _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % DELAY_DISCONNECT)
6962
+ time.sleep(DELAY_DISCONNECT)
6963
+
6964
+ # some manufactureres will reset the device host ip port after a disconnect (e.g. "Denon", etc).
6965
+ # due to this, we must issue a call to zeroconf to re-discover the device,
6966
+ # and update the device results with the updated discovery details.
6967
+
6968
+ # re-discover Spotify Connect devices on the network.
6969
+ _logsi.LogVerbose("Rediscovering Spotify Connect devices on the local network (due to previous disconnect)")
6970
+ rediscovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
6971
+ rediscovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
6972
+
6973
+ # process all rediscovered devices, and update selected device.
6974
+ rediscoverResult:ZeroconfDiscoveryResult
6975
+ for rediscoverResult in rediscovery.DiscoveryResults:
6976
+ if (rediscoverResult.Key == scDevice.DiscoveryResult.Key):
6977
+ if (rediscoverResult.HostIpAddress != scDevice.DiscoveryResult.HostIpAddress):
6978
+ _logsi.LogVerbose("Spotify Connect device HostIpAddress changed to \"%s\" from \"%s\" after disconnect" % (rediscoverResult.HostIpAddress, scDevice.DiscoveryResult.HostIpAddress))
6979
+ if (rediscoverResult.HostIpPort != scDevice.DiscoveryResult.HostIpPort):
6980
+ _logsi.LogVerbose("Spotify Connect device HostIpPort changed to \"%s\" from \"%s\" after disconnect" % (rediscoverResult.HostIpPort, scDevice.DiscoveryResult.HostIpPort))
6981
+ _logsi.LogVerbose("Updating Spotify Connect device \"%s\" DiscoveryResult instance with rediscovered properties" % (scDevice.Title))
6982
+ scDevice.DiscoveryResult = rediscoverResult
6983
+ break
6984
+
6950
6985
  else:
6986
+
6951
6987
  status = status + 'disconnect bypassed for librespot device; '
6952
6988
 
6953
6989
  # connect the device to OUR Spotify Connect user context.
@@ -9288,8 +9324,6 @@ class SpotifyClient:
9288
9324
  deviceResultId:str = None
9289
9325
  deviceResult:SpotifyConnectDevice = None
9290
9326
 
9291
- DELAY_DISCONNECT:float = 0.350
9292
-
9293
9327
  try:
9294
9328
 
9295
9329
  # trace.
@@ -9461,13 +9495,34 @@ class SpotifyClient:
9461
9495
 
9462
9496
  _logsi.LogVerbose("Issuing Disconnect to Spotify Connect device '%s' for current user context '%s'" % (deviceResult.Title, deviceActiveUser))
9463
9497
  zcfResult = zconn.Disconnect(delay)
9464
-
9498
+
9465
9499
  # delay just a little to give the device time to process the disconnect.
9466
9500
  # some devices only support a single connection on the spotify connect
9467
9501
  # zeroconf webserver, so we have to give it a little time to process the
9468
9502
  # disconnect command before we try to call the Connect method.
9469
9503
  _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % DELAY_DISCONNECT)
9470
9504
  time.sleep(DELAY_DISCONNECT)
9505
+
9506
+ # some manufactureres will reset the device host ip port after a disconnect (e.g. "Denon", etc).
9507
+ # due to this, we must issue a call to zeroconf to re-discover the device,
9508
+ # and update the device results with the updated discovery details.
9509
+
9510
+ # re-discover Spotify Connect devices on the network
9511
+ _logsi.LogVerbose("Rediscovering Spotify Connect devices on the local network (due to previous disconnect)")
9512
+ rediscovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
9513
+ rediscovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
9514
+
9515
+ # process all rediscovered devices, and update selected device.
9516
+ rediscoverResult:ZeroconfDiscoveryResult
9517
+ for rediscoverResult in rediscovery.DiscoveryResults:
9518
+ if (rediscoverResult.Key == scDevice.DiscoveryResult.Key):
9519
+ if (rediscoverResult.HostIpAddress != scDevice.DiscoveryResult.HostIpAddress):
9520
+ _logsi.LogVerbose("Spotify Connect device HostIpAddress changed to \"%s\" from \"%s\" after disconnect" % (rediscoverResult.HostIpAddress, scDevice.DiscoveryResult.HostIpAddress))
9521
+ if (rediscoverResult.HostIpPort != scDevice.DiscoveryResult.HostIpPort):
9522
+ _logsi.LogVerbose("Spotify Connect device HostIpPort changed to \"%s\" from \"%s\" after disconnect" % (rediscoverResult.HostIpPort, scDevice.DiscoveryResult.HostIpPort))
9523
+ _logsi.LogVerbose("Updating Spotify Connect device \"%s\" DiscoveryResult instance with rediscovered properties" % (scDevice.Title))
9524
+ scDevice.DiscoveryResult = rediscoverResult
9525
+ break
9471
9526
 
9472
9527
  # connect the device to OUR Spotify Connect user context.
9473
9528
  # note that the result here only indicates that the connect was submitted - NOT that it was successful!
@@ -12336,119 +12391,6 @@ class SpotifyClient:
12336
12391
  _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
12337
12392
 
12338
12393
 
12339
- def PlayerResolveDeviceId(
12340
- self,
12341
- deviceValue:str,
12342
- verifyUserContext:bool=True,
12343
- verifyTimeout:float=5.0,
12344
- delay:float=0.25
12345
- ) -> str:
12346
- """
12347
- This method has been deprecated.
12348
- Use the `GetSpotifyConnectDevice` method instead.
12349
-
12350
- Resolves a Spotify Connect device identifier from a specified device id, name, alias id,
12351
- or alias name. This will ensure that the device id can be found on the network, as well
12352
- as connect to the device if necessary with the current user context.
12353
-
12354
- Args:
12355
- deviceValue (str):
12356
- The device id / name value to check.
12357
- verifyUserContext (bool):
12358
- If True, the active user context of the resolved device is checked to ensure it
12359
- matches the user context specified on the class constructor.
12360
- If False, the user context will not be checked.
12361
- Default is True.
12362
- verifyTimeout (float):
12363
- Maximum time to wait (in seconds) for the device to become active in the Spotify
12364
- Connect device list. This value is only used if a Connect command has to be
12365
- issued to activate the device.
12366
- Default is 5; value range is 0 - 10.
12367
- delay (float):
12368
- Time delay (in seconds) to wait AFTER issuing any command to the device.
12369
- This delay will give the spotify zeroconf api time to process the change before
12370
- another command is issued.
12371
- Default is 0.25; value range is 0 - 10.
12372
-
12373
- Returns:
12374
- A device Id for the deviceValue if one could be resolved;
12375
- otherwise, a null value with the understanding that subsequent operations will probably
12376
- fail since it's not in the device list.
12377
-
12378
- Raises:
12379
- SpotifyWebApiError:
12380
- If the Spotify Web API request was for a non-authorization service
12381
- and the response contains error information.
12382
- SpotifyZeroconfApiError:
12383
- If any Spotify Zeroconf API request failed.
12384
- SpotifApiError:
12385
- If the method fails for any other reason.
12386
-
12387
- A Zeroconf discovery process is initiated to search for all Spotify Connect devices connected
12388
- to the local network. A Spotify Zeroconf API `getInfo` call is then issued for each device
12389
- that was found, searching for a match on the `deviceValue` argument. Depending on the format
12390
- of the `deviceValue`, the search will match on either a Device ID or RemoteName as well as any
12391
- alias ID's or Names that are in use (for multi-room configurations).
12392
-
12393
- The device ID value is returned if a match is found or if the `device_value` argument is null;
12394
- otherwise, a null value with the understanding that subsequent operations will probably fail
12395
- since it's not in the device list.
12396
-
12397
- A user context switch will also be performed if a device id is resolved AND the active user of the
12398
- device does not match the `SpotifyConnectUsername` argument specified on the class constructor.
12399
- The user context switch is bypassed if the `verifyUserContext` argument is False, or if the
12400
- `SpotifyConnectUsername` was not supplied on the class constructor. If the user context is to be
12401
- switched, then a Disconnect will be issued if a user context is active on the device followed by
12402
- a Connect to the user context specified on the class constructor.
12403
- """
12404
- apiMethodName:str = 'PlayerResolveDeviceId'
12405
- apiMethodParms:SIMethodParmListContext = None
12406
- deviceIdResult:str = None
12407
-
12408
- try:
12409
-
12410
- # TODO method is deprecated as of 2024/08/15, v1.0.80.
12411
- # remove in a future release.
12412
-
12413
- # trace.
12414
- apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
12415
- apiMethodParms.AppendKeyValue("deviceValue", deviceValue)
12416
- apiMethodParms.AppendKeyValue("verifyUserContext", verifyUserContext)
12417
- apiMethodParms.AppendKeyValue("verifyTimeout", verifyTimeout)
12418
- apiMethodParms.AppendKeyValue("delay", delay)
12419
- _logsi.LogMethodParmList(SILevel.Verbose, "Resolving Spotify Connect Player Device Id", apiMethodParms)
12420
-
12421
- # trace.
12422
- _logsi.LogWarning("SpotifyWebApiPython `PlayerResolveDeviceId` method is deprecated, and will be removed in a future release; use the `GetSpotifyConnectDevice` instead.")
12423
-
12424
- # call newer method to resolve the device id.
12425
- scDevice:SpotifyConnectDevice = self.GetSpotifyConnectDevice(
12426
- deviceValue=deviceValue,
12427
- verifyTimeout=verifyTimeout,
12428
- refreshDeviceList=True,
12429
- activateDevice=True,
12430
- delay=delay)
12431
-
12432
- # if device not found then we are done.
12433
- if scDevice is None:
12434
- return None
12435
-
12436
- # otherwise return the device id value.
12437
- return scDevice.Id
12438
-
12439
- except SpotifyZeroconfApiError: raise # pass handled exceptions on thru
12440
- except SpotifyApiError: raise # pass handled exceptions on thru
12441
- except Exception as ex:
12442
-
12443
- # format unhandled exception.
12444
- raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
12445
-
12446
- finally:
12447
-
12448
- # trace.
12449
- _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
12450
-
12451
-
12452
12394
  def PlayerSetRepeatMode(self,
12453
12395
  state:str='off',
12454
12396
  deviceId:str=None,
@@ -1192,7 +1192,7 @@ class ZeroconfConnect:
1192
1192
  apiMethodParms.AppendKeyValue("CPath", self._CPath)
1193
1193
  apiMethodParms.AppendKeyValue("Version", self._Version)
1194
1194
  apiMethodParms.AppendKeyValue("Uri", self._Uri)
1195
- _logsi.LogMethodParmList(SILevel.Verbose, "Get information from Spotify Connect Zeroconf API (ip=%s)" % self._HostIpAddress, apiMethodParms)
1195
+ _logsi.LogMethodParmList(SILevel.Verbose, "Get information from Spotify Connect Zeroconf API (ip=%s:%s)" % (self._HostIpAddress, self._HostIpPort), apiMethodParms)
1196
1196
 
1197
1197
  # set request endpoint.
1198
1198
  endpoint:str = self.GetEndpoint("getInfo")
@@ -1211,14 +1211,59 @@ class ZeroconfConnect:
1211
1211
  }
1212
1212
  _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (params)" % (endpoint), reqParams)
1213
1213
 
1214
- # execute spotify zeroconf api request.
1215
- response = requests.get(
1216
- self._Uri,
1217
- timeout=5,
1218
- headers=reqHeaders,
1219
- params=reqParams
1220
- )
1221
-
1214
+ # try connecting to the device until we timeout or there are no more "connection refused" errors.
1215
+ loopTotalDelay:float = 0
1216
+ LOOP_DELAY:float = 0.25
1217
+ LOOP_TIMEOUT:float = 2.0
1218
+ while True:
1219
+
1220
+ try:
1221
+
1222
+ # execute spotify zeroconf api request.
1223
+ response = requests.get(
1224
+ self._Uri,
1225
+ timeout=5,
1226
+ headers=reqHeaders,
1227
+ params=reqParams
1228
+ )
1229
+
1230
+ # TEST TODO - force a connection refused error for testing.
1231
+ # if (self._HostIpAddress == "192.168.1.82"):
1232
+ # if loopTotalDelay > 1:
1233
+ # pass
1234
+ # else:
1235
+ # raise Exception("Failed to establish a new connection: [Errno 111] Connection refused")
1236
+ # #raise ValueError("Some other request exception test")
1237
+
1238
+ # if device connection was previously delayed, then log the info
1239
+ # as it may be helpful in debugging other issues.
1240
+ if (loopTotalDelay > 0):
1241
+ _logsi.LogVerbose("Spotify Connect device (ip=%s:%s) started accepting connections after %f seconds from initial request" % (self._HostIpAddress, self._HostIpPort, loopTotalDelay))
1242
+
1243
+ # no exception; break out of retry loop.
1244
+ break
1245
+
1246
+ except Exception as ex:
1247
+
1248
+ # check for connection refused error.
1249
+ exMsg:str = str(ex).lower()
1250
+ if (exMsg.find("connection refused") > -1):
1251
+
1252
+ # retry after a slight delay.
1253
+ _logsi.LogVerbose("Spotify Connect device (ip=%s:%s) connection refused exception; delaying for %s seconds to allow the device to start accepting connections" % (self._HostIpAddress, self._HostIpPort, LOOP_DELAY))
1254
+ time.sleep(LOOP_DELAY)
1255
+ loopTotalDelay = loopTotalDelay + LOOP_DELAY
1256
+
1257
+ # only check so many times before we give up.
1258
+ if (loopTotalDelay >= LOOP_TIMEOUT):
1259
+ _logsi.LogWarning("Spotify Connect device (ip=%s:%s) wait time exceeded for device to start accepting connections; gave up after %f seconds from initial request" % (self._HostIpAddress, self._HostIpPort, loopTotalDelay))
1260
+ raise
1261
+
1262
+ else:
1263
+
1264
+ # some other exception occured - stop retrying the connection.
1265
+ raise
1266
+
1222
1267
  # check response for initial errors, and return json response.
1223
1268
  responseData:dict = self._CheckResponseForErrors(response, apiMethodName, endpoint)
1224
1269