spotifywebapiPython 1.0.150__py3-none-any.whl → 1.0.152__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.150.dist-info → spotifywebapiPython-1.0.152.dist-info}/METADATA +2 -1
  2. {spotifywebapiPython-1.0.150.dist-info → spotifywebapiPython-1.0.152.dist-info}/RECORD +26 -26
  3. spotifywebapipython/const.py +6 -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 +20 -3
  8. spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
  9. spotifywebapipython/docs/spotifywebapipython/sautils.html +65 -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 +337 -120
  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/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
  18. spotifywebapipython/models/playerplaystate.py +42 -0
  19. spotifywebapipython/models/spotifyconnectdevice.py +12 -0
  20. spotifywebapipython/sautils.py +50 -0
  21. spotifywebapipython/spotifyclient.py +847 -296
  22. spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +98 -16
  23. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfexceptions.py +10 -0
  24. {spotifywebapiPython-1.0.150.dist-info → spotifywebapiPython-1.0.152.dist-info}/LICENSE +0 -0
  25. {spotifywebapiPython-1.0.150.dist-info → spotifywebapiPython-1.0.152.dist-info}/WHEEL +0 -0
  26. {spotifywebapiPython-1.0.150.dist-info → spotifywebapiPython-1.0.152.dist-info}/top_level.txt +0 -0
@@ -6,12 +6,14 @@ from datetime import datetime
6
6
  import hashlib
7
7
  from pychromecast import CastBrowser, CastInfo, Chromecast, get_chromecast_from_cast_info
8
8
  import socket
9
+ from soco import SoCo
9
10
  import threading
10
11
  import time
11
12
  from uuid import UUID
12
13
  from zeroconf import Zeroconf, ServiceBrowser
13
14
 
14
15
  # our package imports.
16
+ from .spotifyconnectzeroconfexceptions import SpotifyConnectDeviceNotFound
15
17
  from .spotifyconnectzeroconfcastapptask import SpotifyConnectZeroconfCastAppTask
16
18
  from .spotifyconnectzeroconfcastlistener import SpotifyConnectZeroconfCastListener
17
19
  from .spotifyconnectdeviceeventargs import SpotifyConnectDeviceEventArgs
@@ -101,6 +103,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
101
103
  self._CastBrowser:CastBrowser = None
102
104
  self._InitialDiscoveryTimeout = initialDiscoveryTimeout
103
105
  self._IsStopRequested:bool = False
106
+ self._SonosPlayers:dict = {}
104
107
  self._SpotifyClientInstance = spotifyClientInstance
105
108
  self._SpotifyConnectBrowser:ServiceBrowser = None
106
109
  self._SpotifyConnectDevices:SpotifyConnectDevices = SpotifyConnectDevices()
@@ -731,33 +734,42 @@ class SpotifyConnectDirectoryTask(threading.Thread):
731
734
  self,
732
735
  value:str,
733
736
  refreshDynamicDevices:bool=True,
737
+ raiseExceptionIfNotFound:bool=True,
734
738
  ) -> SpotifyConnectDevice:
