spotifywebapiPython 1.0.243__py3-none-any.whl → 1.0.251__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.
- spotifywebapipython/const.py +1 -1
- spotifywebapipython/docs/__init__.html +56 -2
- spotifywebapipython/docs/index.html +5 -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 +235 -4
- 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/models/__init__.py +2 -0
- spotifywebapipython/models/episodesimplified.py +41 -0
- spotifywebapipython/models/imagepalettecolors.py +111 -0
- spotifywebapipython/models/imagevibrantcolors.py +2 -0
- spotifywebapipython/models/pageobject.py +1 -0
- spotifywebapipython/spotifyclient.py +219 -6
- spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py +227 -76
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastcontroller.py +2 -2
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonecontrollerlistener.py +247 -0
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonemanagerlistener.py +384 -0
- spotifywebapipython/vibrant/__init__.py +2 -0
- spotifywebapipython/vibrant/colorthieffast.py +626 -0
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.251.dist-info}/METADATA +3 -2
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.251.dist-info}/RECORD +32 -29
- spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastmultizonelistener.py +0 -212
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.251.dist-info}/LICENSE +0 -0
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.251.dist-info}/WHEEL +0 -0
- {spotifywebapipython-1.0.243.dist-info → spotifywebapipython-1.0.251.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# external package imports.
|
|
2
2
|
from abc import abstractmethod
|
|
3
|
-
from collections.abc import Mapping
|
|
4
3
|
import copy
|
|
5
4
|
from datetime import datetime
|
|
6
5
|
import hashlib
|
|
7
6
|
from pychromecast import CastBrowser, CastInfo, Chromecast, get_chromecast_from_cast_info, get_chromecast_from_host
|
|
8
7
|
from pychromecast.dial import MultizoneInfo, MultizoneStatus, get_multizone_status
|
|
9
|
-
from pychromecast.controllers.multizone import MultizoneController
|
|
8
|
+
from pychromecast.controllers.multizone import MultiZoneManagerListener, MultizoneController, MultizoneManager
|
|
10
9
|
from pychromecast.const import CAST_TYPE_GROUP
|
|
11
10
|
import socket
|
|
12
11
|
from soco import SoCo
|
|
@@ -19,7 +18,8 @@ from zeroconf import Zeroconf, ServiceBrowser
|
|
|
19
18
|
from .spotifyconnectzeroconfexceptions import SpotifyConnectDeviceNotFound
|
|
20
19
|
from .spotifyconnectzeroconfcastapptask import SpotifyConnectZeroconfCastAppTask
|
|
21
20
|
from .spotifyconnectzeroconfcastlistener import SpotifyConnectZeroconfCastListener
|
|
22
|
-
from .
|
|
21
|
+
from .spotifyconnectzeroconfcastmultizonecontrollerlistener import SpotifyConnectZeroconfCastMultiZoneControllerListener
|
|
22
|
+
from .spotifyconnectzeroconfcastmultizonemanagerlistener import SpotifyConnectZeroconfCastMultiZoneManagerListener
|
|
23
23
|
from .spotifyconnectdeviceeventargs import SpotifyConnectDeviceEventArgs
|
|
24
24
|
from .spotifyconnectzeroconflistener import ZEROCONF_SERVICETYPE_SPOTIFYCONNECT, SpotifyConnectZeroconfListener
|
|
25
25
|
from .spotifyconnectzeroconfcastcontroller import (
|
|
@@ -108,6 +108,8 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
108
108
|
self._CastAppTasks:dict[str, SpotifyConnectZeroconfCastAppTask] = {}
|
|
109
109
|
self._CastBrowser:CastBrowser = None
|
|
110
110
|
self._CastMultiZoneControllers:dict[str, MultizoneController] = {}
|
|
111
|
+
self._CastMultiZoneManager:MultizoneManager = None
|
|
112
|
+
self._CastMultiZoneManagerListeners:dict[str, MultiZoneManagerListener] = {}
|
|
111
113
|
self._InitialDiscoveryTimeout = initialDiscoveryTimeout
|
|
112
114
|
self._IsStopRequested:bool = False
|
|
113
115
|
self._SonosPlayers:dict = {}
|
|
@@ -269,6 +271,9 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
269
271
|
handlers=[handler.OnServiceStateChange]
|
|
270
272
|
)
|
|
271
273
|
|
|
274
|
+
# create new chromecast multizone manager that will inform us of multizone events.
|
|
275
|
+
self._CastMultiZoneManager = MultizoneManager()
|
|
276
|
+
|
|
272
277
|
# create new chromecast zeroconf discovery browser instance.
|
|
273
278
|
# this will discover Chromecasts on the local network.
|
|
274
279
|
# when a Chromecast is found, cast_listener.add_cast is called.
|
|
@@ -401,6 +406,107 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
401
406
|
# ignore exceptions, since we are shutting down.
|
|
402
407
|
|
|
403
408
|
|
|
409
|
+
def _TraceMultizoneGroupMembers(
|
|
410
|
+
self,
|
|
411
|
+
group_uuid:str,
|
|
412
|
+
friendly_name:str,
|
|
413
|
+
) -> None:
|
|
414
|
+
"""
|
|
415
|
+
Adds user-friendly multizone group member list to the trace log.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
uuid (str):
|
|
419
|
+
CastInfo uuid of the multizone group.
|
|
420
|
+
friendly_name (str):
|
|
421
|
+
Friendly name of the multizone group.
|
|
422
|
+
"""
|
|
423
|
+
try:
|
|
424
|
+
|
|
425
|
+
# get multizone controller reference for the group.
|
|
426
|
+
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(group_uuid, None)
|
|
427
|
+
if (castMultizoneController is not None):
|
|
428
|
+
|
|
429
|
+
# trace.
|
|
430
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
431
|
+
membersSummary:list[str] = []
|
|
432
|
+
for member in castMultizoneController.members:
|
|
433
|
+
scMemberDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.GetDeviceByDiscoveryKey(member)
|
|
434
|
+
if (scMemberDevice is None):
|
|
435
|
+
membersSummary.append(member)
|
|
436
|
+
else:
|
|
437
|
+
membersSummary.append("%s [ip=%s]" % (scMemberDevice.Title, scMemberDevice.DiscoveryResult.HostIpTitle))
|
|
438
|
+
_logsi.LogArray(SILevel.Verbose, "%s - Chromecast multizone group members for group: \"%s\" (count=%d)" % (self.name, friendly_name, len(membersSummary)), membersSummary, colorValue=SIColors.Coral)
|
|
439
|
+
|
|
440
|
+
except Exception as ex:
|
|
441
|
+
|
|
442
|
+
# trace.
|
|
443
|
+
_logsi.LogException("Chromecast Multizone group members trace exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
444
|
+
# ignore exception, as nothing can be done about it.
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _VerifyCastGroupMultiZoneController(
|
|
448
|
+
self,
|
|
449
|
+
castDevice:Chromecast,
|
|
450
|
+
scDevice:SpotifyConnectDevice,
|
|
451
|
+
) -> None:
|
|
452
|
+
"""
|
|
453
|
+
Verifies that a multizone controller is registered for the the cast group device,
|
|
454
|
+
and that the controller is connected.
|
|
455
|
+
|
|
456
|
+
A new controller will be registered (or re-registered) if necessary.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
castDevice (Chromecast):
|
|
460
|
+
Cast group device to register the controller for.
|
|
461
|
+
scDevice (SpotifyConnectDevice):
|
|
462
|
+
SpotifyConnectDevice instance that contains extended device information.
|
|
463
|
+
|
|
464
|
+
A controller can go inactive if the device drops off the network and is re-attached.
|
|
465
|
+
This will be detected if it happens, and a new controller instantiated.
|
|
466
|
+
"""
|
|
467
|
+
try:
|
|
468
|
+
|
|
469
|
+
# multizone controllers only need to be registered to group devices.
|
|
470
|
+
if (castDevice.cast_type == CAST_TYPE_GROUP):
|
|
471
|
+
|
|
472
|
+
# have we registered a multizone controller for this group yet?
|
|
473
|
+
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(str(castDevice.uuid), None)
|
|
474
|
+
|
|
475
|
+
# is multizone controller still registered, and connected to the network?
|
|
476
|
+
# the controller can go inactive if the device drops off the network and is re-attached.
|
|
477
|
+
if (castMultizoneController) and (castMultizoneController._socket_client is None):
|
|
478
|
+
|
|
479
|
+
# trace
|
|
480
|
+
_logsi.LogVerbose("Chromecast Multizone Controller is no longer active; removing the controller instance so it can be re-created for the device", colorValue=SIColors.Red)
|
|
481
|
+
|
|
482
|
+
# remove inactive multizone controller listener.
|
|
483
|
+
castMultizoneController.tear_down()
|
|
484
|
+
self._CastMultiZoneControllers.pop(str(castDevice.uuid), None)
|
|
485
|
+
castMultizoneController = None
|
|
486
|
+
|
|
487
|
+
# is multizone controller still registered?
|
|
488
|
+
if (castMultizoneController is None):
|
|
489
|
+
|
|
490
|
+
# trace.
|
|
491
|
+
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being added for Cast device: %s [%s]" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), colorValue=SIColors.Lavender)
|
|
492
|
+
|
|
493
|
+
# add cast device to multizone controller instance.
|
|
494
|
+
# this will add a listener for multizone status events to be processed.
|
|
495
|
+
castMultizoneController = MultizoneController(castDevice.uuid)
|
|
496
|
+
castMultizoneController.register_listener(SpotifyConnectZeroconfCastMultiZoneControllerListener(self, self._ZeroconfInstance, self._Zeroconf_RLock, castMultizoneController, castDevice))
|
|
497
|
+
castDevice.register_handler(castMultizoneController)
|
|
498
|
+
self._CastMultiZoneControllers[str(castDevice.uuid)] = castMultizoneController
|
|
499
|
+
|
|
500
|
+
# request an initial status update.
|
|
501
|
+
castMultizoneController.update_members()
|
|
502
|
+
|
|
503
|
+
except Exception as ex:
|
|
504
|
+
|
|
505
|
+
# trace.
|
|
506
|
+
_logsi.LogException("Chromecast Multizone verify controller exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
507
|
+
# ignore exception, as nothing can be done about it.
|
|
508
|
+
|
|
509
|
+
|
|
404
510
|
def ActivateCastAppSpotify(
|
|
405
511
|
self,
|
|
406
512
|
deviceName:str,
|
|
@@ -530,6 +636,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
530
636
|
scDevice.ZeroconfResponseInfo = ZeroconfResponse()
|
|
531
637
|
|
|
532
638
|
castDevice:Chromecast = None
|
|
639
|
+
deviceWaitTimeoutSecs:float = 20.0
|
|
533
640
|
|
|
534
641
|
# is this a cast group?
|
|
535
642
|
if (castInfo.cast_type == CAST_TYPE_GROUP):
|
|
@@ -544,46 +651,66 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
544
651
|
# current group leader host info for each group. we will then create a cast device to
|
|
545
652
|
# the group leader host address, and start the SpotifyCastApp on that specific host.
|
|
546
653
|
|
|
547
|
-
#
|
|
548
|
-
|
|
549
|
-
|
|
654
|
+
# trace.
|
|
655
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
656
|
+
self._TraceMultizoneGroupMembers(str(castInfo.uuid), castInfo.friendly_name)
|
|
550
657
|
|
|
551
658
|
# default host ip and port in case we don't find the group coordinator.
|
|
552
659
|
groupHost:str = scDevice.DiscoveryResult.HostIpAddress
|
|
553
660
|
groupPort:int = scDevice.DiscoveryResult.HostIpPort
|
|
554
661
|
|
|
555
|
-
|
|
556
|
-
zoneInfo:MultizoneInfo = None
|
|
557
|
-
for zoneInfo in castMultiZoneStatus.groups:
|
|
662
|
+
try:
|
|
558
663
|
|
|
559
|
-
#
|
|
560
|
-
|
|
664
|
+
# request a multizone group member update.
|
|
665
|
+
# note that this will just send a request to the cast group device for its
|
|
666
|
+
# status, which will then update the members list via event listener callbacks.
|
|
667
|
+
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(str(castInfo.uuid), None)
|
|
668
|
+
if (castMultizoneController) and (castMultizoneController._socket_client):
|
|
669
|
+
castMultizoneController.update_members()
|
|
561
670
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
groupPort = scDevice.DiscoveryResult.HostIpPort
|
|
568
|
-
break
|
|
671
|
+
except Exception as ex:
|
|
672
|
+
|
|
673
|
+
# trace.
|
|
674
|
+
_logsi.LogException("Chromecast Multizone group update_members exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
675
|
+
# ignore exception, as nothing can be done about it.
|
|
569
676
|
|
|
570
|
-
#
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
677
|
+
# get current multizone status for all groups.
|
|
678
|
+
castMultiZoneStatus:MultizoneStatus = get_multizone_status(castInfo.host, castInfo.services, self.ZeroconfInstance, 5)
|
|
679
|
+
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast group multizone status for device: %s [ip=%s:%s]" % (self.name, scDevice.Title, castInfo.host, castInfo.port), castMultiZoneStatus, colorValue=SIColors.Coral)
|
|
680
|
+
|
|
681
|
+
# if group device dropped off the network, then we can't query it!
|
|
682
|
+
if (castMultiZoneStatus is not None):
|
|
576
683
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
groupCount:int = len(castMultiZoneStatus.groups)
|
|
581
|
-
if (groupCount > 1):
|
|
582
|
-
groupWaitTimeoutSecs = (groupCount * 7.5)
|
|
684
|
+
# find the group coordinator for the selected device.
|
|
685
|
+
zoneInfo:MultizoneInfo = None
|
|
686
|
+
for zoneInfo in castMultiZoneStatus.groups:
|
|
583
687
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
688
|
+
# is this our group?
|
|
689
|
+
if (str(zoneInfo.uuid) == scDevice.DiscoveryResult.Key):
|
|
690
|
+
|
|
691
|
+
# yes - get host info to use for the device.
|
|
692
|
+
# we will use the device discovery result HostIpAddress instead of the zoneInfo.host
|
|
693
|
+
# value here, as the zoneInfo MAY contain an IPV6 formatted address, which was causing
|
|
694
|
+
# issues if the device was originally activated using an IPV4 address!
|
|
695
|
+
groupHost = scDevice.DiscoveryResult.HostIpAddress
|
|
696
|
+
groupPort = scDevice.DiscoveryResult.HostIpPort
|
|
697
|
+
break
|
|
698
|
+
|
|
699
|
+
# connect to the device and build a Chromecast instance from group coordinator host info.
|
|
700
|
+
castDevice = get_chromecast_from_host(
|
|
701
|
+
host=(groupHost, groupPort, castInfo.uuid, castInfo.model_name, castInfo.friendly_name),
|
|
702
|
+
tries=2,
|
|
703
|
+
retry_wait=0.5,
|
|
704
|
+
timeout=10)
|
|
705
|
+
|
|
706
|
+
# calculate wait timeout for devices to become active, based on number of devices in group.
|
|
707
|
+
if (castMultiZoneStatus.groups is not None):
|
|
708
|
+
groupCount:int = len(castMultiZoneStatus.groups)
|
|
709
|
+
if (groupCount > 1):
|
|
710
|
+
deviceWaitTimeoutSecs = (groupCount * 5.0)
|
|
711
|
+
|
|
712
|
+
# trace.
|
|
713
|
+
_logsi.LogVerbose("%s - Waiting %d seconds max for Chromecast group multizone device to activate: %s [ip=%s:%s]" % (self.name, deviceWaitTimeoutSecs, scDevice.Title, groupHost, groupPort), colorValue=SIColors.Coral)
|
|
587
714
|
|
|
588
715
|
else:
|
|
589
716
|
|
|
@@ -595,15 +722,29 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
595
722
|
zconf=self._ZeroconfInstance,
|
|
596
723
|
tries=2,
|
|
597
724
|
retry_wait=0.5,
|
|
598
|
-
timeout=
|
|
725
|
+
timeout=5)
|
|
726
|
+
|
|
727
|
+
# trace.
|
|
728
|
+
_logsi.LogVerbose("%s - Waiting %d seconds max for Chromecast device to activate: %s [ip=%s:%s]" % (self.name, deviceWaitTimeoutSecs, scDevice.Title, castInfo.host, castInfo.port))
|
|
599
729
|
|
|
600
|
-
|
|
601
|
-
|
|
730
|
+
# were we able to obtain the device reference? if not, then it's an error!
|
|
731
|
+
# this can happen if the device falls off the network and tries to be activated
|
|
732
|
+
# again before zeroconf detects that it was removed.
|
|
733
|
+
if (castDevice is None):
|
|
734
|
+
raise SpotifyApiError("Chromecast device %s [ip=%s] reference could not be obtained; check to ensure it is still available on the network" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), logsi=_logsi)
|
|
735
|
+
|
|
736
|
+
# wait for the device to become ready.
|
|
737
|
+
# this will raise an exception if the device is not available.
|
|
738
|
+
castDevice.wait(deviceWaitTimeoutSecs)
|
|
602
739
|
|
|
603
740
|
# trace.
|
|
604
741
|
if (_logsi.IsOn(SILevel.Verbose)):
|
|
605
742
|
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast device object: \"%s\" (ip=%s:%s)" % (self.name, castInfo.friendly_name, castDevice.socket_client.host, castDevice.socket_client.port), castDevice)
|
|
606
|
-
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast device status: %s [ip=%s:%s]" % (self.name, scDevice.Title, castDevice.socket_client.host, castDevice.socket_client.port), castDevice.status
|
|
743
|
+
_logsi.LogObject(SILevel.Verbose, "%s - Chromecast device status: %s [ip=%s:%s]" % (self.name, scDevice.Title, castDevice.socket_client.host, castDevice.socket_client.port), castDevice.status)
|
|
744
|
+
self._TraceMultizoneGroupMembers(str(castInfo.uuid), castInfo.friendly_name)
|
|
745
|
+
|
|
746
|
+
# verify multizone controller instance is registered for cast group device.
|
|
747
|
+
self._VerifyCastGroupMultiZoneController(castDevice, scDevice)
|
|
607
748
|
|
|
608
749
|
# start the Spotify Cast application on the Chromecast device.
|
|
609
750
|
# this will start the spotify app on the cast device.
|
|
@@ -655,6 +796,10 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
655
796
|
# at this point we have received an `adduserResponse` from the Chromecast device, indicating
|
|
656
797
|
# that it has launched the Spotify Cast App and that the user is logged in.
|
|
657
798
|
|
|
799
|
+
# trace.
|
|
800
|
+
if (_logsi.IsOn(SILevel.Verbose)):
|
|
801
|
+
self._TraceMultizoneGroupMembers(str(castInfo.uuid), castInfo.friendly_name)
|
|
802
|
+
|
|
658
803
|
# was transfer playback specified?
|
|
659
804
|
if (transferPlayback == True):
|
|
660
805
|
|
|
@@ -1675,6 +1820,19 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1675
1820
|
# remove the dynamic device from the collection.
|
|
1676
1821
|
self.RemoveDevice(spDynamicDevice.Id, dynamicDeviceOnly=True)
|
|
1677
1822
|
|
|
1823
|
+
try:
|
|
1824
|
+
|
|
1825
|
+
# register the multizone manager listener with each Chromecast non-group device.
|
|
1826
|
+
if (castInfo.cast_type != CAST_TYPE_GROUP):
|
|
1827
|
+
_logsi.LogVerbose("Chromecast Multizone Manager Listener is being added for non-group device: \"%s\"" % (castInfo.friendly_name))
|
|
1828
|
+
castMultiZoneManagerListener = SpotifyConnectZeroconfCastMultiZoneManagerListener(self, self._ZeroconfInstance, self._Zeroconf_RLock, self._CastMultiZoneManager, castDevice)
|
|
1829
|
+
self._CastMultiZoneManagerListeners[str(castDevice.uuid)] = castMultiZoneManagerListener
|
|
1830
|
+
self._CastMultiZoneManager.register_listener(castDevice.uuid, castMultiZoneManagerListener)
|
|
1831
|
+
|
|
1832
|
+
except Exception as ex:
|
|
1833
|
+
|
|
1834
|
+
_logsi.LogException("%s - MultiZone Manager register_listener Exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
|
|
1835
|
+
|
|
1678
1836
|
# is this a Google Cast Group?
|
|
1679
1837
|
if (castInfo.cast_type == CAST_TYPE_GROUP):
|
|
1680
1838
|
|
|
@@ -1698,6 +1856,14 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1698
1856
|
# RemoteName = "Kitchen Parlor"
|
|
1699
1857
|
# DeviceID = 3fb9b12379e4fa1957d065b4c4568939
|
|
1700
1858
|
|
|
1859
|
+
# verify multizone controller instance is registered for cast group device.
|
|
1860
|
+
self._VerifyCastGroupMultiZoneController(castDevice, scDevice)
|
|
1861
|
+
|
|
1862
|
+
# add group to multizone manager.
|
|
1863
|
+
# make sure we do this AFTER adding the MultizoneController listener.
|
|
1864
|
+
_logsi.LogVerbose("ChromeCast Multizone Manager is adding multizone group: \"%s\"" % (castDevice.cast_info.friendly_name))
|
|
1865
|
+
self._CastMultiZoneManager.add_multizone(castDevice)
|
|
1866
|
+
|
|
1701
1867
|
# ignore "update_cast" entries, as we don't want to create duplicates.
|
|
1702
1868
|
if (serviceType == "update_cast"):
|
|
1703
1869
|
_logsi.LogVerbose("SpotifyConnectDevice instance will be ignored for Google Cast Group data: \"%s\" (%s)" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name))
|
|
@@ -1716,36 +1882,6 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1716
1882
|
self._SpotifyConnectDevices.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
1717
1883
|
return
|
|
1718
1884
|
|
|
1719
|
-
# register a multizone listener to listen for group change events; these
|
|
1720
|
-
# events will (hopefully) help us to determine the leader of a group.
|
|
1721
|
-
try:
|
|
1722
|
-
|
|
1723
|
-
# is this an add_cast event?
|
|
1724
|
-
if (serviceType == "add_cast"):
|
|
1725
|
-
|
|
1726
|
-
# do we have a multizone controller registered for this device yet?
|
|
1727
|
-
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(scDevice.DiscoveryResult.Key, None)
|
|
1728
|
-
if (castMultizoneController is None):
|
|
1729
|
-
|
|
1730
|
-
# trace.
|
|
1731
|
-
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being added for Cast device: %s [%s]" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), colorValue=SIColors.Lavender)
|
|
1732
|
-
|
|
1733
|
-
# add cast device to multizone controller instance. this will add a listener
|
|
1734
|
-
# for multizone status events to be processed, which allows us to keep track
|
|
1735
|
-
# of which device is a group leader.
|
|
1736
|
-
castMultizoneController = MultizoneController(castDevice.uuid)
|
|
1737
|
-
castMultizoneController.register_listener(SpotifyConnectZeroconfCastMultiZoneListener(self, self._ZeroconfInstance, self._Zeroconf_RLock, castMultizoneController, castDevice))
|
|
1738
|
-
castDevice.register_handler(castMultizoneController)
|
|
1739
|
-
castMultizoneController.update_members() # Request an initial status
|
|
1740
|
-
|
|
1741
|
-
# add cast multizone controller to active multizone controllers collection.
|
|
1742
|
-
self._CastMultiZoneControllers[str(castDevice.uuid)] = castMultizoneController
|
|
1743
|
-
|
|
1744
|
-
except Exception as ex:
|
|
1745
|
-
|
|
1746
|
-
# trace.
|
|
1747
|
-
_logsi.LogException("%s - MultiZoneController register_listener Exception: %s" % (self.name, str(ex)), ex, logToSystemLogger=False)
|
|
1748
|
-
|
|
1749
1885
|
# add new Spotify Connect Device to devices collection.
|
|
1750
1886
|
self._SpotifyConnectDevices.Items.append(scDevice)
|
|
1751
1887
|
self._SpotifyConnectDevices.DateLastRefreshed = datetime.utcnow().timestamp()
|
|
@@ -1779,6 +1915,7 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1779
1915
|
|
|
1780
1916
|
# did the device name change? if so, then update name and id properties,
|
|
1781
1917
|
# as well as the corresponding getInfo properties.
|
|
1918
|
+
# note that the discovery key value does not change!
|
|
1782
1919
|
deviceNameChanged:bool = False
|
|
1783
1920
|
if (scDevice.Name != zeroconfDiscoveryResult.DeviceName):
|
|
1784
1921
|
|
|
@@ -1899,26 +2036,40 @@ class SpotifyConnectDirectoryTask(threading.Thread):
|
|
|
1899
2036
|
|
|
1900
2037
|
else:
|
|
1901
2038
|
|
|
1902
|
-
#
|
|
2039
|
+
# get device information.
|
|
2040
|
+
scDevice:SpotifyConnectDevice = self._SpotifyConnectDevices.Items[idx]
|
|
2041
|
+
|
|
1903
2042
|
try:
|
|
1904
2043
|
|
|
1905
|
-
#
|
|
2044
|
+
# unregister multizone controller listener for the device.
|
|
1906
2045
|
castMultizoneController:MultizoneController = self._CastMultiZoneControllers.get(zeroconfDiscoveryResult.Key, None)
|
|
1907
2046
|
if (castMultizoneController is not None):
|
|
2047
|
+
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being removed for Cast device: \"%s\" (%s) [%s]" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name, zeroconfDiscoveryResult.Key), colorValue=SIColors.Lavender)
|
|
2048
|
+
castMultizoneController.tear_down()
|
|
2049
|
+
self._CastMultiZoneControllers.pop(zeroconfDiscoveryResult.Key, None)
|
|
1908
2050
|
|
|
1909
|
-
|
|
1910
|
-
_logsi.LogVerbose("Chromecast Multizone Controller Listener is being removed: \"%s\" (%s) [%s]" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name, zeroconfDiscoveryResult.Key), colorValue=SIColors.Lavender)
|
|
2051
|
+
except Exception as ex:
|
|
1911
2052
|
|
|
1912
|
-
|
|
1913
|
-
|
|
2053
|
+
# trace.
|
|
2054
|
+
_logsi.LogException("Chromecast MultiZoneController tear_down Exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
1914
2055
|
|
|
1915
|
-
|
|
1916
|
-
|
|
2056
|
+
try:
|
|
2057
|
+
|
|
2058
|
+
# remove group from multizone manager.
|
|
2059
|
+
# make sure we do this AFTER removing the MultizoneController listener.
|
|
2060
|
+
_logsi.LogVerbose("Chromecast Multizone Manager is removing multizone device: \"%s\"" % (scDevice.Title))
|
|
2061
|
+
self._CastMultiZoneManager.remove_multizone(scDevice.DiscoveryResult.Key)
|
|
2062
|
+
|
|
2063
|
+
# deregister multizone manager listener for the device.
|
|
2064
|
+
_logsi.LogVerbose("Chromecast Multizone Manager Listener is deregistering for device: \"%s\"" % (scDevice.Title))
|
|
2065
|
+
castMultiZoneManagerListener = self._CastMultiZoneManagerListeners.pop(scDevice.DiscoveryResult.Key, None)
|
|
2066
|
+
if castMultiZoneManagerListener:
|
|
2067
|
+
self._CastMultiZoneManager.deregister_listener(scDevice.DiscoveryResult.Key, castMultiZoneManagerListener)
|
|
1917
2068
|
|
|
1918
2069
|
except Exception as ex:
|
|
1919
2070
|
|
|
1920
2071
|
# trace.
|
|
1921
|
-
_logsi.LogException("
|
|
2072
|
+
_logsi.LogException("Chromecast MultizoneManager deregister_listener Exception: %s" % (str(ex)), ex, logToSystemLogger=False)
|
|
1922
2073
|
|
|
1923
2074
|
# trace.
|
|
1924
2075
|
_logsi.LogVerbose("Removing existing SpotifyConnectDevice instance from CastInfo data: \"%s\" (%s) [%s]" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name, zeroconfDiscoveryResult.Key))
|
|
@@ -151,11 +151,11 @@ class SpotifyConnectZeroconfCastController(BaseController):
|
|
|
151
151
|
"""
|
|
152
152
|
Callback function
|
|
153
153
|
"""
|
|
154
|
-
# for cast groups, we will wait a
|
|
154
|
+
# for cast groups, we will wait a few seconds for any cast messages to be
|
|
155
155
|
# processed, before we query the device for Spotify Connect getInfo.
|
|
156
156
|
if (self.castDevice.cast_type == CAST_TYPE_GROUP):
|
|
157
157
|
delay:float = 3.0
|
|
158
|
-
_logsi.LogVerbose("Cast controller is for a Cast Group request; delaying processing for %s seconds to allow cast zeroconf responses to be processed" % (str(delay)), colorValue=SIColors.
|
|
158
|
+
_logsi.LogVerbose("Cast controller is for a Cast Group request; delaying processing for %s seconds to allow cast zeroconf responses to be processed" % (str(delay)), colorValue=SIColors.Coral)
|
|
159
159
|
time.sleep(delay)
|
|
160
160
|
|
|
161
161
|
# send the initial getInfo request after launching the app.
|