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.
- {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/METADATA +2 -1
- {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/RECORD +23 -23
- 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 +2 -77
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/spotifyconnectdevice.py +12 -0
- spotifywebapipython/models/zeroconfdiscoveryresult.py +20 -1
- spotifywebapipython/spotifyclient.py +58 -116
- spotifywebapipython/zeroconfapi/zeroconfconnect.py +54 -9
- {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/WHEEL +0 -0
- {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
|
-
#
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
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
|
|
|
File without changes
|
|
File without changes
|
{spotifywebapiPython-1.0.130.dist-info → spotifywebapiPython-1.0.132.dist-info}/top_level.txt
RENAMED
|
File without changes
|