spotifywebapiPython 1.0.51__py3-none-any.whl → 1.0.53__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 (26) hide show
  1. {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/RECORD +26 -26
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +15 -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 +29 -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 +31 -7
  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 +103 -0
  18. spotifywebapipython/models/zeroconfdiscoveryresult.py +13 -0
  19. spotifywebapipython/sautils.py +31 -0
  20. spotifywebapipython/spotifyclient.py +105 -92
  21. spotifywebapipython/zeroconfapi/zeroconfconnect.py +46 -10
  22. spotifywebapipython/zeroconfapi/zeroconfgetinfo.py +32 -0
  23. spotifywebapipython/zeroconfapi/zeroconfresponse.py +24 -0
  24. {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/LICENSE +0 -0
  25. {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/WHEEL +0 -0
  26. {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ from typing import Iterator
5
5
  from ..sautils import export
6
6
  from .device import Device
7
7
  from .spotifyconnectdevice import SpotifyConnectDevice
8
+ from .zeroconfdiscoveryresult import ZeroconfDiscoveryResult
8
9
  from spotifywebapipython.zeroconfapi import ZeroconfGetInfo, ZeroconfGetInfoAlias
9
10
 
10
11
  @export
@@ -58,6 +59,108 @@ class SpotifyConnectDevices():
58
59
  return 0
59
60
 
60
61
 
62
+ def AddDynamicDevice(self, device:Device, activeUser:str) -> None:
63
+ """
64
+ Adds a new dynamic device entry to the `Items` collection.
65
+
66
+ Dynamic devices are Spotify Connect devices that are not found in Zeroconf discovery
67
+ process, but still exist in the player device list. These are usually devices in
68
+ a Spotify Connect web or mobile player.
69
+ """
70
+ # validations.
71
+ if (device is None) or (not isinstance(device, Device)):
72
+ return
73
+
74
+ # build zeroconf discovery result with what we can.
75
+ discoverResult:ZeroconfDiscoveryResult = ZeroconfDiscoveryResult()
76
+ discoverResult.DeviceName = device.Name
77
+ discoverResult.HostIpv4Address = '127.0.0.1'
78
+ discoverResult.HostIpPort = 0
79
+ discoverResult.Name = device.Name
80
+ discoverResult.SpotifyConnectCPath = '/zc'
81
+ discoverResult.SpotifyConnectVersion = '1.0'
82
+
83
+ # build zeroconf getinfo result with what we can.
84
+ info:ZeroconfGetInfo = ZeroconfGetInfo()
85
+ info.ActiveUser = activeUser
86
+ info.DeviceId = device.Id
87
+ info.DeviceType = device.Type
88
+ info.RemoteName = device.Name
89
+ info.SpotifyError = 0
90
+ info.Status = 101
91
+ info.StatusString = 'OK'
92
+
93
+ # add the device.
94
+ scDevice:SpotifyConnectDevice = SpotifyConnectDevice()
95
+ scDevice.DeviceInfo = info
96
+ scDevice.DiscoveryResult = discoverResult
97
+ self._Items.append(scDevice)
98
+
99
+
100
+ def ContainsDeviceId(self, value:str) -> bool:
101
+ """
102
+ Returns True if the `Items` collection contains the specified device id value;
103
+ otherwise, False.
104
+
105
+ Alias entries (if any) are also compared.
106
+ """
107
+ result:bool = False
108
+ if value is None:
109
+ return result
110
+
111
+ # convert case for comparison.
112
+ value = value.lower()
113
+
114
+ # process all discovered devices.
115
+ scDevice:SpotifyConnectDevice
116
+ for scDevice in self._Items:
117
+ if (scDevice.DeviceInfo.HasAliases):
118
+ scAlias:ZeroconfGetInfoAlias
119
+ for scAlias in scDevice.DeviceInfo.Aliases:
120
+ if (scAlias.Id.lower() == value):
121
+ result = True
122
+ break
123
+ if result:
124
+ break
125
+ else:
126
+ if (scDevice.DeviceInfo.DeviceId.lower() == value):
127
+ result = True
128
+ break
129
+ return result
130
+
131
+
132
+ def ContainsDeviceName(self, value:str) -> bool:
133
+ """
134
+ Returns True if the `Items` collection contains the specified device name value;
135
+ otherwise, False.
136
+
137
+ Alias entries (if any) are also compared.
138
+ """
139
+ result:bool = False
140
+ if value is None:
141
+ return result
142
+
143
+ # convert case for comparison.
144
+ value = value.lower()
145
+
146
+ # process all discovered devices.
147
+ scDevice:SpotifyConnectDevice
148
+ for scDevice in self._Items:
149
+ if (scDevice.DeviceInfo.HasAliases):
150
+ scAlias:ZeroconfGetInfoAlias
151
+ for scAlias in scDevice.DeviceInfo.Aliases:
152
+ if (scAlias.Name.lower() == value):
153
+ result = True
154
+ break
155
+ if result:
156
+ break
157
+ else:
158
+ if (scDevice.DeviceInfo.RemoteName.lower() == value):
159
+ result = True
160
+ break
161
+ return result
162
+
163
+
61
164
  def GetDeviceList(self) -> list[Device]:
62
165
  """
63
166
  Returns a list of `Device` objects that can be used to build a selection list
@@ -127,6 +127,19 @@ class ZeroconfDiscoveryResult:
127
127
  self._HostTTL = value
128
128
 
129
129
 
130
+ @property
131
+ def IsDynamicDevice(self) -> bool:
132
+ """
133
+ Returns True if the device is a dynamic device;
134
+ otherwise, False.
135
+
136
+ Dynamic devices are Spotify Connect devices that are not found in Zeroconf discovery
137
+ process, but still exist in the player device list. These are usually Spotify Connect
138
+ web or mobile players with temporary device id's.
139
+ """
140
+ return (self._HostIpPort == 0)
141
+
142
+
130
143
  @property
131
144
  def Key(self) -> str:
132
145
  """
@@ -56,6 +56,37 @@ def export(fn):
56
56
  return fn
57
57
 
58
58
 
59
+ def validateDelay(
60
+ self,
61
+ delay:float,
62
+ default:float=0.5,
63
+ maxDelay:float=10
64
+ ) -> float:
65
+ """
66
+ Validates a delay value.
67
+
68
+ Args:
69
+ delay (int):
70
+ The delay value to validate.
71
+ default (int):
72
+ The default delay value to set if the user-input delay is not valid.
73
+ maxDelay (int):
74
+ The maximum delay value allowed.
75
+ Default is 10.
76
+ """
77
+ if (isinstance(delay,int)):
78
+ delay = float(delay)
79
+
80
+ if (not isinstance(delay,float)) or (delay < 0):
81
+ result = default
82
+ elif (delay > maxDelay):
83
+ result = maxDelay
84
+ else:
85
+ result = delay
86
+
87
+ return result
88
+
89
+
59
90
  def _xmlGetInnerText(elmNode, default:str=None) -> str:
60
91
  """
61
92
  Finds the specified xml node inner text from the root Element object and returns its value
@@ -15,7 +15,7 @@ from zeroconf import Zeroconf
15
15
  from .oauthcli import AuthClient
16
16
  from .models import *
17
17
  from .models import UserProfile as UserProfileCurrentUser
18
- from .sautils import export, GetUnixTimestampMSFromUtcNow, _xmlGetInnerText
18
+ from .sautils import export, GetUnixTimestampMSFromUtcNow, _xmlGetInnerText, validateDelay
19
19
  from .saappmessages import SAAppMessages
20
20
  from .spotifyapierror import SpotifyApiError
21
21
  from .spotifyapimessage import SpotifyApiMessage
@@ -631,32 +631,6 @@ class SpotifyClient:
631
631
  return result
632
632
 
633
633
 
634
- def _ValidateDelay(self, delay:float, default:float=0.5, maxDelay:float=10) -> float:
635
- """
636
- Validates a delay value.
637
-
638
- Args:
639
- delay (int):
640
- The delay value to validate.
641
- default (int):
642
- The default delay value to set if the user-input delay is not valid.
643
- maxDelay (int):
644
- The maximum delay value allowed.
645
- Default is 10.
646
- """
647
- if (isinstance(delay,int)):
648
- delay = float(delay)
649
-
650
- if (not isinstance(delay,float)) or (delay < 0):
651
- result = default
652
- elif (delay > maxDelay):
653
- result = maxDelay
654
- else:
655
- result = delay
656
-
657
- return result
658
-
659
-
660
634
  def _ValidateMarket(self,
661
635
  market:str,
662
636
  ) -> str:
@@ -5945,10 +5919,10 @@ class SpotifyClient:
5945
5919
  def PlayerActivateDevices(
5946
5920
  self,
5947
5921
  verifyUserContext:bool=False,
5948
- delay:float=0.50,
5949
- ) -> None:
5922
+ delay:float=0.25
5923
+ ) -> str:
5950
5924
  """
5951
- Activates all Spotify Connect player devices, and (optionally) switches the active user
5925
+ Activates all static Spotify Connect player devices, and (optionally) switches the active user
5952
5926
  context to the current user context.
5953
5927
 
5954
5928
  Args:
@@ -5961,7 +5935,10 @@ class SpotifyClient:
5961
5935
  Time delay (in seconds) to wait AFTER issuing the final Connect command (if necessary).
5962
5936
  This delay will give the spotify web api time to process the device list change before
5963
5937
  another command is issued.
5964
- Default is 0.50; value range is 0 - 10.
5938
+ Default is 0.25; value range is 0 - 10.
5939
+
5940
+ Returns:
5941
+ A string that contains status text.
5965
5942
 
5966
5943
  Raises:
5967
5944
  SpotifyWebApiError:
@@ -5982,6 +5959,10 @@ class SpotifyClient:
5982
5959
  The user context switch is bypassed if the `verifyUserContext` argument is False. If the user context
5983
5960
  is to be switched, then a Disconnect will be issued if a user context is active on the device followed
5984
5961
  by a Connect to the user context specified on the class constructor.
5962
+
5963
+ Dynamic Spotify Connect devices are not processed by this method, as they are temporary devices and are
5964
+ already active and in the device list. These devices are not found in Zeroconf discovery process, and only
5965
+ exist in the player device list. These are usually Spotify Connect web or mobile players with temporary device id's.
5985
5966
 
5986
5967
  <details>
5987
5968
  <summary>Sample Code</summary>
@@ -6009,46 +5990,44 @@ class SpotifyClient:
6009
5990
  raise SpotifyZeroconfApiError(SAAppMessages.ARGUMENT_REQUIRED_ERROR % (apiMethodName, 'SpotifyConnectUsername'), logsi=_logsi)
6010
5991
 
6011
5992
  # validations.
6012
- delay = self._ValidateDelay(delay, 0.50, 10)
5993
+ delay = validateDelay(delay, 0.25, 10)
6013
5994
  if (not isinstance(verifyUserContext, bool)):
6014
5995
  verifyUserContext = False
6015
5996
 
6016
5997
  deviceActiveUser:str = None
6017
5998
  deviceIdResult:str = None
6018
-
6019
- # discover Spotify Connect devices on the network, waiting up to the specified timeout
6020
- # for all devices to be discovered. this will return all Spotify Connect devices that
6021
- # are currently powered on, and are registered to Zeroconf / mDNS.
6022
- _logsi.LogVerbose("Discovering Spotify Connect devices on the local network")
6023
- discovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
6024
- discovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
5999
+ discoverResult:ZeroconfDiscoveryResult
6000
+ info:ZeroconfGetInfo
6001
+ scDevice:SpotifyConnectDevice
6002
+
6003
+ # get all Spotify Connect available devices on the network.
6004
+ scDevices:SpotifyConnectDevices = self.GetSpotifyConnectDevices()
6025
6005
 
6026
6006
  # process all discovered devices.
6027
- discoverResult:ZeroconfDiscoveryResult
6028
- for discoverResult in discovery.DiscoveryResults:
6007
+ for scDevice in scDevices:
6029
6008
 
6030
- # get the id from the device via the zeroconf API getInfo endpoint, as the id is not
6031
- # returned in the zeroconf discovery result.
6032
- zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpv4Address,
6033
- discoverResult.HostIpPort,
6034
- discoverResult.SpotifyConnectCPath,
6035
- useSSL=False)
6036
- info:ZeroconfGetInfo = zconn.GetInformation()
6009
+ discoverResult = scDevice.DiscoveryResult
6010
+ info = scDevice.DeviceInfo
6037
6011
 
6038
6012
  # store the currently active user of the device, in case we need to switch users later on.
6039
6013
  deviceActiveUser = info.ActiveUser.lower()
6040
6014
  deviceIdResult = info.DeviceId
6041
6015
 
6042
- status:str = "INACTIVE"
6016
+ status:str = "INACTIVE, "
6043
6017
  if (info.HasActiveUser):
6044
6018
  status = "ACTIVE, "
6045
6019
 
6046
6020
  # did we resolve the device id?
6047
6021
  if deviceIdResult is not None:
6048
6022
 
6023
+ # is this a dynamic device? if so, then we can't control it.
6024
+ if (discoverResult.IsDynamicDevice):
6025
+ _logsi.LogVerbose("Spotify Connect Device id '%s' (%s) is a dynamic device; we cannot control it" % (deviceIdResult, info.RemoteName))
6026
+ status = status + "dynamic device, cannot switch user context"
6027
+
6049
6028
  # does our Spotify Connect user account need to take control of the device?
6050
6029
  # if not, then we are done.
6051
- if (deviceActiveUser == self._SpotifyConnectUsername.lower()): # or (deviceActiveUser == self.UserProfile.Id.lower()):
6030
+ elif (deviceActiveUser == self._SpotifyConnectUsername.lower()): # or (deviceActiveUser == self.UserProfile.Id.lower()):
6052
6031
  _logsi.LogVerbose("Spotify Connect user context '%s' was verified for Device id '%s'; switch not necessary" % (deviceActiveUser, deviceIdResult))
6053
6032
  status = status + "user context switch not needed"
6054
6033
 
@@ -6060,28 +6039,29 @@ class SpotifyClient:
6060
6039
 
6061
6040
  else:
6062
6041
  zcfResult:ZeroconfResponse
6042
+
6043
+ # create a ZeroconfConnect object to access the device.
6044
+ zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpv4Address,
6045
+ discoverResult.HostIpPort,
6046
+ discoverResult.SpotifyConnectCPath,
6047
+ useSSL=False)
6063
6048
 
6064
6049
  # if a different user context has control of the device then we need to disconnect
6065
6050
  # the current user context before connecting a different user.
6066
6051
  if (info.HasActiveUser):
6067
6052
  _logsi.LogVerbose("Spotify Connect user context '%s' is being disconnected for Device id '%s'" % (deviceActiveUser, deviceIdResult))
6068
- zcfResult = zconn.Disconnect()
6053
+ zcfResult = zconn.Disconnect(delay)
6069
6054
  status = status + 'disconnected from user context "%s";' % deviceActiveUser
6070
-
6055
+
6071
6056
  # connect the device to OUR Spotify Connect user context.
6072
6057
  # note that the result here only indicates that the connect was submitted - NOT that it was successful!
6073
6058
  _logsi.LogVerbose("Spotify Connect user context '%s' is being connected for Device id '%s'" % (self._SpotifyConnectUsername, deviceIdResult))
6074
- zcfResult = zconn.Connect(self._SpotifyConnectUsername, self._SpotifyConnectPassword)
6059
+ zcfResult = zconn.Connect(self._SpotifyConnectUsername, self._SpotifyConnectPassword, delay)
6075
6060
  status = status + 'connected to user context "%s"' % self._SpotifyConnectUsername
6076
6061
 
6077
6062
  # trace.
6078
6063
  _logsi.LogVerbose("Spotify Connect user context switch from '%s' to '%s' was requested for Device id '%s'" % (deviceActiveUser, self._SpotifyConnectUsername, deviceIdResult))
6079
6064
 
6080
- # give spotify web api time to process the change and add the device to the device list.
6081
- if delay > 0:
6082
- _logsi.LogVerbose(TRACE_MSG_DELAY_DEVICE % delay)
6083
- time.sleep(delay)
6084
-
6085
6065
  # append device status to results.
6086
6066
  result = "%s%s\n" % (result, MSG_RESULT % (info.RemoteName, info.DeviceId, info.ActiveUser, status))
6087
6067
 
@@ -6236,6 +6216,11 @@ class SpotifyClient:
6236
6216
  SpotifApiError:
6237
6217
  If the method fails for any other reason.
6238
6218
 
6219
+ Note that this method will only return the devices for which the current user
6220
+ context has control of, or other available devices that do not have an active
6221
+ user context assigned. Use the `GetSpotifyConnectDevices` method to retrieve
6222
+ a complete list of devices that are available to all users.
6223
+
6239
6224
  The `ConfigurationCache` is updated with the results of this method. Use the
6240
6225
  `refresh` argument (with False value) to retrieve the cached value and avoid
6241
6226
  the spotify web api request. This results in better performance.
@@ -7894,7 +7879,7 @@ class SpotifyClient:
7894
7879
  self,
7895
7880
  ) -> SpotifyConnectDevices:
7896
7881
  """
7897
- Get information about all available Spotify Connect player devices.
7882
+ Get information about all available Spotify Connect player devices.
7898
7883
 
7899
7884
  Returns:
7900
7885
  A `SpotifyConnectDevices` object that contain the discovery results
@@ -7912,6 +7897,11 @@ class SpotifyClient:
7912
7897
  This method is similar to the `GetPlayerDevices` method, but it contains ALL
7913
7898
  available Spotify Connect devices that are known to the local network (versus
7914
7899
  just the devices known to a specific user).
7900
+
7901
+ It will also call the `GetPlayerDevices` method, in case we have any dynamic devices.
7902
+ Dynamic devices are Spotify Connect devices that are not found in Zeroconf discovery
7903
+ process, but still exist in the player device list. These are usually Spotify Connect
7904
+ web or mobile players with temporary device id's.
7915
7905
 
7916
7906
  <details>
7917
7907
  <summary>Sample Code</summary>
@@ -7927,7 +7917,6 @@ class SpotifyClient:
7927
7917
 
7928
7918
  # trace.
7929
7919
  _logsi.EnterMethod(SILevel.Debug, apiMethodName)
7930
- _logsi.LogVerbose("Get all available Spotify Connect devices")
7931
7920
 
7932
7921
  # discover Spotify Connect devices on the network, waiting up to the specified timeout
7933
7922
  # for all devices to be discovered. this will return all Spotify Connect devices that
@@ -7954,6 +7943,17 @@ class SpotifyClient:
7954
7943
  scDevice.DeviceInfo = info
7955
7944
  result.Items.append(scDevice)
7956
7945
 
7946
+ # we will also call the GetPlayerDevices method, in case we have any dynamic devices.
7947
+ # dynamic devices are Spotify Connect devices that are not found in Zeroconf discovery
7948
+ # process, but still exist in the player device list. these are usually Spotify Connect
7949
+ # web or mobile players with temporary device id's.
7950
+ devices:list[Device] = self.GetPlayerDevices(True)
7951
+
7952
+ device:Device
7953
+ for device in devices:
7954
+ if not result.ContainsDeviceId(device.Id):
7955
+ result.AddDynamicDevice(device, self.UserProfile.Id)
7956
+
7957
7957
  # trace.
7958
7958
  _logsi.LogArray(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(result).__name__), result)
7959
7959
  return result
@@ -9400,7 +9400,7 @@ class SpotifyClient:
9400
9400
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device pause playback", apiMethodParms)
9401
9401
 
9402
9402
  # validations.
9403
- delay = self._ValidateDelay(delay, 0.50, 10)
9403
+ delay = validateDelay(delay, 0.50, 10)
9404
9404
 
9405
9405
  # check for device name; convert to an id if a name was supplied.
9406
9406
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -9533,7 +9533,7 @@ class SpotifyClient:
9533
9533
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play context", apiMethodParms)
9534
9534
 
9535
9535
  # validations.
9536
- delay = self._ValidateDelay(delay, 0.50, 10)
9536
+ delay = validateDelay(delay, 0.50, 10)
9537
9537
 
9538
9538
  # check for device name; convert to an id if a name was supplied.
9539
9539
  #deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -9641,7 +9641,7 @@ class SpotifyClient:
9641
9641
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play track favorites", apiMethodParms)
9642
9642
 
9643
9643
  # validations.
9644
- delay = self._ValidateDelay(delay, 0.50, 10)
9644
+ delay = validateDelay(delay, 0.50, 10)
9645
9645
 
9646
9646
  # get current users favorite tracks.
9647
9647
  tracks:TrackPageSaved = self.GetTrackFavorites(limitTotal=200)
@@ -9752,7 +9752,7 @@ class SpotifyClient:
9752
9752
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play tracks", apiMethodParms)
9753
9753
 
9754
9754
  # validations.
9755
- delay = self._ValidateDelay(delay, 0.50, 10)
9755
+ delay = validateDelay(delay, 0.50, 10)
9756
9756
 
9757
9757
  # build a list of all item uri's.
9758
9758
  # remove any leading / trailing spaces in case user put a space between the items.
@@ -9863,7 +9863,7 @@ class SpotifyClient:
9863
9863
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device resume playback", apiMethodParms)
9864
9864
 
9865
9865
  # validations.
9866
- delay = self._ValidateDelay(delay, 0.50, 10)
9866
+ delay = validateDelay(delay, 0.50, 10)
9867
9867
 
9868
9868
  # check for device name; convert to an id if a name was supplied.
9869
9869
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -9960,7 +9960,7 @@ class SpotifyClient:
9960
9960
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device seek position", apiMethodParms)
9961
9961
 
9962
9962
  # validations.
9963
- delay = self._ValidateDelay(delay, 0.50, 10)
9963
+ delay = validateDelay(delay, 0.50, 10)
9964
9964
 
9965
9965
  # check for device name; convert to an id if a name was supplied.
9966
9966
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10052,7 +10052,7 @@ class SpotifyClient:
10052
10052
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device skip next", apiMethodParms)
10053
10053
 
10054
10054
  # validations.
10055
- delay = self._ValidateDelay(delay, 0.50, 10)
10055
+ delay = validateDelay(delay, 0.50, 10)
10056
10056
 
10057
10057
  # check for device name; convert to an id if a name was supplied.
10058
10058
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10141,7 +10141,7 @@ class SpotifyClient:
10141
10141
  _logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device skip previous", apiMethodParms)
10142
10142
 
10143
10143
  # validations.
10144
- delay = self._ValidateDelay(delay, 0.50, 10)
10144
+ delay = validateDelay(delay, 0.50, 10)
10145
10145
 
10146
10146
  # check for device name; convert to an id if a name was supplied.
10147
10147
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10184,6 +10184,7 @@ class SpotifyClient:
10184
10184
  deviceValue:str,
10185
10185
  verifyUserContext:bool=True,
10186
10186
  verifyTimeout:float=5.0,
10187
+ delay:float=0.25
10187
10188
  ) -> str:
10188
10189
  """
10189
10190
  Resolves a Spotify Connect device identifier from a specified device id, name, alias id,
@@ -10203,6 +10204,11 @@ class SpotifyClient:
10203
10204
  Connect device list. This value is only used if a Connect command has to be
10204
10205
  issued to activate the device.
10205
10206
  Default is 5; value range is 0 - 10.
10207
+ delay (float):
10208
+ Time delay (in seconds) to wait AFTER issuing any command to the device.
10209
+ This delay will give the spotify zeroconf api time to process the change before
10210
+ another command is issued.
10211
+ Default is 0.25; value range is 0 - 10.
10206
10212
 
10207
10213
  Returns:
10208
10214
  A device Id for the deviceValue if one could be resolved;
@@ -10251,6 +10257,7 @@ class SpotifyClient:
10251
10257
  apiMethodParms.AppendKeyValue("deviceValue", deviceValue)
10252
10258
  apiMethodParms.AppendKeyValue("verifyUserContext", verifyUserContext)
10253
10259
  apiMethodParms.AppendKeyValue("verifyTimeout", verifyTimeout)
10260
+ apiMethodParms.AppendKeyValue("delay", delay)
10254
10261
  _logsi.LogMethodParmList(SILevel.Verbose, "Resolving Spotify Connect Player Device Id", apiMethodParms)
10255
10262
 
10256
10263
  # if device value not specified then we are done.
@@ -10259,40 +10266,35 @@ class SpotifyClient:
10259
10266
  return None
10260
10267
 
10261
10268
  # validations.
10262
- verifyTimeout = self._ValidateDelay(verifyTimeout, 5, 10)
10269
+ delay = validateDelay(delay, 0.25, 10)
10270
+ verifyTimeout = validateDelay(verifyTimeout, 5, 10)
10263
10271
  if (not isinstance(verifyUserContext, bool)):
10264
10272
  verifyUserContext = True
10265
10273
 
10266
10274
  # determine if the device value specified is a name (e.g. "Bose-ST10-1") or
10267
10275
  # an id (e.g. "30fbc80e35598f3c242f2120413c943dfd9715fe").
10268
10276
  isDeviceId:bool = SpotifyClient.IsDeviceId(deviceValue)
10277
+
10269
10278
  deviceValueCompare:str = deviceValue.lower()
10270
10279
  deviceActiveUser:str = None
10271
-
10272
- # discover Spotify Connect devices on the network, waiting up to the specified timeout
10273
- # for all devices to be discovered. this will return all Spotify Connect devices that
10274
- # are currently powered on, and are registered to Zeroconf / mDNS.
10275
- _logsi.LogVerbose("Discovering Spotify Connect devices on the local network")
10276
- discovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
10277
- discovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
10280
+ discoverResult:ZeroconfDiscoveryResult
10281
+ info:ZeroconfGetInfo
10282
+ scDevice:SpotifyConnectDevice
10283
+
10284
+ # get all Spotify Connect available devices on the network.
10285
+ scDevices:SpotifyConnectDevices = self.GetSpotifyConnectDevices()
10278
10286
 
10279
10287
  # process all discovered devices.
10280
- discoverResult:ZeroconfDiscoveryResult
10281
- for discoverResult in discovery.DiscoveryResults:
10288
+ for scDevice in scDevices:
10282
10289
 
10283
- # get the id from the device via the zeroconf API getInfo endpoint, as the id is not
10284
- # returned in the zeroconf discovery result.
10285
- zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpv4Address,
10286
- discoverResult.HostIpPort,
10287
- discoverResult.SpotifyConnectCPath,
10288
- useSSL=False)
10289
- info:ZeroconfGetInfo = zconn.GetInformation()
10290
+ discoverResult = scDevice.DiscoveryResult
10291
+ info = scDevice.DeviceInfo
10290
10292
 
