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.
- {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/METADATA +1 -1
- {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/RECORD +25 -25
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +14 -2
- spotifywebapipython/docs/index.html +2 -2
- spotifywebapipython/docs/search.js +1 -1
- spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
- spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html +2 -2
- spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html +2 -2
- spotifywebapipython/spotifyclient.py +94 -19
- spotifywebapipython/spotifyconnect/__init__.py +2 -2
- spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +13 -8
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastapptask.py +84 -75
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastcontroller.py +14 -12
- {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/LICENSE +0 -0
- {spotifywebapiPython-1.0.155.dist-info → spotifywebapiPython-1.0.157.dist-info}/WHEEL +0 -0
- {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
|
|
1253
|
+
# resolve the device object from the device id.
|
|
1235
1254
|
scDevice:SpotifyConnectDevice = self._ResolveDeviceObject(deviceId, False)
|
|
1236
1255
|
|
|
1237
|
-
# is this
|
|
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
|
|
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
|
|
11732
|
-
scDevice = self._ResolveDeviceObject(deviceId,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
12232
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
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 ==
|
|
537
|
-
|
|
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 ==
|
|
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)
|
|
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 ==
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
148
|
+
def _CallZeroconfResponseReceivedCallback(
|
|
149
|
+
self,
|
|
150
|
+
zcResponse:ZeroconfResponse=None,
|
|
151
|
+
) -> None:
|
|
167
152
|
"""
|
|
168
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
#
|
|
224
|
-
if (self.
|
|
225
|
-
|
|
226
|
-
try:
|
|
215
|
+
# did an error occur?
|
|
216
|
+
if (not self._SpotifyConnectZeroconfCastController.isLaunched):
|
|
227
217
|
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
_logsi.LogException("
|
|
238
|
-
|
|
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.
|
|
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
|
-
|
|
272
|
+
self._PostLaunchErrorEvent(1001, "Playback transfer error: %s" % (self._SpotifyConnectZeroconfCastController.zeroconfResponse.StatusString))
|
|
273
|
+
return
|
|
288
274
|
break
|
|
289
275
|
if (counter >= timeout):
|
|
290
|
-
|
|
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
|
-
#
|
|
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
|