spotifywebapiPython 1.0.155__py3-none-any.whl → 1.0.157__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 (25) hide show
  1. {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/RECORD +25 -25
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +14 -2
  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 +2 -2
  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/spotifyclient.py +94 -19
  19. spotifywebapipython/spotifyconnect/__init__.py +2 -2
  20. spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +13 -8
  21. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastapptask.py +84 -75
  22. spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastcontroller.py +14 -12
  23. {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/LICENSE +0 -0
  24. {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/WHEEL +0 -0
  25. {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/top_level.txt +0 -0
@@ -24,7 +24,7 @@ from zeroconf import Zeroconf
24
24
  from .oauthcli import AuthClient
25
25
  from .models import *
26
26
  from .models import UserProfile as UserProfileCurrentUser
27
- from .spotifyconnect import SpotifyConnectDirectoryTask
27
+ from .spotifyconnect import SpotifyConnectDirectoryTask, SpotifyConnectDeviceNotFound
28
28
  from .saappmessages import SAAppMessages
29
29
  from .spotifyapierror import SpotifyApiError
30
30
  from .spotifyapimessage import SpotifyApiMessage
@@ -473,6 +473,25 @@ class SpotifyClient:
473
473
  return self._ZeroconfClient
474
474
 
475
475
 
476
+ def _CheckForDeviceNotFound(
477
+ self,
478
+ scDevice:SpotifyConnectDevice,
479
+ deviceId:str,
480
+ ) -> None:
481
+ """
482
+ Called to raise a SpotifyConnectDeviceNotFound exception when a Spotify Connect
483
+ device could not be found and there is no active player.
484
+ """
485
+ # was a device resolved?
486
+ if (scDevice is None):
487
+
488
+ # if deviceId could not be resolved, then it's an error.
489
+ if (deviceId is None) or (deviceId == "*"):
490
+ raise SpotifyConnectDeviceNotFound("There is no active Spotify player, and a default player device was not configured; select a player device and try again.")
491
+ else:
492
+ raise SpotifyConnectDeviceNotFound("Spotify Player device \"%s\" was not found, and there is no active Spotify player; verify the device is active and available on the local network, and try again." % (deviceId))
493
+
494
+
476
495
  def _CheckForNextPageWithOffset(
477
496
  self,
478
497
  pageObj:PageObject,
@@ -1231,10 +1250,11 @@ class SpotifyClient:
1231
1250
  for idx in range(0, len(arrUris)):
1232
1251
  arrUris[idx] = arrUris[idx].strip()
1233
1252
 
1234
- # resolve / activate the device object if needed.
1253
+ # resolve the device object from the device id.
1235
1254
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
1236
1255
 
1237
- # is this a Sonos device?
1256
+ # is this an active Sonos device?
1257
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
1238
1258
  if (scDevice is not None) and (scDevice.IsSonos):
1239
1259
 
1240
1260
  # trace.
@@ -1257,6 +1277,10 @@ class SpotifyClient:
1257
1277
  # trace.
1258
1278
  _logsi.LogVerbose("Items will be added to playback queue for device: %s" % (scDevice.Title))
1259
1279
 
1280
+ # was the deviceId resolved?
1281
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
1282
+ self._CheckForDeviceNotFound(scDevice, deviceId)
1283
+
1260
1284
  # process all uri's.
1261
1285
  for idx in range(0, len(arrUris)):
1262
1286
 
@@ -11569,10 +11593,11 @@ class SpotifyClient:
11569
11593
  # validations.
11570
11594
  delay = validateDelay(delay, 0.50, 10)
11571
11595
 
11572
- # resolve / activate the device object if needed.
11596
+ # resolve the device object from the device id.
11573
11597
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
11574
11598
 
11575
11599
  # is this an active Sonos device?
11600
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
11576
11601
  if (scDevice is not None) and (scDevice.IsSonos):
11577
11602
 
11578
11603
  _logsi.LogVerbose("Issuing command to Sonos device %s: PAUSE" % (scDevice.Title))
@@ -11581,6 +11606,10 @@ class SpotifyClient:
11581
11606
 
11582
11607
  else:
11583
11608
 
11609
+ # was the deviceId resolved?
11610
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
11611
+ self._CheckForDeviceNotFound(scDevice, deviceId)
11612
+
11584
11613
  # build spotify web api request parameters.
11585
11614
  urlParms:dict = {}
11586
11615
  if (scDevice is not None):
@@ -11728,10 +11757,11 @@ class SpotifyClient:
11728
11757
  if (offsetPosition is None) or (offsetPosition < 0):
11729
11758
  offsetPosition = 0
11730
11759
 
11731
- # resolve / activate the device object if needed.
11732
- scDevice = self._ResolveDeviceObject(deviceId, False)
11760
+ # resolve the device object from the device idl activate if it's dormant.
11761
+ scDevice = self._ResolveDeviceObject(deviceId, True)
11733
11762
 
11734
- # is this a Sonos device?
11763
+ # is this an active Sonos device?
11764
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
11735
11765
  if (scDevice is not None) and (scDevice.IsSonos):
11736
11766
 
11737
11767
  # trace.
@@ -11808,6 +11838,10 @@ class SpotifyClient:
11808
11838
  # trace.
11809
11839
  _logsi.LogVerbose("Context will be played on the Spotify Connect device via Spotify Web API")
11810
11840
 
11841
+ # was the deviceId resolved?
11842
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
11843
+ self._CheckForDeviceNotFound(scDevice, deviceId)
11844
+
11811
11845
  # build spotify web api request parameters.
11812
11846
  reqData:dict = \
11813
11847
  {
@@ -11950,7 +11984,7 @@ class SpotifyClient:
11950
11984
  for trackSaved in tracks.Items:
11951
11985
  arrUris.append(trackSaved.Track.Uri)
11952
11986
 
11953
- # resolve / activate the device object if needed.
11987
+ # resolve the device object from the device id.
11954
11988
  scDevice = self._ResolveDeviceObject(deviceId, False)
11955
11989
 
11956
11990
  # set desired shuffle mode.
@@ -12073,10 +12107,11 @@ class SpotifyClient:
12073
12107
  for idx in range(0, len(arrUris)):
12074
12108
  arrUris[idx] = arrUris[idx].strip()
12075
12109
 
12076
- # resolve / activate the device object if needed.
12110
+ # resolve the device object from the device id; activate if it's dormant.
12077
12111
  scDevice = self._ResolveDeviceObject(deviceId, True)
12078
12112
 
12079
- # is this a Sonos device?
12113
+ # is this an active Sonos device?
12114
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12080
12115
  if (scDevice is not None) and (scDevice.IsSonos):
12081
12116
 
12082
12117
  # trace.
@@ -12129,6 +12164,10 @@ class SpotifyClient:
12129
12164
  # trace.
12130
12165
  _logsi.LogVerbose("Track list will be played on the Spotify Connect device via Spotify Web API")
12131
12166
 
12167
+ # was the deviceId resolved?
12168
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12169
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12170
+
12132
12171
  # build spotify web api request parameters.
12133
12172
  reqData:dict = \
12134
12173
  {
@@ -12228,10 +12267,12 @@ class SpotifyClient:
12228
12267
  # validations.
12229
12268
  delay = validateDelay(delay, 0.50, 10)
12230
12269
 
12231
- # resolve / activate the device object if needed.
12232
- scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12270
+ # resolve the device object from the device id; activate if it's dormant.
12271
+ # we activate the device here, as it may have been paused for a long time.
12272
+ scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, True)
12233
12273
 
12234
12274
  # is this an active Sonos device?
12275
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12235
12276
  if (scDevice is not None) and (scDevice.IsSonos):
12236
12277
 
12237
12278
  _logsi.LogVerbose("Issuing command to Sonos device %s: PLAY (RESUME)" % (scDevice.Title))
@@ -12240,6 +12281,10 @@ class SpotifyClient:
12240
12281
 
12241
12282
  else:
12242
12283
 
12284
+ # was the deviceId resolved?
12285
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12286
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12287
+
12243
12288
  # build spotify web api request parameters.
12244
12289
  urlParms:dict = {}
12245
12290
  if (scDevice is not None):
@@ -12366,7 +12411,7 @@ class SpotifyClient:
12366
12411
  if (relativePositionMS is None):
12367
12412
  relativePositionMS = 0
12368
12413
 
12369
- # resolve / activate the device object if needed.
12414
+ # resolve the device object from the device id.
12370
12415
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12371
12416
 
12372
12417
  # was relative seeking specified?
@@ -12384,6 +12429,7 @@ class SpotifyClient:
12384
12429
  positionMS = newPositionMS
12385
12430
 
12386
12431
  # is this an active Sonos device?
12432
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12387
12433
  if (scDevice is not None) and (scDevice.IsSonos):
12388
12434
 
12389
12435
  sonosPosition:str = mediaPositionHMS_fromSeconds(positionMS / 1000) # convert from milliseconds to Sonos H:MM:SS format
@@ -12393,6 +12439,10 @@ class SpotifyClient:
12393
12439
 
12394
12440
  else:
12395
12441
 
12442
+ # was the deviceId resolved?
12443
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12444
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12445
+
12396
12446
  # build spotify web api request parameters.
12397
12447
  urlParms:dict = \
12398
12448
  {
@@ -12487,10 +12537,11 @@ class SpotifyClient:
12487
12537
  # validations.
12488
12538
  delay = validateDelay(delay, 0.50, 10)
12489
12539
 
12490
- # resolve / activate the device object if needed.
12540
+ # resolve the device object from the device id.
12491
12541
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12492
12542
 
12493
12543
  # is this an active Sonos device?
12544
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12494
12545
  if (scDevice is not None) and (scDevice.IsSonos):
12495
12546
 
12496
12547
  _logsi.LogVerbose("Issuing command to Sonos device %s: NEXT" % (scDevice.Title))
@@ -12499,6 +12550,10 @@ class SpotifyClient:
12499
12550
 
12500
12551
  else:
12501
12552
 
12553
+ # was the deviceId resolved?
12554
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12555
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12556
+
12502
12557
  # build spotify web api request parameters.
12503
12558
  urlParms:dict = {}
12504
12559
  if (scDevice is not None):
@@ -12590,10 +12645,11 @@ class SpotifyClient:
12590
12645
  # validations.
12591
12646
  delay = validateDelay(delay, 0.50, 10)
12592
12647
 
12593
- # resolve / activate the device object if needed.
12648
+ # resolve the device object from the device id.
12594
12649
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12595
12650
 
12596
12651
  # is this an active Sonos device?
12652
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12597
12653
  if (scDevice is not None) and (scDevice.IsSonos):
12598
12654
 
12599
12655
  _logsi.LogVerbose("Issuing command to Sonos device %s: PREVIOUS" % (scDevice.Title))
@@ -12602,6 +12658,10 @@ class SpotifyClient:
12602
12658
 
12603
12659
  else:
12604
12660
 
12661
+ # was the deviceId resolved?
12662
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12663
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12664
+
12605
12665
  # build spotify web api request parameters.
12606
12666
  urlParms:dict = {}
12607
12667
  if (scDevice is not None):
@@ -12701,10 +12761,11 @@ class SpotifyClient:
12701
12761
  # validations.
12702
12762
  delay = validateDelay(delay, 0.50, 10)
12703
12763
 
12704
- # resolve / activate the device object if needed.
12764
+ # resolve the device object from the device id.
12705
12765
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12706
12766
 
12707
12767
  # is this an active Sonos device?
12768
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12708
12769
  if (scDevice is not None) and (scDevice.IsSonos):
12709
12770
 
12710
12771
  # get current Spotify Connect player state.
@@ -12730,6 +12791,10 @@ class SpotifyClient:
12730
12791
 
12731
12792
  else:
12732
12793
 
12794
+ # was the deviceId resolved?
12795
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12796
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12797
+
12733
12798
  # build spotify web api request parameters.
12734
12799
  urlParms:dict = \
12735
12800
  {
@@ -12831,10 +12896,11 @@ class SpotifyClient:
12831
12896
  # validations.
12832
12897
  delay = validateDelay(delay, 0.50, 10)
12833
12898
 
12834
- # resolve / activate the device object if needed.
12899
+ # resolve the device object from the device id.
12835
12900
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12836
12901
 
12837
12902
  # is this an active Sonos device?
12903
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12838
12904
  if (scDevice is not None) and (scDevice.IsSonos):
12839
12905
 
12840
12906
  # get current Spotify Connect player state.
@@ -12861,6 +12927,10 @@ class SpotifyClient:
12861
12927
 
12862
12928
  else:
12863
12929
 
12930
+ # was the deviceId resolved?
12931
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
12932
+ self._CheckForDeviceNotFound(scDevice, deviceId)
12933
+
12864
12934
  # build spotify web api request parameters.
12865
12935
  urlParms:dict = \
12866
12936
  {
@@ -12961,10 +13031,11 @@ class SpotifyClient:
12961
13031
  # validations.
12962
13032
  delay = validateDelay(delay, 0.50, 10)
12963
13033
 
12964
- # resolve / activate the device object if needed.
13034
+ # resolve the device object from the device id.
12965
13035
  scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
12966
13036
 
12967
13037
  # is this an active Sonos device?
13038
+ # Sonos device can still be active, even if there is no active device in Spotify playstate.
12968
13039
  if (scDevice is not None) and (scDevice.IsSonos):
12969
13040
 
12970
13041
  _logsi.LogVerbose("Issuing command to Sonos device %s: VOLUME = %s" % (scDevice.Title, volumePercent))
@@ -12973,6 +13044,10 @@ class SpotifyClient:
12973
13044
 
12974
13045
  else:
12975
13046
 
13047
+ # was the deviceId resolved?
13048
+ # if not, then raise an exception as the Spotify Web API request will fail anyway.
13049
+ self._CheckForDeviceNotFound(scDevice, deviceId)
13050
+
12976
13051
  # build spotify web api request parameters.
12977
13052
  urlParms:dict = \
12978
13053
  {
@@ -13129,7 +13204,7 @@ class SpotifyClient:
13129
13204
  if (scDeviceFrom is not None) and (scDeviceFrom.IsSonos):
13130
13205
  self.PlayerMediaPause(scDeviceFrom)
13131
13206
 
13132
- # resolve / activate the device object if needed.
13207
+ # resolve the device object from the device id; activate if it's dormant.
13133
13208
  # for transfer playback, we will always activate the device.
13134
13209
  scDevice = self._ResolveDeviceObject(deviceId, True)
13135
13210
 
@@ -4,7 +4,7 @@ from .spotifyconnectdirectorytask import SpotifyConnectDirectoryTask
4
4
  from .spotifyconnectzeroconfcastapptask import SpotifyConnectZeroconfCastAppTask
5
5
  from .spotifyconnectzeroconfcastcontroller import SpotifyConnectZeroconfCastController
6
6
  from .spotifyconnectzeroconfcastlistener import SpotifyConnectZeroconfCastListener
7
- from .spotifyconnectzeroconfexceptions import SpotifyConnectZeroconfLaunchError, SpotifyConnectZeroconfPlaybackTransferError
7
+ from .spotifyconnectzeroconfexceptions import SpotifyConnectZeroconfLaunchError, SpotifyConnectZeroconfPlaybackTransferError, SpotifyConnectDeviceNotFound
8
8
  from .spotifyconnectzeroconflistener import SpotifyConnectZeroconfListener
9
9
 
10
10
  # all classes to import when "import *" is specified.
@@ -15,5 +15,5 @@ __all__ = [
15
15
  'SpotifyConnectZeroconfCastListener',
16
16
  'SpotifyConnectDirectoryTask',
17
17
  'SpotifyConnectZeroconfListener',
18
- 'SpotifyConnectZeroconfLaunchError', 'SpotifyConnectZeroconfPlaybackTransferError',
18
+ 'SpotifyConnectZeroconfLaunchError', 'SpotifyConnectZeroconfPlaybackTransferError', 'SpotifyConnectDeviceNotFound',
19
19
  ]
@@ -22,8 +22,9 @@ from .spotifyconnectzeroconfcastcontroller import (
22
22
  TYPE_ADD_USER_RESPONSE,
23
23
  TYPE_ADD_USER_ERROR,
24
24
  TYPE_GET_INFO_ERROR,
25
- TYPE_TRANSFER_ERROR_RESPONSE,
26
- TYPE_TRANSFER_SUCCESS_RESPONSE
25
+ TYPE_LAUNCH_ERROR,
26
+ TYPE_TRANSFER_ERROR,
27
+ TYPE_TRANSFER_SUCCESS
27
28
  )
28
29
  # note - cannot reference `SpotifyClient` due to circular import!
29
30
  # from spotifywebapipython import SpotifyClient
@@ -523,7 +524,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
523
524
 
524
525
  # wait for the launched spotify app to activate the user (or fail).
525
526
  # this occurs when we receive any of the following zeroconf response
526
- # messages: `addUserResponse`, `addUserError`, `getInfoError`.
527
+ # messages: `addUserResponse`, `addUserError`, `getInfoError`, `launchError`.
527
528
 
528
529
  counter = 0
529
530
  while counter < (timeoutActivation + 1):
@@ -533,8 +534,9 @@ class SpotifyConnectDirectoryTask(threading.Thread):
533
534
  scDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.GetDeviceByDiscoveryKey(str(castDevice.uuid))
534
535
  if (scDevice is not None):
535
536
  response = scDevice.ZeroconfResponseInfo
536
- if (response.ResponseSource == TYPE_ADD_USER_RESPONSE):
537
- # indicate device was reconnected.
537
+ if (response.ResponseSource == TYPE_LAUNCH_ERROR):
538
+ raise SpotifyApiError("Spotify Cast Application could not be activated on Chromecast device \"%s\": %s" % (deviceName, response.StatusString), logsi=_logsi)
539
+ elif (response.ResponseSource == TYPE_ADD_USER_RESPONSE):
538
540
  scDevice.WasReConnected = True
539
541
  break
540
542
  raise SpotifyApiError("Spotify Cast App could not be activated on Chromecast device \"%s\": %s" % (deviceName, response.ToString(False)), logsi=_logsi)
@@ -567,7 +569,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
567
569
  scDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.GetDeviceByDiscoveryKey(str(castDevice.uuid))
568
570
  if (scDevice is not None):
569
571
  response = scDevice.ZeroconfResponseInfo
570
- if (response.ResponseSource == TYPE_TRANSFER_SUCCESS_RESPONSE):
572
+ if (response.ResponseSource == TYPE_TRANSFER_SUCCESS):
571
573
  break
572
574
  raise SpotifyApiError("Spotify Cast App failed to receive playback transfer on Chromecast device \"%s\": %s" % (deviceName, response.ToString(False)), logsi=_logsi)
573
575
  raise SpotifyApiError("Spotify Cast App failed to receive playback transfer on Chromecast device: unknown error", logsi=_logsi)
@@ -1661,10 +1663,13 @@ class SpotifyConnectDirectoryTask(threading.Thread):
1661
1663
  _logsi.LogObject(SILevel.Debug, "SpotifyConnectDevice info: \"%s\" (ZeroconfResponseInfo)" % (scDevice.Name), scDevice.ZeroconfResponseInfo, excludeNonPublic=True)
1662
1664
 
1663
1665
  # indicate zeroconf response data was received.
1664
- if (response.ResponseSource == TYPE_ADD_USER_RESPONSE) or (response.ResponseSource == TYPE_ADD_USER_ERROR) or (response.ResponseSource == TYPE_GET_INFO_ERROR):
1666
+ if (response.ResponseSource == TYPE_ADD_USER_RESPONSE) or (response.ResponseSource == TYPE_ADD_USER_ERROR) \
1667
+ or (response.ResponseSource == TYPE_GET_INFO_ERROR):
1665
1668
  self.WaitForActivationComplete.set()
1666
- elif (response.ResponseSource == TYPE_TRANSFER_SUCCESS_RESPONSE) or (response.ResponseSource == TYPE_TRANSFER_ERROR_RESPONSE):
1669
+ elif (response.ResponseSource == TYPE_TRANSFER_SUCCESS) or (response.ResponseSource == TYPE_TRANSFER_ERROR):
1667
1670
  self.WaitForTransferComplete.set()
1671
+ elif (response.ResponseSource == TYPE_LAUNCH_ERROR):
1672
+ self.WaitForActivationComplete.set()
1668
1673
 
1669
1674
 
1670
1675
  def OnServiceInfoAddedUpdatedSpotifyConnect(
@@ -4,11 +4,9 @@ import threading
4
4
  import time
5
5
 
6
6
  # our package imports.
7
- from .spotifyconnectzeroconfcastcontroller import SpotifyConnectZeroconfCastController
8
- from .spotifyconnectzeroconfexceptions import SpotifyConnectZeroconfPlaybackTransferError
9
- #from spotifywebapipython import SpotifyClient
10
- from spotifywebapipython.zeroconfapi import ZeroconfGetInfo, ZeroconfResponse
7
+ from .spotifyconnectzeroconfcastcontroller import SpotifyConnectZeroconfCastController, TYPE_LAUNCH_ERROR
11
8
  from spotifywebapipython.spotifywebplayertoken import SpotifyWebPlayerToken
9
+ from spotifywebapipython.zeroconfapi import ZeroconfGetInfo, ZeroconfResponse
12
10
 
13
11
  # get smartinspect logger reference; create a new session for this module name.
14
12
  from smartinspectpython.siauto import SIAuto, SILevel, SISession
@@ -118,42 +116,27 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
118
116
  return self._TransferPlayback
119
117
 
120
118
 
121
- @property
122
- def ZeroconfInfo(self) -> ZeroconfGetInfo:
123
- """
124
- Returns the Chromecast Spotify Connect getinfo response, if the Spotify
125
- application is running on the Chromecast devive; otherwise, None.
126
- """
127
- if (self._SpotifyConnectZeroconfCastController is None):
128
- return None
129
- return self._SpotifyConnectZeroconfCastController.zeroconfGetInfo
130
-
131
-
132
- @property
133
- def ZeroconfResponseObject(self) -> ZeroconfResponse:
134
- """
135
- Returns the Chromecast Spotify Connect zeroconf response, if the Spotify
136
- application returned one; otherwise, None.
119
+ def _CallGetInfoResponseReceivedCallback(
120
+ self,
121
+ zcResponse:ZeroconfGetInfo=None,
122
+ ) -> None:
137
123
  """
138
- if (self._SpotifyConnectZeroconfCastController is None):
139
- return None
140
- return self._SpotifyConnectZeroconfCastController.zeroconfResponse
124
+ Executes the callback to process the zeroconf getInfo response from the cast app task.
141
125
 
142
-
143
- def _CallGetInfoResponseReceivedCallback(self) -> None:
144
- """
145
- If defined, call the callback to process the zeroconf getInfo response from the cast app task.
126
+ Args:
127
+ zcResponse (ZeroconfGetInfo):
128
+ Zeroconf getInfo response object.
146
129
  """
147
- # was a GetInfoResponse callback defined?
130
+ # was the callback defined?
148
131
  if (self._GetInfoResponseReceivedCallback):
149
132
 
150
133
  try:
151
134
 
152
135
  # trace.
153
- _logsi.LogVerbose("%s - Zeroconf getInfoResponse received; Spotify Connect info: \"%s\" (%s)" % (self.name, self.ZeroconfInfo.RemoteName, self.ZeroconfInfo.DeviceId))
136
+ _logsi.LogVerbose("%s - Zeroconf getInfoResponse received; Spotify Connect info: \"%s\" (%s)" % (self.name, zcResponse.RemoteName, zcResponse.DeviceId))
154
137
 
155
138
  # invoke callback to process the GetInfoResponse data.
156
- self._GetInfoResponseReceivedCallback(self._CastDevice.uuid, self.ZeroconfInfo)
139
+ self._GetInfoResponseReceivedCallback(self._CastDevice.uuid, zcResponse)
157
140
 
158
141
  except Exception as ex:
159
142
 
@@ -162,21 +145,27 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
162
145
  #ignore exceptions, as there is nothing we can do about them!
163
146
 
164
147
 
165
-
166
- def _CallZeroconfResponseReceivedCallback(self) -> None:
148
+ def _CallZeroconfResponseReceivedCallback(
149
+ self,
150
+ zcResponse:ZeroconfResponse=None,
151
+ ) -> None:
167
152
  """
168
- If defined, call the callback to process the various zeroconf responses from the cast app task.
153
+ Executes the callback to process the various zeroconf responses from the cast app task.
154
+
155
+ Args:
156
+ zcResponse (ZeroconfResponse):
157
+ Zeroconf response object.
169
158
  """
170
- # was a ZeroconfResponse callback defined?
159
+ # was the callback defined?
171
160
  if (self._ZeroconfResponseReceivedCallback):
172
161
 
173
162
  try:
174
163
 
175
164
  # trace.
176
- _logsi.LogVerbose("%s - Zeroconf \"%s\" received; Spotify Connect response: \"%s\" (%s)" % (self.name, self.ZeroconfResponseObject.ResponseSource, self.ZeroconfResponseObject.StatusString, self.ZeroconfResponseObject.Status))
165
+ _logsi.LogVerbose("%s - Zeroconf \"%s\" received; Spotify Connect response: \"%s\" (%s)" % (self.name, zcResponse.ResponseSource, zcResponse.StatusString, zcResponse.Status))
177
166
 
178
167
  # invoke callback to process the ZeroconfResponse data.
179
- self._ZeroconfResponseReceivedCallback(self._CastDevice.uuid, self.ZeroconfResponseObject)
168
+ self._ZeroconfResponseReceivedCallback(self._CastDevice.uuid, zcResponse)
180
169
 
181
170
  except Exception as ex:
182
171
 
@@ -188,6 +177,9 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
188
177
  def run(self):
189
178
  """
190
179
  The task to perform on a seperate thread.
180
+
181
+ Note that any exceptions must be processed via a call to _PostLaunchErrorEvent, if you
182
+ want the exception to be see by the user; just raising an exception will only log it!
191
183
  """
192
184
  try:
193
185
 
@@ -220,32 +212,24 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
220
212
  # at this point the cast app should be either fully launched, or an error
221
213
  # occured while trying to launch.
222
214
 
223
- # was a ZeroconfResponse callback defined?
224
- if (self._ZeroconfResponseReceivedCallback):
225
-
226
- try:
215
+ # did an error occur?
216
+ if (not self._SpotifyConnectZeroconfCastController.isLaunched):
227
217
 
228
- # at this point, the Spotify Connect `addUser` has been made and the results populated.
229
- _logsi.LogVerbose("%s - Zeroconf ZeroconfResponse received; Spotify Connect response: \"%s\" (%s)" % (self.name, self.ZeroconfResponseObject.StatusString, self.ZeroconfResponseObject.ResponseSource))
218
+ # was it a launchError or getInfoError?
219
+ if (not self._SpotifyConnectZeroconfCastController.isAddUserError):
220
+ self._CallZeroconfResponseReceivedCallback(self._SpotifyConnectZeroconfCastController.zeroconfResponse)
221
+ _logsi.LogException("%s - Failed to launch spotify controller due to timeout" % (self.name), None, logToSystemLogger=False)
222
+ return
230
223
 
231
- # invoke callback to process the ZeroconfResponse data.
232
- self._ZeroconfResponseReceivedCallback(self._CastDevice.uuid, self.ZeroconfResponseObject)
224
+ # if getInfoResponse was received, then execute callback to process the getInfoResponse.
225
+ if (not self._SpotifyConnectZeroconfCastController.isGetInfoError) and (self._SpotifyConnectZeroconfCastController.zeroconfGetInfo is not None):
226
+ self._CallGetInfoResponseReceivedCallback(self._SpotifyConnectZeroconfCastController.zeroconfGetInfo)
233
227
 
234
- except Exception as ex:
235
-
236
- # trace.
237
- _logsi.LogException("An unhandled exception occured in ZeroconfResponseReceivedCallback: %s" % (str(ex)), ex, logToSystemLogger=False)
238
- #ignore exceptions, as there is nothing we can do about them!
239
-
240
- # check for launch or getInfoError errors.
241
- if (not self._SpotifyConnectZeroconfCastController.isLaunched) and (not self._SpotifyConnectZeroconfCastController.isAddUserError):
242
- self._CallZeroconfResponseReceivedCallback()
243
- raise ValueError("%s - Failed to launch spotify controller due to timeout" % (self.name))
244
-
245
- # check for addUserError errors.
246
- if (not self._SpotifyConnectZeroconfCastController.isLaunched) and (self._SpotifyConnectZeroconfCastController.isAddUserError):
247
- self._CallZeroconfResponseReceivedCallback()
248
- raise ValueError("%s - Failed to launch spotify controller due to credentials error" % (self.name))
228
+ # was it a addUserError? if so, then sp_dc / sp_key credentials are probably bad.
229
+ if (self._SpotifyConnectZeroconfCastController.isAddUserError):
230
+ self._CallZeroconfResponseReceivedCallback(self._SpotifyConnectZeroconfCastController.zeroconfResponse)
231
+ _logsi.LogException("%s - Failed to launch spotify controller due to credentials error" % (self.name), None, logToSystemLogger=False)
232
+ return
249
233
 
250
234
  # at this point the cast app launch was successful; the following steps were performed:
251
235
  # - issues a Spotify Connect Zeroconf `getInfo` request to retrieve info about the device.
@@ -258,10 +242,10 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
258
242
  # as well as AFTER the call to `addUser` (no need to call `getInfo` again AFTER addUser).
259
243
 
260
244
  # call the callback to process the getInfoResponse.
261
- self._CallGetInfoResponseReceivedCallback()
245
+ self._CallGetInfoResponseReceivedCallback(self._SpotifyConnectZeroconfCastController.zeroconfGetInfo)
262
246
 
263
247
  # call the callback to process the addUserResponse.
264
- self._CallZeroconfResponseReceivedCallback()
248
+ self._CallZeroconfResponseReceivedCallback(self._SpotifyConnectZeroconfCastController.zeroconfResponse)
265
249
 
266
250
  # trace.
267
251
  _logsi.LogVerbose("%s - User is logged in to Spotify Cast App; waiting for transfer playback ..." % (self.name))
@@ -271,7 +255,8 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
271
255
 
272
256
  # transfer playback to the Chromecast device.
273
257
  _logsi.LogVerbose("%s - Transferring playback for loginId \"%s\"" % (self.name, self.SpotifyClientInstance.SpotifyConnectLoginId))
274
- self.SpotifyClientInstance.PlayerTransferPlayback(self.ZeroconfInfo.DeviceId, play=True, refreshDeviceList=False)
258
+ deviceId:str = self._SpotifyConnectZeroconfCastController.zeroconfGetInfo.DeviceId
259
+ self.SpotifyClientInstance.PlayerTransferPlayback(deviceId, play=True, refreshDeviceList=False)
275
260
 
276
261
  # the cast device will receive a Chromecast message of payload type `transferSuccess`
277
262
  # on successful transfer of playback; this will set the `isPlaybackTransferred` flag to True.
@@ -284,17 +269,19 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
284
269
  while counter < (timeout + 1):
285
270
  if (self._SpotifyConnectZeroconfCastController.waitPlaybackTransfer.wait(1)):
286
271
  if (self._SpotifyConnectZeroconfCastController.isPlaybackTransferError):
287
- raise SpotifyConnectZeroconfPlaybackTransferError("%s - playback transfer error: %s" % (self.name, str(self.ZeroconfResponseObject)))
272
+ self._PostLaunchErrorEvent(1001, "Playback transfer error: %s" % (self._SpotifyConnectZeroconfCastController.zeroconfResponse.StatusString))
273
+ return
288
274
  break
289
275
  if (counter >= timeout):
290
- raise SpotifyConnectZeroconfPlaybackTransferError("%s - Timed out waiting for playback transfer to device" % (self.name))
276
+ self._PostLaunchErrorEvent(1002, "Playback transfer error - Timed out waiting for playback transfer to device")
277
+ return
291
278
  counter += 1
292
279
 
293
280
  # update task status.
294
281
  _logsi.LogVerbose("%s - Transfer Playback complete for loginId \"%s\"" % (self.name, self.SpotifyClientInstance.SpotifyConnectLoginId))
295
282
 
296
283
  # call the callback to process the transferSuccess or transferError.
297
- self._CallZeroconfResponseReceivedCallback()
284
+ self._CallZeroconfResponseReceivedCallback(self._SpotifyConnectZeroconfCastController.zeroconfResponse)
298
285
 
299
286
  # event loop; we have to keep this thread active in order to keep the
300
287
  # Spotify App on the Chromecast device active and available.
@@ -309,20 +296,16 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
309
296
  # trace.
310
297
  _logsi.LogVerbose("%s - Thread task was stopped" % (self.name))
311
298
 
312
- except SpotifyConnectZeroconfPlaybackTransferError as ex:
313
-
314
- # trace.
315
- _logsi.LogError(str(ex))
316
- _logsi.LogVerbose("%s - Thread task is ending due to error" % (self.name))
317
-
318
299
  except Exception as ex:
319
300
 
301
+ # post launchError event with exception details.
302
+ # this will also log a trace message.
303
+ self._PostLaunchErrorEvent(1000, str(ex))
304
+
320
305
  # trace.
321
- _logsi.LogException("%s - Exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
322
306
  _logsi.LogVerbose("%s - Thread task is ending due to exception" % (self.name))
323
-
324
- # raise unhandled exception.
325
- raise
307
+
308
+ #ignore exceptions, as there is nothing we can do about them!
326
309
 
327
310
  finally:
328
311
 
@@ -334,3 +317,29 @@ class SpotifyConnectZeroconfCastAppTask(threading.Thread):
334
317
  self._SpotifyConnectZeroconfCastController.tear_down()
335
318
  except:
336
319
  pass
320
+
321
+
322
+ def _PostLaunchErrorEvent(
323
+ self,
324
+ returnCode:int,
325
+ message:str
326
+ ) -> None:
327
+ """
328
+ Formats a `launchError` Zeroconf Response, and executes the callback.
329
+ """
330
+ try:
331
+
332
+ # trace.
333
+ _logsi.LogError("%s - LaunchError event; (%s) %s" % (self.name, returnCode, message), logToSystemLogger=False)
334
+
335
+ # post launchError event with error details.
336
+ response:ZeroconfResponse = ZeroconfResponse()
337
+ response.ResponseSource = TYPE_LAUNCH_ERROR
338
+ response.Status = returnCode
339
+ response.StatusString = message
340
+ self._CallZeroconfResponseReceivedCallback(response)
341
+
342
+ except:
343
+
344
+ # ignore exceptions, as there is nothing we can do about them.
345
+ pass