10291
10293
  # store the currently active user of the device, in case we need to switch users later on.
10292
10294
  deviceActiveUser = info.ActiveUser.lower()
10293
10295
 
10294
10296
  # are aliases being used (RemoteName is null if so)?
10295
- if info.RemoteName is None:
10297
+ if info.HasAliases:
10296
10298
 
10297
10299
  # if aliases are defined for the device, then we have to check each alias for a match.
10298
10300
  infoAlias:ZeroconfGetInfoAlias
@@ -10325,6 +10327,11 @@ class SpotifyClient:
10325
10327
  # did we resolve the device id?
10326
10328
  if deviceIdResult is not None:
10327
10329
 
10330
+ # is this a dynamic device? if so, then we can't control it.
10331
+ if (discoverResult.IsDynamicDevice):
10332
+ _logsi.LogVerbose("Spotify Connect Device '%s' (%s) is a dynamic device; we cannot control it" % (info.RemoteName, deviceIdResult))
10333
+ break
10334
+
10328
10335
  # did caller request user context switch bypass?
10329
10336
  # if so, then just return the device id.
10330
10337
  if (not verifyUserContext):
@@ -10345,11 +10352,17 @@ class SpotifyClient:
10345
10352
 
10346
10353
  zcfResult:ZeroconfResponse
