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.
- {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/RECORD +26 -26
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +15 -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 +29 -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 +31 -7
- spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
- spotifywebapipython/models/spotifyconnectdevices.py +103 -0
- spotifywebapipython/models/zeroconfdiscoveryresult.py +13 -0
- spotifywebapipython/sautils.py +31 -0
- spotifywebapipython/spotifyclient.py +105 -92
- spotifywebapipython/zeroconfapi/zeroconfconnect.py +46 -10
- spotifywebapipython/zeroconfapi/zeroconfgetinfo.py +32 -0
- spotifywebapipython/zeroconfapi/zeroconfresponse.py +24 -0
- {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.51.dist-info → spotifywebapiPython-1.0.53.dist-info}/WHEEL +0 -0
- {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
|
"""
|
spotifywebapipython/sautils.py
CHANGED
|
@@ -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.
|
|
5949
|
-
) ->
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
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
|
-
|
|
6028
|
-
for discoverResult in discovery.DiscoveryResults:
|
|
6007
|
+
for scDevice in scDevices:
|
|
6029
6008
|
|
|
6030
|
-
|
|
6031
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
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
|
-
|
|
10281
|
-
for discoverResult in discovery.DiscoveryResults:
|
|
10288
|
+
for scDevice in scDevices:
|
|
10282
10289
|
|
|
10283
|
-
|
|
10284
|
-
|
|
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.
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
10880
|
+
delay = validateDelay(delay, 0.50, 10)
|
|
10868
10881
|
|
|
10869
10882
|
# get current Spotify Connect player state.
|
|
10870
10883
|
result = self.GetPlayerPlaybackState()
|