735
739
  """
736
- Returns a `SpotifyConnectDevice` object for the specified device name / id if found;
737
- otherwise, an exception is returned indicating a device was not found.
740
+ Returns a `SpotifyConnectDevice` object for the specified device name / id if found
741
+ in the devices collection. otherwise, returns the currently active player
742
+ `SpotifyConnectDevice` object is returned. otherwise, null is returned.
738
743
 
739
744
  Args:
740
- value (str):
741
- Spotify Connect device name or id value used to identify the device,
742
- or null to retrieve the currently active Spotify Player device,
743
- or an asterisk (*) to use the SpotifyPlus configured default device.
745
+ value (str | None):
746
+ The target player device identifier.
747
+ This could be an id, name, a default device indicator (e.g. "*"), or null
748
+ to utilize the active player device.
749
+ Examples are `0d1841b0976bae2a3a310dd74c0f3df354899bc8`, `Office`, `*`, None.
744
750
  refreshDynamicDevices (bool):
745
751
  True to refresh the list of dynamically added devices and the active player
746
752
  state from real-time Spotify Web API data; otherwise, false to use the
747
753
  current cached state and device list.
748
754
  Default is True.
755
+ raiseExceptionIfNotFound (bool):
756
+ True to raise an exception if a device could not be resolved;
757
+ otherwise, False to just return a null value.
758
+ Default is True.
749
759
 
750
760
  Returns:
751
- A `SpotifyConnectDevice` object for the specified device name / id.
761
+ A `SpotifyConnectDevice` object for the specified device name / id if found;
762
+ otherwise, null.
752
763
 
753
764
  Raises:
765
+ SpotifyConnectDeviceNotFound:
766
+ If the `raiseExceptionIfNotFound` argument value is True, and the specified
767
+ device value was not found or there was no default device found.
754
768
  SpotifyApiError:
755
- If the specified device value was not found.
756
- If there was no defaualt device found.
757
769
  If an error occured while obtaining current Spotify Web API Player device data.
758
770
 
759
- The object returned is a copy of the internal master device entry; any changes made
760
- to the copy do not affect the internal master device entry.
771
+ If a SpotifyConnectDevice object is returned, it will be a copy of the internal master
772
+ device entry; any changes made to the copy do not affect the internal master device entry.
761
773
 
762
774
  <details>
763
775
  <summary>Sample Code</summary>
@@ -788,6 +800,8 @@ class SpotifyConnectDirectoryTask(threading.Thread):
788
800
  value = None
789
801
  if (not isinstance(refreshDynamicDevices, bool)):
790
802
  refreshDynamicDevices = True
803
+ if (not isinstance(raiseExceptionIfNotFound, bool)):
804
+ raiseExceptionIfNotFound = True
791
805
  valueOriginal:str = value
792
806
 
793
807
  # get currently active player device.
@@ -804,6 +818,10 @@ class SpotifyConnectDirectoryTask(threading.Thread):
804
818
  _logsi.LogObject(SILevel.Verbose, "Spotify Player device \"%s\" (%s) was found in the Spotify Connect Devices collection (by Active PlayerState)" % (scActiveDevice.Name, scActiveDevice.Id), scActiveDevice, excludeNonPublic=True)
805
819
  return scActiveDevice
806
820
 
821
+ # if no active player device, then let's use the default device value.
822
+ _logsi.LogVerbose("Spotify Player has no active device; defaulting device selection to use DefaultDeviceId: \"%s\"" % (self.SpotifyClientInstance.DefaultDeviceId))
823
+ value = self.SpotifyClientInstance.DefaultDeviceId
824
+
807
825
  # was a default device value specified?
808
826
  # if so, then use the `defaultDeviceId` value; if the `defaultDeviceId` value is not in the device list,
809
827
  # then we will catch it with the `GetDeviceById` / `GetDeviceByName` logic.
@@ -822,7 +840,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
822
840
  _logsi.LogObject(SILevel.Verbose, "Spotify Player default device (*) was not configured; using active player device: %s" % (scActiveDevice.Title), scActiveDevice, excludeNonPublic=True)
823
841
  return scActiveDevice
824
842
  # if no default device and no active player, then it's an error!
825
- raise SpotifyApiError("Spotify Player default device (*) was not configured, and there is no active Spotify Player device", logsi=_logsi)
843
+ #raise SpotifyApiError("Spotify Player default device (*) was not configured, and there is no active Spotify Player device", logsi=_logsi)
826
844
 
827
845
  # assign default device value.
828
846
  _logsi.LogVerbose("Spotify Player device default \"%s\" will be used for selecting a device (by default *)" % (defaultDeviceId))
@@ -858,8 +876,13 @@ class SpotifyConnectDirectoryTask(threading.Thread):
858
876
  _logsi.LogObject(SILevel.Verbose, "Spotify Player default device \"%s\" was not found in the Spotify Connect Devices collection; using active player device: %s" % (value, scActiveDevice.Title), scActiveDevice, excludeNonPublic=True)
859
877
  return scActiveDevice
860
878
 
861
- # at this point we could not resolve the device name / id!
862
- raise SpotifyApiError("Spotify Player device \"%s\" was not found in the Spotify Connect Devices collection" % (value), logsi=_logsi)
879
+ # at this point we could not resolve the device name / id, and there is no active player
880
+ # available; raise exception / return null as requested by the caller.
881
+ if (raiseExceptionIfNotFound):
882
+ raise SpotifyConnectDeviceNotFound("Spotify Player device \"%s\" was not found, and there is no active Spotify player" % (value))
883
+ else:
884
+ _logsi.LogVerbose("Spotify Player device \"%s\" was not found, and there is no active Spotify player" % (value))
885
+ return None
863
886
 
864
887
  except SpotifyApiError: raise # pass handled exceptions on thru
865
888
  except Exception as ex:
@@ -957,6 +980,50 @@ class SpotifyConnectDirectoryTask(threading.Thread):
957
980
  return result
958
981
 
959
982
 
983
+ def GetSonosPlayer(
984
+ self,
985
+ device:SpotifyConnectDevice,
986
+ ) -> SoCo:
987
+ """
988
+ Returns the Sonos Controller instance for the specified Spotify Connect device.
989
+ Returns the Sonos player instance for the specified Spotify Connect Sonos device.
990
+
991
+ Args:
992
+ device (SpotifyConnectDevice):
993
+ Spotify Connect device instance used to identify the Sonos player.
994
+
995
+ Returns:
996
+ The Sonos Controller instance for the specified Spotify Connect device.
997
+
998
+ Raises:
999
+ SpotifyApiError:
1000
+ If a Sonos Controller instance could not be found for the device id.
1001
+
1002
+ You should invoke "if (scDevice.IsSonos):" logic prior to calling this method, as
1003
+ a Sonos Controller instance will not be created for non-Sonos devices and an exception
1004
+ will be raised!
1005
+ """
1006
+ apiMethodName:str = "GetSonosPlayer"
1007
+
1008
+ # syncronize access via lock, as we are accessing the collection.
1009
+ with self._SpotifyConnectDevices_RLock:
1010
+
1011
+ result:SpotifyConnectDevice = None
1012
+
1013
+ # validations.
1014
+ if (device is None):
1015
+ raise SpotifyApiError(SAAppMessages.ARGUMENT_REQUIRED_ERROR % (apiMethodName, 'device'), logsi=_logsi)
1016
+
1017
+ # get Sonos Controller instance for device name.
1018
+ sonosPlayer:SoCo = self._SonosPlayers.get(device.DiscoveryResult.HostIpAddress, None)
1019
+
1020
+ # if not found then it's an error.
1021
+ if (sonosPlayer is None):
1022
+ raise SpotifyApiError("Could not find Sonos Controller instance for device: %s" % (device.Title), None, logsi=_logsi)
1023
+
1024
+ return sonosPlayer
1025
+
1026
+
960
1027
  def GetSpotifyDeviceIDFromName(
961
1028
  self,
962
1029
  name:str
@@ -1013,9 +1080,9 @@ class SpotifyConnectDirectoryTask(threading.Thread):
1013
1080
 
1014
1081
  # trace.
1015
1082
  if (scDevice is None):
1016
- _logsi.LogVerbose("Spotify Player playstate active device is not present; no active device")
1083
+ _logsi.LogVerbose("Spotify Player playstate device is not present; no active device")
1017
1084
  else:
1018
- _logsi.LogObject(SILevel.Verbose, "Spotify Player playstate active device instance %s" % (scDevice.Title), scDevice, excludeNonPublic=True)
1085
+ _logsi.LogObject(SILevel.Verbose, "Spotify Player playstate active device instance: %s" % (scDevice.Title), scDevice, excludeNonPublic=True)
1019
1086
 
1020
1087
  # return real-time active Spotify Player device.
1021
1088
  return scDevice
@@ -1738,6 +1805,21 @@ class SpotifyConnectDirectoryTask(threading.Thread):
1738
1805
  if (len(self._SpotifyConnectDevices.Items) > 0):
1739
1806
  self._SpotifyConnectDevices.Items.sort(key=lambda x: (x.Name or "").lower(), reverse=False)
1740
1807
 
1808
+ try:
1809
+
1810
+ # if new device is a Sonos, then create a Sonos Controller instance for the device.
1811
+ # use device ip address as the key, as Spotify Web API reports id=null for restricted devices in playerstate!
1812
+ if (scDevice.IsSonos):
1813
+ _logsi.LogVerbose("Sonos device detected; creating Sonos Controller instance for device: %s (ip=%s)" % (scDevice.Title, zeroconfDiscoveryResult.HostIpAddress))
1814
+ self._SonosPlayers[zeroconfDiscoveryResult.HostIpAddress] = SoCo(zeroconfDiscoveryResult.HostIpAddress)
1815
+
1816
+ except:
1817
+
1818
+ # trace.
1819
+ _logsi.LogException("%s - Could not create Sonos Control API reference: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
1820
+
1821
+ # ignore exceptions, as there is nothing we can do at this point.
1822
+
1741
1823
  # raise event.
1742
1824
  self._RaiseDeviceAdded(scDevice)
1743
1825
 
@@ -1,3 +1,7 @@
1
+ # our package imports.
2
+ from spotifywebapipython import SpotifyApiError
3
+
4
+
1
5
  class SpotifyConnectZeroconfLaunchError(Exception):
2
6
  """
3
7
  Exception raised when a cast app fails to launch.
@@ -8,3 +12,9 @@ class SpotifyConnectZeroconfPlaybackTransferError(Exception):
8
12
  """
9
13
  Exception raised when a cast app fails to receive transfer of playback control.
10
14
  """
15
+
16
+
17
+ class SpotifyConnectDeviceNotFound(SpotifyApiError):
18
+ """
19
+ Exception raised when a Spotify Connect device could not be resolved.
20
+ """