10347
10354
 
10355
+ # create a ZeroconfConnect object to access the device.
10356
+ zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpv4Address,
10357
+ discoverResult.HostIpPort,
10358
+ discoverResult.SpotifyConnectCPath,
10359
+ useSSL=False)
10360
+
10348
10361
  # if a different user context has control of the device then we need to disconnect
10349
10362
  # the current user context before connecting a different user.
10350
10363
  if (info.HasActiveUser):
10351
10364
  _logsi.LogVerbose("Spotify Connect user context '%s' is being disconnected for Device id '%s'" % (deviceActiveUser, deviceIdResult))
10352
- zcfResult = zconn.Disconnect()
10365
+ zcfResult = zconn.Disconnect(delay)
10353
10366
 
10354
10367
  # connect the device to OUR Spotify Connect user context.
10355
10368
  # note that the result here only indicates that the connect was submitted - NOT that it was successful!
@@ -10463,7 +10476,7 @@ class SpotifyClient:
10463
10476
  _logsi.LogMethodParmList(SILevel.Verbose, "Set Spotify Connect device set repeat mode", apiMethodParms)
10464
10477
 
10465
10478
  # validations.
10466
- delay = self._ValidateDelay(delay, 0.50, 10)
10479
+ delay = validateDelay(delay, 0.50, 10)
10467
10480
 
