spotifywebapiPython 1.0.58__py3-none-any.whl → 1.0.60__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.58.dist-info → spotifywebapiPython-1.0.60.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.58.dist-info → spotifywebapiPython-1.0.60.dist-info}/RECORD +24 -24
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +18 -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 +16 -7
- spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +5 -5
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/spotifyconnectdevices.py +2 -1
- spotifywebapipython/models/zeroconfdiscoveryresult.py +36 -12
- spotifywebapipython/spotifyclient.py +90 -43
- spotifywebapipython/spotifydiscovery.py +52 -56
- spotifywebapipython/zeroconfapi/zeroconfconnect.py +38 -50
- {spotifywebapiPython-1.0.58.dist-info → spotifywebapiPython-1.0.60.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.58.dist-info → spotifywebapiPython-1.0.60.dist-info}/WHEEL +0 -0
- {spotifywebapiPython-1.0.58.dist-info → spotifywebapiPython-1.0.60.dist-info}/top_level.txt +0 -0
|
@@ -5955,7 +5955,8 @@ class SpotifyClient:
|
|
|
5955
5955
|
to the local network. A Spotify Zeroconf API `getInfo` call is then issued for each device
|
|
5956
5956
|
that was found to retrieve device information. A Spotify Zeroconf API `addUser` call is also
|
|
5957
5957
|
issued for each device that does not have an active user context established, OR if the currently
|
|
5958
|
-
active user context does not match the `SpotifyConnectUsername` argument specified on the class
|
|
5958
|
+
active user context does not match the `SpotifyConnectUsername` argument specified on the class
|
|
5959
|
+
constructor and the `verifyUserContext` argument is True.
|
|
5959
5960
|
|
|
5960
5961
|
The user context switch is bypassed if the `verifyUserContext` argument is False. If the user context
|
|
5961
5962
|
is to be switched, then a Disconnect will be issued if a user context is active on the device followed
|
|
@@ -6042,7 +6043,10 @@ class SpotifyClient:
|
|
|
6042
6043
|
zcfResult:ZeroconfResponse
|
|
6043
6044
|
|
|
6044
6045
|
# create a ZeroconfConnect object to access the device.
|
|
6045
|
-
|
|
6046
|
+
# note that we are using the "HostIpAddress" property value here, with "Server" as a fallback.
|
|
6047
|
+
# the "Server" property is an alias, which must be resolved via a DNS lookup under
|
|
6048
|
+
# the covers and adds a significant delay (2-3 seconds!) to the activation time.
|
|
6049
|
+
zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpAddress,
|
|
6046
6050
|
discoverResult.HostIpPort,
|
|
6047
6051
|
discoverResult.SpotifyConnectCPath,
|
|
6048
6052
|
useSSL=False)
|
|
@@ -7953,7 +7957,10 @@ class SpotifyClient:
|
|
|
7953
7957
|
|
|
7954
7958
|
# get the id from the device via the zeroconf API getInfo endpoint, as the id is not
|
|
7955
7959
|
# returned in the zeroconf discovery result.
|
|
7956
|
-
|
|
7960
|
+
# note that we are using the "HostIpAddress" property value here, with "Server" as a fallback.
|
|
7961
|
+
# the "Server" property is an alias, which must be resolved via a DNS lookup under
|
|
7962
|
+
# the covers and adds a significant delay (2-3 seconds!) to the activation time.
|
|
7963
|
+
zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpAddress,
|
|
7957
7964
|
discoverResult.HostIpPort,
|
|
7958
7965
|
discoverResult.SpotifyConnectCPath,
|
|
7959
7966
|
useSSL=False)
|
|
@@ -7967,7 +7974,7 @@ class SpotifyClient:
|
|
|
7967
7974
|
|
|
7968
7975
|
# we will also call the GetPlayerDevices method, in case we have any dynamic devices.
|
|
7969
7976
|
# dynamic devices are Spotify Connect devices that are not found in Zeroconf discovery
|
|
7970
|
-
# process, but
|
|
7977
|
+
# process, but exist in the player device list. these are usually Spotify Connect
|
|
7971
7978
|
# web or mobile players with temporary device id's.
|
|
7972
7979
|
devices:list[Device] = self.GetPlayerDevices(True)
|
|
7973
7980
|
|
|
@@ -9467,14 +9474,16 @@ class SpotifyClient:
|
|
|
9467
9474
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
9468
9475
|
|
|
9469
9476
|
|
|
9470
|
-
def PlayerMediaPlayContext(
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9477
|
+
def PlayerMediaPlayContext(
|
|
9478
|
+
self,
|
|
9479
|
+
contextUri:str,
|
|
9480
|
+
offsetUri:str=None,
|
|
9481
|
+
offsetPosition:int=None,
|
|
9482
|
+
positionMS:int=0,
|
|
9483
|
+
deviceId:str=None,
|
|
9484
|
+
delay:float=0.50,
|
|
9485
|
+
resolveDeviceId:bool=True,
|
|
9486
|
+
) -> None:
|
|
9478
9487
|
"""
|
|
9479
9488
|
Start playing one or more tracks of the specified context on a Spotify Connect device.
|
|
9480
9489
|
|
|
@@ -9513,6 +9522,9 @@ class SpotifyClient:
|
|
|
9513
9522
|
This delay will give the spotify web api time to process the change before
|
|
9514
9523
|
another command is issued.
|
|
9515
9524
|
Default is 0.50; value range is 0 - 10.
|
|
9525
|
+
resolveDeviceId (bool):
|
|
9526
|
+
True to resolve the supplied `deviceId` value; otherwise, False not resolve the `deviceId`
|
|
9527
|
+
value as it has already been resolved.
|
|
9516
9528
|
|
|
9517
9529
|
Raises:
|
|
9518
9530
|
SpotifyWebApiError:
|
|
@@ -9559,14 +9571,18 @@ class SpotifyClient:
|
|
|
9559
9571
|
apiMethodParms.AppendKeyValue("positionMS", positionMS)
|
|
9560
9572
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9561
9573
|
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9574
|
+
apiMethodParms.AppendKeyValue("resolveDeviceId", resolveDeviceId)
|
|
9562
9575
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play context", apiMethodParms)
|
|
9563
9576
|
|
|
9564
9577
|
# validations.
|
|
9565
9578
|
delay = validateDelay(delay, 0.50, 10)
|
|
9566
9579
|
|
|
9567
|
-
#
|
|
9568
|
-
|
|
9569
|
-
|
|
9580
|
+
# has the device id been resolved?
|
|
9581
|
+
if (resolveDeviceId):
|
|
9582
|
+
|
|
9583
|
+
# ensure the specified device id / name is active and available, and
|
|
9584
|
+
# the user context is set correctly.
|
|
9585
|
+
deviceId = self.PlayerResolveDeviceId(deviceId, verifyUserContext=True)
|
|
9570
9586
|
|
|
9571
9587
|
# build spotify web api request parameters.
|
|
9572
9588
|
reqData:dict = \
|
|
@@ -9613,11 +9629,13 @@ class SpotifyClient:
|
|
|
9613
9629
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
9614
9630
|
|
|
9615
9631
|
|
|
9616
|
-
def PlayerMediaPlayTrackFavorites(
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9632
|
+
def PlayerMediaPlayTrackFavorites(
|
|
9633
|
+
self,
|
|
9634
|
+
deviceId:str=None,
|
|
9635
|
+
shuffle:bool=True,
|
|
9636
|
+
delay:float=0.50,
|
|
9637
|
+
resolveDeviceId:bool=True,
|
|
9638
|
+
) -> None:
|
|
9621
9639
|
"""
|
|
9622
9640
|
Get a list of the tracks saved in the current Spotify user's 'Your Library'
|
|
9623
9641
|
and starts playing them.
|
|
@@ -9635,6 +9653,9 @@ class SpotifyClient:
|
|
|
9635
9653
|
This delay will give the spotify web api time to process the change before
|
|
9636
9654
|
another command is issued.
|
|
9637
9655
|
Default is 0.50; value range is 0 - 10.
|
|
9656
|
+
resolveDeviceId (bool):
|
|
9657
|
+
True to resolve the supplied `deviceId` value; otherwise, False not resolve the `deviceId`
|
|
9658
|
+
value as it has already been resolved.
|
|
9638
9659
|
|
|
9639
9660
|
Raises:
|
|
9640
9661
|
SpotifyWebApiError:
|
|
@@ -9665,6 +9686,7 @@ class SpotifyClient:
|
|
|
9665
9686
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9666
9687
|
apiMethodParms.AppendKeyValue("shuffle", shuffle)
|
|
9667
9688
|
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9689
|
+
apiMethodParms.AppendKeyValue("resolveDeviceId", resolveDeviceId)
|
|
9668
9690
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play track favorites", apiMethodParms)
|
|
9669
9691
|
|
|
9670
9692
|
# validations.
|
|
@@ -9682,15 +9704,19 @@ class SpotifyClient:
|
|
|
9682
9704
|
for trackSaved in tracks.Items:
|
|
9683
9705
|
arrUris.append(trackSaved.Track.Uri)
|
|
9684
9706
|
|
|
9685
|
-
#
|
|
9686
|
-
|
|
9687
|
-
|
|
9707
|
+
# has the device id been resolved?
|
|
9708
|
+
if (resolveDeviceId):
|
|
9709
|
+
|
|
9710
|
+
# ensure the specified device id / name is active and available, and
|
|
9711
|
+
# the user context is set correctly.
|
|
9712
|
+
deviceId = self.PlayerResolveDeviceId(deviceId, verifyUserContext=True)
|
|
9688
9713
|
|
|
9689
9714
|
# set desired shuffle mode.
|
|
9690
9715
|
self.PlayerSetShuffleMode(shuffle, deviceId, delay)
|
|
9691
9716
|
|
|
9692
9717
|
# play the tracks.
|
|
9693
|
-
|
|
9718
|
+
# indicate device id has already been resolved.
|
|
9719
|
+
self.PlayerMediaPlayTracks(arrUris, deviceId=deviceId, delay=delay, resolveDeviceId=False)
|
|
9694
9720
|
|
|
9695
9721
|
# process results.
|
|
9696
9722
|
# no results to process - this is pass or fail.
|
|
@@ -9710,12 +9736,14 @@ class SpotifyClient:
|
|
|
9710
9736
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
9711
9737
|
|
|
9712
9738
|
|
|
9713
|
-
def PlayerMediaPlayTracks(
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9739
|
+
def PlayerMediaPlayTracks(
|
|
9740
|
+
self,
|
|
9741
|
+
uris:list[str],
|
|
9742
|
+
positionMS:int=0,
|
|
9743
|
+
deviceId:str=None,
|
|
9744
|
+
delay:float=0.50,
|
|
9745
|
+
resolveDeviceId:bool=True,
|
|
9746
|
+
) -> None:
|
|
9719
9747
|
"""
|
|
9720
9748
|
Start playing one or more tracks on the specified Spotify Connect device.
|
|
9721
9749
|
|
|
@@ -9744,6 +9772,9 @@ class SpotifyClient:
|
|
|
9744
9772
|
This delay will give the spotify web api time to process the change before
|
|
9745
9773
|
another command is issued.
|
|
9746
9774
|
Default is 0.50; value range is 0 - 10.
|
|
9775
|
+
resolveDeviceId (bool):
|
|
9776
|
+
True to resolve the supplied `deviceId` value; otherwise, False not resolve the `deviceId`
|
|
9777
|
+
value as it has already been resolved.
|
|
9747
9778
|
|
|
9748
9779
|
Raises:
|
|
9749
9780
|
SpotifyWebApiError:
|
|
@@ -9774,6 +9805,7 @@ class SpotifyClient:
|
|
|
9774
9805
|
apiMethodParms.AppendKeyValue("positionMS", positionMS)
|
|
9775
9806
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
9776
9807
|
apiMethodParms.AppendKeyValue("delay", delay)
|
|
9808
|
+
apiMethodParms.AppendKeyValue("resolveDeviceId", resolveDeviceId)
|
|
9777
9809
|
_logsi.LogMethodParmList(SILevel.Verbose, "Spotify Connect device play tracks", apiMethodParms)
|
|
9778
9810
|
|
|
9779
9811
|
# validations.
|
|
@@ -9788,10 +9820,13 @@ class SpotifyClient:
|
|
|
9788
9820
|
arrUris = uris.split(',')
|
|
9789
9821
|
for idx in range(0, len(arrUris)):
|
|
9790
9822
|
arrUris[idx] = arrUris[idx].strip()
|
|
9823
|
+
|
|
9824
|
+
# has the device id been resolved?
|
|
9825
|
+
if (resolveDeviceId):
|
|
9791
9826
|
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9827
|
+
# ensure the specified device id / name is active and available, and
|
|
9828
|
+
# the user context is set correctly.
|
|
9829
|
+
deviceId = self.PlayerResolveDeviceId(deviceId, verifyUserContext=True)
|
|
9795
9830
|
|
|
9796
9831
|
# build spotify web api request parameters.
|
|
9797
9832
|
reqData:dict = \
|
|
@@ -10378,7 +10413,10 @@ class SpotifyClient:
|
|
|
10378
10413
|
zcfResult:ZeroconfResponse
|
|
10379
10414
|
|
|
10380
10415
|
# create a ZeroconfConnect object to access the device.
|
|
10381
|
-
|
|
10416
|
+
# note that we are using the "HostIpAddress" property value here, with "Server" as a fallback.
|
|
10417
|
+
# the "Server" property is an alias, which must be resolved via a DNS lookup under
|
|
10418
|
+
# the covers and adds a significant delay (2-3 seconds!) to the activation time.
|
|
10419
|
+
zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpAddress,
|
|
10382
10420
|
discoverResult.HostIpPort,
|
|
10383
10421
|
discoverResult.SpotifyConnectCPath,
|
|
10384
10422
|
useSSL=False)
|
|
@@ -10424,7 +10462,7 @@ class SpotifyClient:
|
|
|
10424
10462
|
|
|
10425
10463
|
# did we resolve the device id?
|
|
10426
10464
|
if deviceIdResult is None:
|
|
10427
|
-
_logsi.LogWarning("Spotify Connect
|
|
10465
|
+
_logsi.LogWarning("Spotify Connect device name '%s' could not be resolved; please ensure that the specified device is powered on and available on the local network" % (deviceValue))
|
|
10428
10466
|
|
|
10429
10467
|
# return the resolved device id.
|
|
10430
10468
|
return deviceIdResult
|
|
@@ -10739,11 +10777,13 @@ class SpotifyClient:
|
|
|
10739
10777
|
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)
|
|
10740
10778
|
|
|
10741
10779
|
|
|
10742
|
-
def PlayerTransferPlayback(
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10780
|
+
def PlayerTransferPlayback(
|
|
10781
|
+
self,
|
|
10782
|
+
deviceId:str=None,
|
|
10783
|
+
play:bool=True,
|
|
10784
|
+
delay:float=0.50,
|
|
10785
|
+
resolveDeviceId:bool=True,
|
|
10786
|
+
) -> None:
|
|
10747
10787
|
"""
|
|
10748
10788
|
Transfer playback to a new Spotify Connect device and optionally begin playback.
|
|
10749
10789
|
|
|
@@ -10764,6 +10804,9 @@ class SpotifyClient:
|
|
|
10764
10804
|
This delay will give the spotify web api time to process the change before
|
|
10765
10805
|
another command is issued.
|
|
10766
10806
|
Default is 0.50; value range is 0 - 10.
|
|
10807
|
+
resolveDeviceId (bool):
|
|
10808
|
+
True to resolve the supplied `deviceId` value; otherwise, False not resolve the `deviceId`
|
|
10809
|
+
value as it has already been resolved.
|
|
10767
10810
|
|
|
10768
10811
|
Raises:
|
|
10769
10812
|
SpotifyWebApiError:
|
|
@@ -10793,14 +10836,18 @@ class SpotifyClient:
|
|
|
10793
10836
|
apiMethodParms.AppendKeyValue("deviceId", deviceId)
|
|
10794
10837
|
apiMethodParms.AppendKeyValue("play", play)
|
|
10795
10838
|
apiMethodParms.AppendKeyValue("delay", delay)
|
|
10839
|
+
apiMethodParms.AppendKeyValue("resolveDeviceId", resolveDeviceId)
|
|
10796
10840
|
_logsi.LogMethodParmList(SILevel.Verbose, "Transfer playback to a new Spotify Connect device", apiMethodParms)
|
|
10797
10841
|
|
|
10798
10842
|
# validations.
|
|
10799
10843
|
delay = validateDelay(delay, 0.50, 10)
|
|
10800
10844
|
|
|
10801
|
-
#
|
|
10802
|
-
|
|
10803
|
-
|
|
10845
|
+
# has the device id been resolved?
|
|
10846
|
+
if (resolveDeviceId):
|
|
10847
|
+
|
|
10848
|
+
# ensure the specified device id / name is active and available, and
|
|
10849
|
+
# the user context is set correctly.
|
|
10850
|
+
deviceId = self.PlayerResolveDeviceId(deviceId, verifyUserContext=True)
|
|
10804
10851
|
|
|
10805
10852
|
# build spotify web api request parameters.
|
|
10806
10853
|
reqData:dict = \
|
|
@@ -94,9 +94,9 @@ class SpotifyDiscovery:
|
|
|
94
94
|
"""
|
|
95
95
|
A dictionary of discovered device names that were detected by the discovery process.
|
|
96
96
|
|
|
97
|
-
Dictionary keys will be in the form of "address:port", where "
|
|
98
|
-
|
|
99
|
-
is listening on.
|
|
97
|
+
Dictionary keys will be in the form of "'name' (address:port)", where "name" is the
|
|
98
|
+
device name, "address" is the device server address and the "port" is the ipv4 port
|
|
99
|
+
number the Spotify Connect device is listening on.
|
|
100
100
|
|
|
101
101
|
Dictionary values will be the device names (e.g. "Web Player (Chrome)", etc.). This SHOULD
|
|
102
102
|
match the name of the device as displayed in the Spotify App, but is not guaranteed.
|
|
@@ -152,60 +152,56 @@ class SpotifyDiscovery:
|
|
|
152
152
|
if (ipAddressList is None):
|
|
153
153
|
return
|
|
154
154
|
|
|
155
|
-
# create
|
|
156
|
-
|
|
155
|
+
# create new result instance.
|
|
156
|
+
result:ZeroconfDiscoveryResult = ZeroconfDiscoveryResult()
|
|
157
|
+
result.DeviceName = deviceName
|
|
158
|
+
result.Domain = '.local'
|
|
159
|
+
for item in ipAddressList:
|
|
160
|
+
result.HostIpAddresses.append(item)
|
|
161
|
+
result.HostIpPort = serviceInfo.port
|
|
162
|
+
result.HostTTL = serviceInfo.host_ttl
|
|
163
|
+
result.Key = serviceInfo.key
|
|
164
|
+
result.Name = serviceInfo.name
|
|
165
|
+
result.OtherTTL = serviceInfo.other_ttl
|
|
166
|
+
result.Priority = serviceInfo.priority
|
|
167
|
+
result.Server = serviceInfo.server
|
|
168
|
+
result.ServerKey = serviceInfo.server_key
|
|
169
|
+
result.ServiceInfo = serviceInfo
|
|
170
|
+
result.ServiceType = serviceType
|
|
171
|
+
result.Weight = serviceInfo.weight
|
|
157
172
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
result.
|
|
163
|
-
|
|
164
|
-
result.HostTTL = serviceInfo.host_ttl
|
|
165
|
-
result.Key = serviceInfo.key
|
|
166
|
-
result.Name = serviceInfo.name
|
|
167
|
-
result.OtherTTL = serviceInfo.other_ttl
|
|
168
|
-
result.Priority = serviceInfo.priority
|
|
169
|
-
result.Server = serviceInfo.server
|
|
170
|
-
result.ServerKey = serviceInfo.server_key
|
|
171
|
-
result.ServiceInfo = serviceInfo
|
|
172
|
-
result.ServiceType = serviceType
|
|
173
|
-
result.Weight = serviceInfo.weight
|
|
173
|
+
# formulate a unique key for this device.
|
|
174
|
+
deviceKey:str = "'%s' (%s:%i)" % (deviceName, result.HostIpAddress, result.HostIpPort)
|
|
175
|
+
_logsi.LogVerbose("Discovered Spotify Connect device: %s %s" % (deviceKey, result.HostIpAddresses))
|
|
176
|
+
if (self._PrintToConsole == True):
|
|
177
|
+
print("Discovered Spotify Connect device: %s [%s]" % (deviceKey, result.HostIpAddresses))
|
|
178
|
+
_logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo: %s" % (deviceKey), serviceInfo)
|
|
174
179
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if deviceKey not in self._DiscoveredDeviceNames.keys():
|
|
201
|
-
self._DiscoveredDeviceNames[deviceKey] = deviceName
|
|
202
|
-
|
|
203
|
-
# add the device result to the list.
|
|
204
|
-
self._DiscoveryResults.append(result)
|
|
205
|
-
|
|
206
|
-
# trace.
|
|
207
|
-
_logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device result: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result, excludeNonPublic=True)
|
|
208
|
-
#_logsi.LogArray(SILevel.Verbose, "Discovered Spotify Connect device result.Properties: '%s' (%s:%i)" % (deviceName, deviceIpAddress, devicePort), result.Properties)
|
|
180
|
+
# process service info propertys.
|
|
181
|
+
# note that the property keys and values must first be decoded to utf-8 encoding.
|
|
182
|
+
if serviceInfo.properties is not None:
|
|
183
|
+
dspProperties:dict = {}
|
|
184
|
+
for key, value in serviceInfo.properties.items():
|
|
185
|
+
keyStr = key.decode('utf-8')
|
|
186
|
+
valueStr = value.decode('utf-8')
|
|
187
|
+
result.Properties.append(ZeroconfProperty(keyStr, valueStr))
|
|
188
|
+
dspProperties[keyStr] = valueStr
|
|
189
|
+
# check for Spotify Connect common properties.
|
|
190
|
+
if keyStr.lower() == 'cpath':
|
|
191
|
+
result.SpotifyConnectCPath = valueStr
|
|
192
|
+
elif keyStr.lower() == 'version':
|
|
193
|
+
result.SpotifyConnectVersion = valueStr
|
|
194
|
+
_logsi.LogDictionary(SILevel.Verbose, "Discovered Spotify Connect device ServiceInfo.Properties: %s" % (deviceKey), dspProperties)
|
|
195
|
+
|
|
196
|
+
# add the device name to the list (if not already added).
|
|
197
|
+
if deviceKey not in self._DiscoveredDeviceNames.keys():
|
|
198
|
+
self._DiscoveredDeviceNames[deviceKey] = deviceName
|
|
199
|
+
|
|
200
|
+
# add the device result to the list.
|
|
201
|
+
self._DiscoveryResults.append(result)
|
|
202
|
+
|
|
203
|
+
# trace.
|
|
204
|
+
_logsi.LogObject(SILevel.Verbose, "Discovered Spotify Connect device result: %s" % (deviceKey), result, excludeNonPublic=True)
|
|
209
205
|
|
|
210
206
|
elif (serviceStateChange is ServiceStateChange.Removed):
|
|
211
207
|
_logsi.LogVerbose("Discovered Spotify Connect device removal (ignored): '%s' (%s:%s)", serviceName, serviceType, serviceStateChange)
|
|
@@ -272,6 +268,6 @@ class SpotifyDiscovery:
|
|
|
272
268
|
if includeItems == True:
|
|
273
269
|
|
|
274
270
|
for key, deviceName in self._DiscoveredDeviceNames.items():
|
|
275
|
-
msg = "%s\n- %s
|
|
271
|
+
msg = "%s\n- %s" % (msg, key)
|
|
276
272
|
|
|
277
273
|
return msg
|
|
@@ -14,6 +14,7 @@ from .zeroconfgetinfo import ZeroconfGetInfo
|
|
|
14
14
|
from ..saappmessages import SAAppMessages
|
|
15
15
|
from ..sautils import export, validateDelay
|
|
16
16
|
from ..spotifyapierror import SpotifyApiError
|
|
17
|
+
from ..spotifywebapierror import SpotifyWebApiError
|
|
17
18
|
from ..const import (
|
|
18
19
|
TRACE_MSG_DELAY_DEVICE,
|
|
19
20
|
)
|
|
@@ -41,8 +42,8 @@ class ZeroconfConnect:
|
|
|
41
42
|
"""
|
|
42
43
|
|
|
43
44
|
def __init__(self,
|
|
44
|
-
|
|
45
|
-
hostIpPort:
|
|
45
|
+
hostIpAddress:str,
|
|
46
|
+
hostIpPort:int,
|
|
46
47
|
cpath:str,
|
|
47
48
|
version:str='1.0',
|
|
48
49
|
useSSL:bool=False
|
|
@@ -51,9 +52,9 @@ class ZeroconfConnect:
|
|
|
51
52
|
Initializes a new instance of the class.
|
|
52
53
|
|
|
53
54
|
Args:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
on the Spotify Connect device (e.g. "192.168.1.81").
|
|
55
|
+
hostIpAddress (str):
|
|
56
|
+
IP address or alias at which the Spotify Connect Zeroconf API can be reached
|
|
57
|
+
on the Spotify Connect device (e.g. "192.168.1.81", "Bose-SM2-341513fbeeae.local.", etc).
|
|
57
58
|
hostIpPort (int):
|
|
58
59
|
Port number (as an integer) at which the Spotify Connect Zeroconf API can be reached
|
|
59
60
|
on the Spotify Connect device (e.g. "8200").
|
|
@@ -80,7 +81,7 @@ class ZeroconfConnect:
|
|
|
80
81
|
# initialize storage.
|
|
81
82
|
self._CPath:str = cpath
|
|
82
83
|
self._HostIpPort:int = hostIpPort
|
|
83
|
-
self.
|
|
84
|
+
self._HostIpAddress:str = hostIpAddress
|
|
84
85
|
self._UseSSL:bool = useSSL
|
|
85
86
|
self._Version:str = version
|
|
86
87
|
|
|
@@ -97,12 +98,12 @@ class ZeroconfConnect:
|
|
|
97
98
|
|
|
98
99
|
|
|
99
100
|
@property
|
|
100
|
-
def
|
|
101
|
+
def HostIpAddress(self) -> str:
|
|
101
102
|
"""
|
|
102
|
-
|
|
103
|
-
on the Spotify Connect device (e.g. "192.168.1.81").
|
|
103
|
+
IP address or alias at which the Spotify Connect Zeroconf API can be reached
|
|
104
|
+
on the Spotify Connect device (e.g. "192.168.1.81", "Bose-SM2-341513fbeeae.local.", etc).
|
|
104
105
|
"""
|
|
105
|
-
return self.
|
|
106
|
+
return self._HostIpAddress
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
@property
|
|
@@ -191,43 +192,30 @@ class ZeroconfConnect:
|
|
|
191
192
|
if (response.headers):
|
|
192
193
|
_logsi.LogCollection(SILevel.Debug, "ZeroconfConnect http response [%s-%s]: '%s' (headers)" % (response.status_code, response.reason, responseUrl), response.headers.items())
|
|
193
194
|
|
|
194
|
-
|
|
195
|
+
# do we have response data?
|
|
196
|
+
if (response.content is not None) and (len(response.content) > 0):
|
|
195
197
|
|
|
196
|
-
#
|
|
197
|
-
#
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
# do we have response data?
|
|
203
|
-
if len(response.content) == 0:
|
|
204
|
-
|
|
205
|
-
# some requests will not return a response, which is ok.
|
|
206
|
-
responseData = None
|
|
207
|
-
_logsi.LogVerbose("ZeroconfConnect http response [%s-%s]: '%s' (no data)" % (response.status_code, response.reason, responseUrl))
|
|
208
|
-
|
|
209
|
-
elif (contentType is not None) and (contentType.find('json') > -1):
|
|
210
|
-
|
|
211
|
-
# response is json.
|
|
212
|
-
# do not use the "response.json()" method to parse JSON responses, as it is unreliable!
|
|
213
|
-
data = response.content.decode('utf-8')
|
|
214
|
-
responseData = json.loads(data)
|
|
215
|
-
|
|
216
|
-
if _logsi.IsOn(SILevel.Verbose):
|
|
217
|
-
if isinstance(responseData, dict):
|
|
218
|
-
_logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json dict)" % (response.status_code, response.reason, responseUrl), responseData)
|
|
219
|
-
elif isinstance(responseData, list):
|
|
220
|
-
_logsi.LogArray(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json array)" % (response.status_code, response.reason, responseUrl), responseData)
|
|
221
|
-
else:
|
|
222
|
-
_logsi.LogObject(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json object)" % (response.status_code, response.reason, responseUrl), responseData)
|
|
223
|
-
|
|
224
|
-
else:
|
|
198
|
+
# convert response to JSON object.
|
|
199
|
+
# ALL Spotify Connect Zeroconf API responses SHOULD be JSON format!
|
|
200
|
+
# do not use the "response.json()" method to parse JSON responses, as it is unreliable!
|
|
201
|
+
data = response.content.decode('utf-8')
|
|
202
|
+
responseData = json.loads(data)
|
|
225
203
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
responseData
|
|
230
|
-
|
|
204
|
+
if _logsi.IsOn(SILevel.Verbose):
|
|
205
|
+
if isinstance(responseData, dict):
|
|
206
|
+
_logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json dict)" % (response.status_code, response.reason, responseUrl), responseData)
|
|
207
|
+
elif isinstance(responseData, list):
|
|
208
|
+
_logsi.LogArray(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json array)" % (response.status_code, response.reason, responseUrl), responseData)
|
|
209
|
+
else:
|
|
210
|
+
_logsi.LogObject(SILevel.Verbose, "ZeroconfConnect http response [%s-%s]: '%s' (json object)" % (response.status_code, response.reason, responseUrl), responseData)
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
else:
|
|
214
|
+
|
|
215
|
+
# raise an exception, as we are expecting a JSON-formatted response!
|
|
216
|
+
errMessage = response.content.decode('utf-8')
|
|
217
|
+
raise SpotifyWebApiError(response.status_code, errMessage, methodName, response.reason, _logsi)
|
|
218
|
+
|
|
231
219
|
except SpotifyZeroconfApiError: raise # pass handled exceptions on thru
|
|
232
220
|
except SpotifyApiError: raise # pass handled exceptions on thru
|
|
233
221
|
except Exception as ex:
|
|
@@ -325,7 +313,7 @@ class ZeroconfConnect:
|
|
|
325
313
|
|
|
326
314
|
# trace.
|
|
327
315
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
328
|
-
apiMethodParms.AppendKeyValue("
|
|
316
|
+
apiMethodParms.AppendKeyValue("HostIpAddress", self._HostIpAddress)
|
|
329
317
|
apiMethodParms.AppendKeyValue("HostIpPort", self._HostIpPort)
|
|
330
318
|
apiMethodParms.AppendKeyValue("CPath", self._CPath)
|
|
331
319
|
apiMethodParms.AppendKeyValue("Version", self._Version)
|
|
@@ -440,7 +428,7 @@ class ZeroconfConnect:
|
|
|
440
428
|
|
|
441
429
|
# trace.
|
|
442
430
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
443
|
-
apiMethodParms.AppendKeyValue("
|
|
431
|
+
apiMethodParms.AppendKeyValue("HostIpAddress", self._HostIpAddress)
|
|
444
432
|
apiMethodParms.AppendKeyValue("HostIpPort", self._HostIpPort)
|
|
445
433
|
apiMethodParms.AppendKeyValue("CPath", self._CPath)
|
|
446
434
|
apiMethodParms.AppendKeyValue("Version", self._Version)
|
|
@@ -512,7 +500,7 @@ class ZeroconfConnect:
|
|
|
512
500
|
|
|
513
501
|
return "{protocol}://{ip}:{port}{cpath}".format(
|
|
514
502
|
protocol=protocol,
|
|
515
|
-
ip=self.
|
|
503
|
+
ip=self._HostIpAddress,
|
|
516
504
|
port=self._HostIpPort,
|
|
517
505
|
cpath=self._CPath,
|
|
518
506
|
)
|
|
@@ -546,7 +534,7 @@ class ZeroconfConnect:
|
|
|
546
534
|
|
|
547
535
|
# trace.
|
|
548
536
|
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
|
|
549
|
-
apiMethodParms.AppendKeyValue("
|
|
537
|
+
apiMethodParms.AppendKeyValue("HostIpAddress", self._HostIpAddress)
|
|
550
538
|
apiMethodParms.AppendKeyValue("HostIpPort", self._HostIpPort)
|
|
551
539
|
apiMethodParms.AppendKeyValue("CPath", self._CPath)
|
|
552
540
|
apiMethodParms.AppendKeyValue("Version", self._Version)
|
|
@@ -573,7 +561,7 @@ class ZeroconfConnect:
|
|
|
573
561
|
result = ZeroconfGetInfo(root=responseData)
|
|
574
562
|
|
|
575
563
|
# trace.
|
|
576
|
-
_logsi.LogObject(SILevel.Verbose, '%s result -
|
|
564
|
+
_logsi.LogObject(SILevel.Verbose, '%s result (%s) - "%s" (%s)' % (apiMethodName, type(result).__name__, result.RemoteName, result.DeviceId), result, excludeNonPublic=True)
|
|
577
565
|
return result
|
|
578
566
|
|
|
579
567
|
except SpotifyZeroconfApiError: raise # pass handled exceptions on thru
|
|
File without changes
|
|
File without changes
|
|
File without changes
|