spotifywebapiPython 1.0.49__py3-none-any.whl → 1.0.51__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.49.dist-info → spotifywebapiPython-1.0.51.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.49.dist-info → spotifywebapiPython-1.0.51.dist-info}/RECORD +26 -24
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +16 -3
  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 +2 -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 +115 -2
  14. spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +18 -2
  15. spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/models/__init__.py +2 -0
  18. spotifywebapipython/models/device.py +50 -0
  19. spotifywebapipython/models/spotifyconnectdevice.py +93 -0
  20. spotifywebapipython/models/spotifyconnectdevices.py +137 -0
  21. spotifywebapipython/models/zeroconfdiscoveryresult.py +0 -5
  22. spotifywebapipython/spotifyclient.py +93 -4
  23. spotifywebapipython/spotifydiscovery.py +10 -2
  24. {spotifywebapiPython-1.0.49.dist-info → spotifywebapiPython-1.0.51.dist-info}/LICENSE +0 -0
  25. {spotifywebapiPython-1.0.49.dist-info → spotifywebapiPython-1.0.51.dist-info}/WHEEL +0 -0
  26. {spotifywebapiPython-1.0.49.dist-info → spotifywebapiPython-1.0.51.dist-info}/top_level.txt +0 -0
@@ -64,6 +64,24 @@ class Device:
64
64
  return self.ToString()
65
65
 
66
66
 
67
+ # implement sorting support.
68
+ def __eq__(self, other):
69
+ try:
70
+ return self.Name == other.Name
71
+ except Exception as ex:
72
+ if (isinstance(self, Device )) and (isinstance(other, Device )):
73
+ return self.Name == other.Name
74
+ return False
75
+
76
+ def __lt__(self, other):
77
+ try:
78
+ return self.Name < other.Name
79
+ except Exception as ex:
80
+ if (isinstance(self, Device )) and (isinstance(other, Device )):
81
+ return self.Name < other.Name
82
+ return False
83
+
84
+
67
85
  @property
68
86
  def Id(self) -> str:
69
87
  """
@@ -74,6 +92,14 @@ class Device:
74
92
  """
75
93
  return self._Id
76
94
 
95
+ @Id.setter
96
+ def Id(self, value:str):
97
+ """
98
+ Sets the Id property value.
99
+ """
100
+ if isinstance(value, str):
101
+ self._Id = value
102
+
77
103
 
78
104
  @property
79
105
  def IsActive(self) -> bool:
@@ -82,6 +108,14 @@ class Device:
82
108
  """
83
109
  return self._IsActive
84
110
 
111
+ @IsActive.setter
112
+ def IsActive(self, value:bool):
113
+ """
114
+ Sets the IsActive property value.
115
+ """
116
+ if isinstance(value, bool):
117
+ self._IsActive = value
118
+
85
119
 
86
120
  @property
87
121
  def IsMuted(self) -> bool:
@@ -122,6 +156,14 @@ class Device:
122
156
  """
123
157
  return self._Name
124
158
 
159
+ @Name.setter
160
+ def Name(self, value:str):
161
+ """
162
+ Sets the Name property value.
163
+ """
164
+ if isinstance(value, str):
165
+ self._Name = value
166
+
125
167
 
126
168
  @property
127
169
  def SelectItemNameAndId(self) -> str:
@@ -151,6 +193,14 @@ class Device:
151
193
  """
152
194
  return self._Type
153
195
 
196
+ @Type.setter
197
+ def Type(self, value:str):
198
+ """
199
+ Sets the Type property value.
200
+ """
201
+ if isinstance(value, str):
202
+ self._Type = value
203
+
154
204
 
155
205
  @property
156
206
  def VolumePercent(self) -> int:
@@ -0,0 +1,93 @@
1
+ # external package imports.
2
+ from zeroconf import ServiceInfo
3
+
4
+ # our package imports.
5
+ from ..sautils import export
6
+ from .zeroconfdiscoveryresult import ZeroconfDiscoveryResult
7
+ from spotifywebapipython.zeroconfapi.zeroconfgetinfo import ZeroconfGetInfo
8
+
9
+ @export
10
+ class SpotifyConnectDevice():
11
+ """
12
+ Spotify Connect Device object.
13
+
14
+ Information about the Spotify Connect device, which is a combination of the
15
+ `ZeroconfDiscoveryResult` and `ZeroconfGetInfo` classes.
16
+ """
17
+
18
+ def __init__(self) -> None:
19
+ """
20
+ Initializes a new instance of the class.
21
+ """
22
+ self._DiscoveryResult:ZeroconfDiscoveryResult = None
23
+ self._DeviceInfo:ZeroconfGetInfo = None
24
+
25
+
26
+ def __repr__(self) -> str:
27
+ return self.ToString()
28
+
29
+
30
+ def __str__(self) -> str:
31
+ return self.ToString()
32
+
33
+
34
+ @property
35
+ def DiscoveryResult(self) -> ZeroconfDiscoveryResult:
36
+ """
37
+ Information about the Zeroconf entry for a SpotifyConnect device as found by Zeroconf (mDNS).
38
+ """
39
+ return self._DiscoveryResult
40
+
41
+ @DiscoveryResult.setter
42
+ def DiscoveryResult(self, value:ZeroconfDiscoveryResult):
43
+ """
44
+ Sets the DiscoveryResult property value.
45
+ """
46
+ if isinstance(value, ZeroconfDiscoveryResult):
47
+ self._DiscoveryResult = value
48
+
49
+
50
+ @property
51
+ def DeviceInfo(self) -> ZeroconfGetInfo:
52
+ """
53
+ Spotify Zeroconf API GetInfo response object.
54
+ """
55
+ return self._DeviceInfo
56
+
57
+ @DeviceInfo.setter
58
+ def DeviceInfo(self, value:ZeroconfGetInfo):
59
+ """
60
+ Sets the DeviceInfo property value.
61
+ """
62
+ if isinstance(value, ZeroconfGetInfo):
63
+ self._DeviceInfo = value
64
+
65
+
66
+ def ToDictionary(self) -> dict:
67
+ """
68
+ Returns a dictionary representation of the class.
69
+ """
70
+ result:dict = \
71
+ {
72
+ 'DeviceInfo': self._DeviceInfo.ToDictionary(),
73
+ 'DiscoveryResult': self._DiscoveryResult.ToDictionary(),
74
+ }
75
+ return result
76
+
77
+
78
+ def ToString(self, includeTitle:bool=True) -> str:
79
+ """
80
+ Returns a displayable string representation of the class.
81
+
82
+ Args:
83
+ includeTitle (str):
84
+ True to include the class name title prefix.
85
+ """
86
+ msg:str = ''
87
+ if includeTitle:
88
+ msg = 'Device: "%s" (%s)' % (self._DiscoveryResult.DeviceName, self._DeviceInfo.DeviceId)
89
+
90
+ # build result.
91
+ msg = '%s\n %s\n' % (msg, str(self._DeviceInfo.ToString(True)))
92
+ msg = '%s\n %s\n' % (msg, str(self._DiscoveryResult.ToString(True)))
93
+ return msg
@@ -0,0 +1,137 @@
1
+ # external package imports.
2
+ from typing import Iterator
3
+
4
+ # our package imports.
5
+ from ..sautils import export
6
+ from .device import Device
7
+ from .spotifyconnectdevice import SpotifyConnectDevice
8
+ from spotifywebapipython.zeroconfapi import ZeroconfGetInfo, ZeroconfGetInfoAlias
9
+
10
+ @export
11
+ class SpotifyConnectDevices():
12
+ """
13
+ Spotify Connect Devices collection.
14
+ """
15
+
16
+ def __init__(self) -> None:
17
+ """
18
+ Initializes a new instance of the class.
19
+ """
20
+ self._Items:list[SpotifyConnectDevice] = []
21
+
22
+
23
+ def __getitem__(self, key) -> SpotifyConnectDevice:
24
+ return self._Presets[key]
25
+
26
+
27
+ def __iter__(self) -> Iterator:
28
+ return iter(self._Items)
29
+
30
+
31
+ def __len__(self) -> int:
32
+ return len(self._Items)
33
+
34
+
35
+ def __repr__(self) -> str:
36
+ return self.ToString()
37
+
38
+
39
+ def __str__(self) -> str:
40
+ return self.ToString()
41
+
42
+
43
+ @property
44
+ def Items(self) -> list[SpotifyConnectDevice]:
45
+ """
46
+ Array of `SpotifyConnectDevice` objects.
47
+ """
48
+ return self._Items
49
+
50
+
51
+ @property
52
+ def ItemsCount(self) -> int:
53
+ """
54
+ Number of objects in the `Items` property array.
55
+ """
56
+ if self._Items is not None:
57
+ return len(self._Items)
58
+ return 0
59
+
60
+
61
+ def GetDeviceList(self) -> list[Device]:
62
+ """
63
+ Returns a list of `Device` objects that can be used to build a selection list
64
+ of available devices.
65
+
66
+ Note that the `Device` object has the following properties populated:
67
+ Id, Name, IsActive, Type.
68
+ """
69
+ result:list[Device] = []
70
+
71
+ # process all discovered devices.
72
+ scDevice:SpotifyConnectDevice
73
+ for scDevice in self._Items:
74
+
75
+ # map device information details.
76
+ info:ZeroconfGetInfo = scDevice.DeviceInfo
77
+
78
+ # create new mock device.
79
+ device = Device()
80
+ device.IsActive = info.HasActiveUser
81
+ device.Type = info.DeviceType
82
+
83
+ # are aliases being used (RemoteName is null if so)?
84
+ if info.RemoteName is None:
85
+
86
+ # if aliases are defined, then use the alias details.
87
+ infoAlias:ZeroconfGetInfoAlias
88
+ for infoAlias in info.Aliases:
89
+ device.Id = infoAlias.Id
90
+ device.Name = infoAlias.Name
91
+
92
+ else:
93
+
94
+ # if no aliases then use the remote name and id.
95
+ device.Id = info.DeviceId
96
+ device.Name = info.RemoteName
97
+
98
+ # append device to results.
99
+ result.append(device)
100
+
101
+ # sort items on Name property, ascending order.
102
+ if len(result) > 0:
103
+ result.sort(key=lambda x: (x.Name or "").lower(), reverse=False)
104
+
105
+ return result
106
+
107
+
108
+ def ToDictionary(self) -> dict:
109
+ """
110
+ Returns a dictionary representation of the class.
111
+ """
112
+ result:dict = \
113
+ {
114
+ 'ItemsCount': self.ItemsCount,
115
+ 'Items': [ item.ToDictionary() for item in self._Items ],
116
+ }
117
+
118
+ return result
119
+
120
+
121
+ def ToString(self, includeItems:bool=False) -> str:
122
+ """
123
+ Returns a displayable string representation of the class.
124
+
125
+ Args:
126
+ includeItems (bool):
127
+ True to include the Items collection of objects; otherwise, False
128
+ to only return base properties.
129
+ """
130
+ msg:str = 'Items Count=%s' % (str(self.ItemsCount))
131
+
132
+ if (includeItems):
133
+ item:SpotifyConnectDevice
134
+ for item in self._Items:
135
+ msg = "%s\n- %s" % (msg, item.ToString(True))
136
+
137
+ return msg
@@ -16,11 +16,6 @@ class ZeroconfDiscoveryResult:
16
16
  def __init__(self) -> None:
17
17
  """
