spotifywebapiPython 1.0.42__py3-none-any.whl → 1.0.44__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.42.dist-info → spotifywebapiPython-1.0.44.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.42.dist-info → spotifywebapiPython-1.0.44.dist-info}/RECORD +33 -26
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +24 -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 +376 -36
- spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +45 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/__init__.py +14 -0
- spotifywebapipython/models/playlist.py +4 -1
- spotifywebapipython/models/playlistsimplified.py +4 -1
- spotifywebapipython/models/playlisttracksummary.py +3 -1
- spotifywebapipython/models/searchresponse.py +1 -1
- spotifywebapipython/models/zeroconfblobbuilder.py +144 -0
- spotifywebapipython/models/zeroconfdiscoveryresult.py +451 -0
- spotifywebapipython/models/zeroconfgetinfo.py +359 -0
- spotifywebapipython/models/zeroconfgetinfoalias.py +96 -0
- spotifywebapipython/models/zeroconfgetinfodrmmediaformat.py +90 -0
- spotifywebapipython/models/zeroconfproperty.py +93 -0
- spotifywebapipython/models/zeroconfresponse.py +108 -0
- spotifywebapipython/spotifyclient.py +484 -32
- spotifywebapipython/spotifydiscovery.py +98 -11
- {spotifywebapiPython-1.0.42.dist-info → spotifywebapiPython-1.0.44.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.42.dist-info → spotifywebapiPython-1.0.44.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.42.dist-info → spotifywebapiPython-1.0.44.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@ try:
|
|
|
3
3
|
from queue import Queue, Empty
|
|
4
4
|
except ImportError:
|
|
5
5
|
from Queue import Queue, Empty
|
|
6
|
+
from time import sleep
|
|
6
7
|
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo, ServiceStateChange, IPVersion
|
|
7
8
|
|
|
8
9
|
# our package imports.
|
|
@@ -10,6 +11,7 @@ from .saappmessages import SAAppMessages
|
|
|
10
11
|
from .sautils import export
|
|
11
12
|
from .models.device import Device
|
|
12
13
|
from .spotifyclient import SpotifyClient, SpotifyApiError
|
|
14
|
+
from .models import ZeroconfDiscoveryResult, ZeroconfProperty
|
|
13
15
|
|
|
14
16
|
# get smartinspect logger reference; create a new session for this module name.
|
|
15
17
|
from smartinspectpython.siauto import SIAuto, SILevel, SISession
|
|
@@ -63,6 +65,7 @@ class SpotifyDiscovery:
|
|
|
63
65
|
# initialize instance properties.
|
|
64
66
|
self._AreDevicesVerified:bool = areDevicesVerified
|
|
65
67
|
self._DiscoveredDeviceNames:dict = {}
|
|
68
|
+
self._DiscoveryResults:list[ZeroconfDiscoveryResult] = []
|
|
66
69
|
self._PrintToConsole:bool = printToConsole
|
|
67
70
|
self._SpotifyClient:SpotifyClient = spotifyClient
|
|
68
71
|
self._VerifiedDevices:dict = {}
|
|
@@ -119,6 +122,15 @@ class SpotifyDiscovery:
|
|
|
119
122
|
return self._DiscoveredDeviceNames
|
|
120
123
|
|
|
121
124
|
|
|
125
|
+
@property
|
|
126
|
+
def DiscoveryResults(self) -> list[ZeroconfDiscoveryResult]:
|
|
127
|
+
"""
|
|
128
|
+
An array of `ZeroconfDiscoveryResult` items that contain discovery details for
|
|
129
|
+
each service that was discovered.
|
|
130
|
+
"""
|
|
131
|
+
return self._DiscoveryResults
|
|
132
|
+
|
|
133
|
+
|
|
122
134
|
@property
|
|
123
135
|
def VerifiedDevices(self) -> dict:
|
|
124
136
|
"""
|
|
@@ -170,6 +182,23 @@ class SpotifyDiscovery:
|
|
|
170
182
|
# create device instances for each ipv4 address found.
|
|
171
183
|
for deviceIpAddress in ipAddressList:
|
|
172
184
|
|
|
185
|
+
# create new result instance.
|
|
186
|
+
result:ZeroconfDiscoveryResult = ZeroconfDiscoveryResult()
|
|
187
|
+
result.DeviceName = deviceName
|
|
188
|
+
result.Domain = '.local'
|
|
189
|
+
result.HostIpv4Address = deviceIpAddress
|
|
190
|
+
result.HostIpPort = serviceInfo.port
|
|
191
|
+
result.HostTTL = serviceInfo.host_ttl
|
|
192
|
+
result.Key = serviceInfo.key
|
|
193
|
+
result.Name = serviceInfo.name
|
|
194
|
+
result.OtherTTL = serviceInfo.other_ttl
|
|
195
|
+
result.Priority = serviceInfo.priority
|
|
196
|
+
result.Server = serviceInfo.server
|
|
197
|
+
result.ServerKey = serviceInfo.server_key
|
|
198
|
+
result.ServiceInfo = serviceInfo
|
|
199
|
+
result.ServiceType = serviceType
|
|
200
|
+
result.Weight = serviceInfo.weight
|
|
201
|
+
|
|
173
202
|
devicePort:int = serviceInfo.port
|
|
174
203
|
deviceKey:str = '%s:%i' % (deviceIpAddress, devicePort)
|
|
175
204
|
_logsi.LogVerbose("Discovered Spotify Connect device: %s - %s" % (deviceKey, deviceName))
|
|
@@ -177,20 +206,30 @@ class SpotifyDiscovery:
|
|
|
177
206
|
print("Discovered Spotify Connect device: %s - %s" % (deviceKey, deviceName))
|
|
178
207
|
_logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo: %s - %s" % (deviceKey, deviceName), serviceInfo)
|
|
179
208
|
|
|
180
|
-
#
|
|
181
|
-
#
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
209
|
+
# process service info propertys.
|
|
210
|
+
# note that the property keys and values must first be decoded to utf-8 encoding.
|
|
211
|
+
if serviceInfo.properties is not None:
|
|
212
|
+
dspProperties:dict = {}
|
|
213
|
+
for key, value in serviceInfo.properties.items():
|
|
214
|
+
keyStr = key.decode('utf-8')
|
|
215
|
+
valueStr = value.decode('utf-8')
|
|
216
|
+
result.Properties.append(ZeroconfProperty(keyStr, valueStr))
|
|
217
|
+
dspProperties[keyStr] = valueStr
|
|
218
|
+
# check for Spotify Connect common properties.
|
|
219
|
+
if keyStr.lower() == 'cpath':
|
|
220
|
+
result.SpotifyConnectCPath = valueStr
|
|
221
|
+
elif keyStr.lower() == 'version':
|
|
222
|
+
result.SpotifyConnectVersion = valueStr
|
|
223
|
+
_logsi.LogDictionary(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo.Properties: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), dspProperties)
|
|
188
224
|
|
|
189
225
|
# add the device name to the list (if not already added) using its
|
|
190
226
|
# ipv4 address and port number as the key.
|
|
191
227
|
if deviceKey not in self._DiscoveredDeviceNames.keys():
|
|
192
228
|
self._DiscoveredDeviceNames[deviceKey] = deviceName
|
|
193
229
|
|
|
230
|
+
# add the device result to the list.
|
|
231
|
+
self._DiscoveryResults.append(result)
|
|
232
|
+
|
|
194
233
|
# are we verifying devices connections? if so, then create a SpotifyClient
|
|
195
234
|
# object, which will verify the connection and gather basic capabilities of the device.
|
|
196
235
|
# we will also add the SpotifyClient instance using the same key as the device name list.
|
|
@@ -198,12 +237,15 @@ class SpotifyDiscovery:
|
|
|
198
237
|
|
|
199
238
|
if deviceKey not in self._VerifiedDevices.keys():
|
|
200
239
|
|
|
201
|
-
#device:SpotifyClient = SpotifyClient(host=deviceIpAddress, port=devicePort)
|
|
202
|
-
|
|
203
240
|
# get Spotify Connect player device by it's Name value.
|
|
204
241
|
device:Device = self._SpotifyClient.GetPlayerDevice(deviceName)
|
|
205
242
|
if device is not None:
|
|
206
243
|
self._VerifiedDevices[deviceKey] = device
|
|
244
|
+
result.SpotifyConnectIsInDeviceList = True
|
|
245
|
+
|
|
246
|
+
# trace.
|
|
247
|
+
_logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device result: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result, excludeNonPublic=True)
|
|
248
|
+
#_logsi.LogArray(SILevel.Verbose, "Discovered Spotify Connect device result.Properties: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result.Properties)
|
|
207
249
|
|
|
208
250
|
elif (serviceStateChange is ServiceStateChange.Removed):
|
|
209
251
|
_logsi.LogVerbose("Discovered Spotify Connect device removal (ignored): '%s' (%s:%s)", serviceName, serviceType, serviceStateChange)
|
|
@@ -258,6 +300,52 @@ class SpotifyDiscovery:
|
|
|
258
300
|
return self._DiscoveredDeviceNames
|
|
259
301
|
|
|
260
302
|
|
|
303
|
+
def BroadcastService(self, serviceInfo:ServiceInfo, port:int=5353) -> None:
|
|
304
|
+
"""
|
|
305
|
+
Generate a Zeroconf / mDNS broadcast to announce a service, and sends the broadcast out.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
serviceInfo (ServiceInfo):
|
|
309
|
+
Service information to broadcast.
|
|
310
|
+
port (int):
|
|
311
|
+
mDNS port number to send the broadcast on.
|
|
312
|
+
Default is 5353.
|
|
313
|
+
"""
|
|
314
|
+
try:
|
|
315
|
+
|
|
316
|
+
# create the zeroconf service.
|
|
317
|
+
zeroconf:Zeroconf = Zeroconf(ip_version=IPVersion.V4Only)
|
|
318
|
+
|
|
319
|
+
# # TEST TODO - hardcoded serviceinfo properties for testing.
|
|
320
|
+
# serviceType:str = '_spotify-connect._tcp.local.'
|
|
321
|
+
# serviceName:str = 'Bose-ST10-95._spotify-connect._tcp.local.'
|
|
322
|
+
# parsedAddresses:list[str] = ['192.168.1.82']
|
|
323
|
+
# port:int = 8200
|
|
324
|
+
# hostTTL:int = 120
|
|
325
|
+
# priority:int = 0
|
|
326
|
+
# otherTTL:int = 4500
|
|
327
|
+
# server:str = 'Bose-SM2-987bf3244-95.local.'
|
|
328
|
+
# weight:int = 0
|
|
329
|
+
# properties:dict = {b'CPath': b'/zc', b'VERSION': b'1.0'}
|
|
330
|
+
|
|
331
|
+
# # create the zeroconf serviceinfo object.
|
|
332
|
+
# zcfServiceInfo:ServiceInfo = ServiceInfo(serviceType, serviceName, port, weight, priority, properties, server, hostTTL, otherTTL, parsed_addresses=parsedAddresses)
|
|
333
|
+
|
|
334
|
+
# generate a broadcast packet to announce the service.
|
|
335
|
+
_logsi.LogVerbose("Generating Zeroconf Service Broadcast for service: '%s'" % serviceInfo.name)
|
|
336
|
+
dnsOutgoing = zeroconf.generate_service_broadcast(serviceInfo, serviceInfo.host_ttl, True)
|
|
337
|
+
|
|
338
|
+
# send the broadcast packet.
|
|
339
|
+
_logsi.LogVerbose("Sending Zeroconf Service Broadcast for service: '%s'" % serviceInfo.name)
|
|
340
|
+
zeroconf.send(dnsOutgoing, addr=None, port=port)
|
|
341
|
+
|
|
342
|
+
except Exception as ex:
|
|
343
|
+
|
|
344
|
+
# this is not really an exception, but more of an indicator that
|
|
345
|
+
# the timeout has been reached.
|
|
346
|
+
_logsi.LogException("Zeroconf Broadcast Service exception: %s" % str(ex), ex, logToSystemLogger=False)
|
|
347
|
+
|
|
348
|
+
|
|
261
349
|
def ToString(self, includeItems:bool=False) -> str:
|
|
262
350
|
"""
|
|
263
351
|
Returns a displayable string representation of the class.
|
|
@@ -273,7 +361,6 @@ class SpotifyDiscovery:
|
|
|
273
361
|
if includeItems == True:
|
|
274
362
|
|
|
275
363
|
for key, deviceName in self._DiscoveredDeviceNames.items():
|
|
276
|
-
|
|
277
364
|
device:Device
|
|
278
365
|
verifiedStatus:str = ''
|
|
279
366
|
device = self._VerifiedDevices.get(key, None)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|