10468
10481
  # check for device name; convert to an id if a name was supplied.
10469
10482
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10562,7 +10575,7 @@ class SpotifyClient:
10562
10575
  _logsi.LogMethodParmList(SILevel.Verbose, "Set Spotify Connect device set shuffle mode", apiMethodParms)
10563
10576
 
10564
10577
  # validations.
10565
- delay = self._ValidateDelay(delay, 0.50, 10)
10578
+ delay = validateDelay(delay, 0.50, 10)
10566
10579
 
10567
10580
  # check for device name; convert to an id if a name was supplied.
10568
10581
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10660,7 +10673,7 @@ class SpotifyClient:
10660
10673
  _logsi.LogMethodParmList(SILevel.Verbose, "Set Spotify Connect device set volume level", apiMethodParms)
10661
10674
 
10662
10675
  # validations.
10663
- delay = self._ValidateDelay(delay, 0.50, 10)
10676
+ delay = validateDelay(delay, 0.50, 10)
10664
10677
 
10665
10678
  # check for device name; convert to an id if a name was supplied.
10666
10679
  deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10758,7 +10771,7 @@ class SpotifyClient:
10758
10771
  _logsi.LogMethodParmList(SILevel.Verbose, "Transfer playback to a new Spotify Connect device", apiMethodParms)
10759
10772
 
10760
10773
  # validations.
10761
- delay = self._ValidateDelay(delay, 0.50, 10)
10774
+ delay = validateDelay(delay, 0.50, 10)
10762
10775
 
10763
10776
  # check for device name; convert to an id if a name was supplied.
10764
10777
  #deviceId = self.PlayerConvertDeviceNameToId(deviceId)
@@ -10864,7 +10877,7 @@ class SpotifyClient:
10864
10877
  _logsi.LogMethodParmList(SILevel.Verbose, "Checks if a Spotify Connect device is active, and activate one if not", apiMethodParms)
10865
10878
 
10866
10879
  # validations.
10867
- delay = self._ValidateDelay(delay, 0.50, 10)
10880
+ delay = validateDelay(delay, 0.50, 10)
10868
10881
 
10869
10882
  # get current Spotify Connect player state.
10870
10883
  result = self.GetPlayerPlaybackState()