18
18
  Initializes a new instance of the class.
19
-
20
- Args:
21
- root (dict):
22
- Spotify Web API JSON response in dictionary format, used to load object
23
- attributes; otherwise, None to not load attributes.
24
19
  """
25
20
  self._DeviceName:str = None
26
21
  self._Domain:str = '.'
@@ -146,7 +146,7 @@ class SpotifyClient:
146
146
  self._TokenStorageDir:str = tokenStorageDir
147
147
  self._TokenUpdater:Callable = tokenUpdater
148
148
  self._UserProfile:UserProfile = None
149
- self._Zeroconf = zeroconfClient
149
+ self._ZeroconfClient = zeroconfClient
150
150
 
151
151
  # if pool manager instance is none or not a PoolManager instance, then create one.
152
152
  # we increase the maximum number of connections to keep in the pool (maxsize=) to avoid the following warnings:
@@ -165,7 +165,7 @@ class SpotifyClient:
165
165
  # create the zeroconf client if one was not specified.
166
166
  if zeroconfClient is None:
167
167
  _logsi.LogVerbose("Creating new Zeroconf instance for discovery")
168
- self._Zeroconf = Zeroconf()
168
+ self._ZeroconfClient = Zeroconf()
169
169
  else:
170
170
  _logsi.LogObject(SILevel.Verbose, "Using existing Zeroconf instance for discovery", zeroconfClient)
171
171
 
@@ -299,6 +299,14 @@ class SpotifyClient:
299
299
  return self._UserProfile
300
300
 
301
301
 
302
+ @property
303
+ def ZeroconfClient(self) -> Zeroconf:
304
+ """
305
+ Zeroconf client instance that will be used to discover Spotify Connect devices.
306
+ """
307
+ return self._ZeroconfClient
308
+
309
+
302
310
  def _CheckForNextPageWithOffset(self,
303
311
  pageObj:PageObject,
304
312
  resultItemsCount,
@@ -6012,7 +6020,7 @@ class SpotifyClient:
6012
6020
  # for all devices to be discovered. this will return all Spotify Connect devices that
6013
6021
  # are currently powered on, and are registered to Zeroconf / mDNS.
6014
6022
  _logsi.LogVerbose("Discovering Spotify Connect devices on the local network")
6015
- discovery:SpotifyDiscovery = SpotifyDiscovery(self._Zeroconf, printToConsole=False)
6023
+ discovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
6016
6024
  discovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
6017
6025
 
6018
6026
  # process all discovered devices.
@@ -7882,6 +7890,87 @@ class SpotifyClient:
7882
7890
  _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
7883
7891
 
7884
7892
 
7893
+ def GetSpotifyConnectDevices(
7894
+ self,
7895
+ ) -> SpotifyConnectDevices:
7896
+ """
7897
+ Get information about all available Spotify Connect player devices.
7898
+
7899
+ Returns:
7900
+ A `SpotifyConnectDevices` object that contain the discovery results
7901
+ as well as the `getInfo` result for each found device.
7902
+
7903
+ Raises:
7904
+ SpotifyWebApiError:
7905
+ If the Spotify Web API request was for a non-authorization service
7906
+ and the response contains error information.
7907
+ SpotifyZeroconfApiError:
7908
+ If the Spotify Zeroconf API request faild for any reason.
7909
+ SpotifApiError:
7910
+ If the method fails for any other reason.
7911
+
7912
+ This method is similar to the `GetPlayerDevices` method, but it contains ALL
7913
+ available Spotify Connect devices that are known to the local network (versus
7914
+ just the devices known to a specific user).
7915
+
7916
+ <details>
7917
+ <summary>Sample Code</summary>
7918
+ ```python
7919
+ .. include:: ../docs/include/samplecode/SpotifyClient/GetSpotifyConnectDevices.py
7920
+ ```
7921
+ </details>
7922
+ """
7923
+ apiMethodName:str = 'GetSpotifyConnectDevices'
7924
+ result:SpotifyConnectDevices = SpotifyConnectDevices()
7925
+
7926
+ try:
7927
+
7928
+ # trace.
7929
+ _logsi.EnterMethod(SILevel.Debug, apiMethodName)
7930
+ _logsi.LogVerbose("Get all available Spotify Connect devices")
7931
+
7932
+ # discover Spotify Connect devices on the network, waiting up to the specified timeout
7933
+ # for all devices to be discovered. this will return all Spotify Connect devices that
7934
+ # are currently powered on, and are registered to Zeroconf / mDNS.
7935
+ _logsi.LogVerbose("Discovering Spotify Connect devices on the local network")
7936
+ discovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
7937
+ discovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
7938
+
7939
+ # process all discovered devices.
7940
+ discoverResult:ZeroconfDiscoveryResult
7941
+ for discoverResult in discovery.DiscoveryResults:
7942
+
7943
+ # get the id from the device via the zeroconf API getInfo endpoint, as the id is not
7944
+ # returned in the zeroconf discovery result.
7945
+ zconn:ZeroconfConnect = ZeroconfConnect(discoverResult.HostIpv4Address,
7946
+ discoverResult.HostIpPort,
7947
+ discoverResult.SpotifyConnectCPath,
7948
+ useSSL=False)
7949
+ info:ZeroconfGetInfo = zconn.GetInformation()
7950
+
7951
+ # create new spotify connect device object.
7952
+ scDevice:SpotifyConnectDevice = SpotifyConnectDevice()
7953
+ scDevice.DiscoveryResult = discoverResult
7954
+ scDevice.DeviceInfo = info
7955
+ result.Items.append(scDevice)
7956
+
7957
+ # trace.
7958
+ _logsi.LogArray(SILevel.Verbose, TRACE_METHOD_RESULT_TYPE % (apiMethodName, type(result).__name__), result)
7959
+ return result
7960
+
7961
+ except SpotifyZeroconfApiError: raise # pass handled exceptions on thru
7962
+ except SpotifyApiError: raise # pass handled exceptions on thru
7963
+ except Exception as ex:
7964
+
7965
+ # format unhandled exception.
7966
+ raise SpotifyApiError(SAAppMessages.UNHANDLED_EXCEPTION.format(apiMethodName, str(ex)), ex, logsi=_logsi)
7967
+
7968
+ finally:
7969
+
7970
+ # trace.
7971
+ _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
7972
+
7973
+
7885
7974
  def GetTrack(self,
7886
7975
  trackId:str,
7887
7976
  ) -> Track:
@@ -10184,7 +10273,7 @@ class SpotifyClient:
10184
10273
  # for all devices to be discovered. this will return all Spotify Connect devices that
10185
10274
  # are currently powered on, and are registered to Zeroconf / mDNS.
10186
10275
  _logsi.LogVerbose("Discovering Spotify Connect devices on the local network")
10187
- discovery:SpotifyDiscovery = SpotifyDiscovery(self._Zeroconf, printToConsole=False)
10276
+ discovery:SpotifyDiscovery = SpotifyDiscovery(self._ZeroconfClient, printToConsole=False)
10188
10277
  discovery.DiscoverDevices(timeout=self._SpotifyConnectDiscoveryTimeout)
10189
10278
 
10190
10279
  # process all discovered devices.
@@ -65,7 +65,7 @@ class SpotifyDiscovery:
65
65
  self._DiscoveredDeviceNames:dict = {}
66
66
  self._DiscoveryResults:list[ZeroconfDiscoveryResult] = []
67
67
  self._PrintToConsole:bool = printToConsole
68
- self._Zeroconf = zeroconfClient
68
+ self._ZeroconfClient = zeroconfClient
69
69
 
70
70
 
71
71
  def __getitem__(self, key):
@@ -113,6 +113,14 @@ class SpotifyDiscovery:
113
113
  return self._DiscoveryResults
114
114
 
115
115
 
116
+ @property
117
+ def ZeroconfClient(self) -> Zeroconf:
118
+ """
119
+ Zeroconf client instance that will be used to discover Spotify Connect devices.
120
+ """
121
+ return self._ZeroconfClient
122
+
123
+
116
124
  def _OnServiceStateChange(self,
117
125
  zeroconf:Zeroconf,
118
126
  service_type:str,
@@ -234,7 +242,7 @@ class SpotifyDiscovery:
234
242
 
235
243
  # create the zeroconf service browser that will start device discovery.
236
244
  _logsi.LogVerbose("Discovery of Spotify Connect devices via Zeroconf is starting ...")
237
- ServiceBrowser(self._Zeroconf, "_spotify-connect._tcp.local.", handlers=[self._OnServiceStateChange])
245
+ ServiceBrowser(self._ZeroconfClient, "_spotify-connect._tcp.local.", handlers=[self._OnServiceStateChange])
238
246
 
239
247
  try:
240
248
  # give the ServiceBrowser time to discover