spotifywebapiPython 1.0.290__py3-none-any.whl → 1.0.292__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.
Files changed (25) hide show
  1. spotifywebapipython/const.py +1 -1
  2. spotifywebapipython/docs/__init__.html +8 -2
  3. spotifywebapipython/docs/index.html +2 -2
  4. spotifywebapipython/docs/search.js +1 -1
  5. spotifywebapipython/docs/spotifywebapipython/const.html +3 -3
  6. spotifywebapipython/docs/spotifywebapipython/saappmessages.html +2 -2
  7. spotifywebapipython/docs/spotifywebapipython/sautils.html +2 -2
  8. spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html +2 -2
  9. spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html +2 -2
  10. spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html +2 -2
  11. spotifywebapipython/docs/spotifywebapipython/spotifyclient.html +2 -2
  12. spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html +2 -2
  13. spotifywebapipython/docs/spotifywebapipython/spotifymediatypes.html +2 -2
  14. spotifywebapipython/docs/spotifywebapipython/spotifytypeprefixes.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/spotifyconnect/spotifyconnectdirectorytask.py +84 -23
  19. spotifywebapipython/zeroconfapi/zeroconfconnect.py +165 -9
  20. spotifywebapipython/zeroconfapi/zeroconfgetinfo.py +26 -0
  21. {spotifywebapipython-1.0.290.dist-info → spotifywebapipython-1.0.292.dist-info}/METADATA +1 -1
  22. {spotifywebapipython-1.0.290.dist-info → spotifywebapipython-1.0.292.dist-info}/RECORD +25 -25
  23. {spotifywebapipython-1.0.290.dist-info → spotifywebapipython-1.0.292.dist-info}/LICENSE +0 -0
  24. {spotifywebapipython-1.0.290.dist-info → spotifywebapipython-1.0.292.dist-info}/WHEEL +0 -0
  25. {spotifywebapipython-1.0.290.dist-info → spotifywebapipython-1.0.292.dist-info}/top_level.txt +0 -0
@@ -2312,6 +2312,8 @@ class SpotifyConnectDirectoryTask(threading.Thread):
2312
2312
  # syncronize access via lock, as we are accessing the collection.
2313
2313
  with self._SpotifyConnectDevices_RLock:
2314
2314
 
2315
+ zconn:ZeroconfConnect = None
2316
+
2315
2317
  try:
2316
2318
 
2317
2319
  # trace.
@@ -2374,14 +2376,12 @@ class SpotifyConnectDirectoryTask(threading.Thread):
2374
2376
  # retrieve initial spotify connect device information (by ip address).
2375
2377
  scDevice.DeviceInfo = zconn.GetInformation()
2376
2378
  scDevice.Id = scDevice.DeviceInfo.DeviceId
2377
- scDevice.Name = scDevice.DeviceInfo.RemoteName
2379
+ scDevice.Name = scDevice.DeviceInfo.RemoteNameOrAlias
2378
2380
 
2379
- # if remote name was not specified, then set device name to first alias name.
2380
- # note that we will not reset the RemoteName, as the "" value indicates an alias is in use.
2381
- if ((scDevice.DeviceInfo.RemoteName + "").strip() == ""):
2382
- if (scDevice.DeviceInfo.HasAliases):
2383
- _logsi.LogVerbose("Spotify Connect Zeroconf GetInformation alias name will be utilized for Zeroconf Discovery Result: \"%s\" (%s)" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name))
2384
- scDevice.Name = scDevice.DeviceInfo.Aliases[0].Name
2381
+ # if service discovery version not set, then use the getInfo version.
2382
+ if (zeroconfDiscoveryResult.SpotifyConnectVersion is None):
2383
+ if (scDevice.DeviceInfo.Version is not None):
2384
+ zeroconfDiscoveryResult.SpotifyConnectVersion = scDevice.DeviceInfo.Version
2385
2385
 
2386
2386
  except Exception as ex:
2387
2387
 
@@ -2408,14 +2408,12 @@ class SpotifyConnectDirectoryTask(threading.Thread):
2408
2408
  # retrieve initial spotify connect device information (by dns alias).
2409
2409
  scDevice.DeviceInfo = zconn.GetInformation()
2410
2410
  scDevice.Id = scDevice.DeviceInfo.DeviceId
2411
- scDevice.Name = scDevice.DeviceInfo.RemoteName
2411
+ scDevice.Name = scDevice.DeviceInfo.RemoteNameOrAlias
2412
2412
 
2413
- # if remote name was not specified, then set device name to first alias name.
2414
- # note that we will not reset the RemoteName, as the "" value indicates an alias is in use.
2415
- if ((scDevice.DeviceInfo.RemoteName + "").strip() == ""):
2416
- if (scDevice.DeviceInfo.HasAliases):
2417
- _logsi.LogVerbose("Spotify Connect Zeroconf GetInformation alias name will be utilized for Zeroconf Discovery Result: \"%s\" (%s)" % (zeroconfDiscoveryResult.DeviceName, zeroconfDiscoveryResult.Name))
2418
- scDevice.Name = scDevice.DeviceInfo.Aliases[0].Name
2413
+ # if service discovery version not set, then use the getInfo version.
2414
+ if (zeroconfDiscoveryResult.SpotifyConnectVersion is None):
2415
+ if (scDevice.DeviceInfo.Version is not None):
2416
+ zeroconfDiscoveryResult.SpotifyConnectVersion = scDevice.DeviceInfo.Version
2419
2417
 
2420
2418
  # update HostIpAddress in discovery result so it knows to use the dns alias
2421
2419
  # instead of the ip address.
@@ -2516,25 +2514,84 @@ class SpotifyConnectDirectoryTask(threading.Thread):
2516
2514
  scDevice = self._SpotifyConnectDevices.Items[idx]
2517
2515
 
2518
2516
  # were any changes made to the zeroconf discovery results?
2517
+ # this can happen when the Spotify Connect zeroconf server on the device has
2518
+ # been restarted (e.g. "resetUsers" called, etc). if so, then we need to update
2519
+ # the DiscoveryResult since it could possibly point to a different host address
2520
+ # (or port number) for the zeroconf endpoints (e.g. getInfo, addUser, etc). we will
2521
+ # also call "getInfo" to update any Spotify Connect changes (e.g. RemoteName, Id, etc).
2519
2522
  if (not scDevice.DiscoveryResult.Equals(zeroconfDiscoveryResult)):
2520
2523
 
2521
2524
  # trace.
2525
+ _logsi.LogVerbose("Spotify Connect zeroconf DiscoveryResult change detected for device: %s" % (scDevice.Title))
2522
2526
  _logsi.LogObject(SILevel.Debug, "SpotifyConnectDevice info: \"%s\" (OLD DeviceInfo / getInfo)" % (scDevice.Title), scDevice.DeviceInfo, excludeNonPublic=True)
2523
2527
  _logsi.LogObject(SILevel.Debug, "SpotifyConnectDevice info: \"%s\" (OLD DiscoveryResult) [%s]" % (scDevice.Title, scDevice.DiscoveryResult.HostIpTitle), scDevice.DiscoveryResult, excludeNonPublic=True)
2524
2528
 
2525
- # set zeroconf discovery result properties.
2529
+ # preserve various old property values for comparison later.
2530
+ oldDeviceId:str = scDevice.DeviceInfo.DeviceId
2531
+ oldRemoteName:str = scDevice.DeviceInfo.RemoteNameOrAlias
2532
+ oldHostIpPort:int = scDevice.DiscoveryResult.HostIpPort
2533
+ newRemoteName:str = None
2534
+ newDeviceId:str = None
2535
+
2536
+ try:
2537
+
2538
+ # trace.
2539
+ _logsi.LogVerbose("Retrieving Spotify Connect device information: %s [DiscoveryResult change]" % (zeroconfDiscoveryResult.Id))
2540
+
2541
+ # create connection object to retrieve spotify connect device information (via direct ip address).
2542
+ zconn:ZeroconfConnect = ZeroconfConnect(
2543
+ zeroconfDiscoveryResult.HostIpAddress,
2544
+ zeroconfDiscoveryResult.HostIpPort,
2545
+ zeroconfDiscoveryResult.SpotifyConnectCPath,
2546
+ zeroconfDiscoveryResult.SpotifyConnectVersion,
2547
+ useSSL=False,
2548
+ tokenStorageDir=self.SpotifyClientInstance.TokenStorageDir,
2549
+ tokenStorageFile=self.SpotifyClientInstance.TokenStorageFile
2550
+ )
2551
+
2552
+ # retrieve spotify connect device information (by ip address).
2553
+ getInfo:ZeroconfGetInfo = zconn.GetInformation()
2554
+
2555
+ # if service discovery version not set, then use the getInfo version.
2556
+ if (zeroconfDiscoveryResult.SpotifyConnectVersion is None):
2557
+ if (getInfo.Version is not None):
2558
+ zeroconfDiscoveryResult.SpotifyConnectVersion = getInfo.Version
2559
+
2560
+ # update base device info from getInfo details.
2561
+ scDevice.DeviceInfo = getInfo
2562
+
2563
+ # set device name and id values from getInfo response values.
2564
+ newRemoteName = getInfo.RemoteNameOrAlias
2565
+ newDeviceId = getInfo.DeviceId
2566
+
2567
+ except Exception as ex:
2568
+
2569
+ # trace.
2570
+ _logsi.LogVerbose("Could not retrieve Spotify Connect device information; defaulting DeviceID to GetSpotifyDeviceIDFromName \"%s\"" % (zeroconfDiscoveryResult.DeviceName))
2571
+
2572
+ # at this point we don't know for sure what the device name is since getInfo failed.
2573
+ # set device name and id values from serviceinfo discovery values.
2574
+ newRemoteName:str = zeroconfDiscoveryResult.DeviceName
2575
+ newDeviceId:str = self.GetSpotifyDeviceIDFromName(newRemoteName)
2576
+
2577
+ # update zeroconf discovery result properties in the base device object.
2526
2578
  scDevice.DiscoveryResult = zeroconfDiscoveryResult
2527
2579
 
2528
- # did the device name change? if so, then update name and id properties,
2529
- # as well as the corresponding getInfo properties.
2530
- if (scDevice.Name != zeroconfDiscoveryResult.DeviceName):
2531
- newDeviceName:str = zeroconfDiscoveryResult.DeviceName
2532
- newDeviceId:str = self.GetSpotifyDeviceIDFromName(zeroconfDiscoveryResult.DeviceName)
2533
- _logsi.LogVerbose("Spotify Connect Zeroconf SpotifyConnectDevice entry name and id changed from %s to \"%s\" (%s)" % (scDevice.Title, newDeviceName, newDeviceId))
2534
- scDevice.Name = newDeviceName
2580
+ # did the device remote name change? if so, then update base device properties.
2581
+ if (oldRemoteName != newRemoteName):
2582
+ _logsi.LogVerbose("Spotify Connect Zeroconf device RemoteNameAlias changed from \"%s\" to \"%s\"" % (oldRemoteName, newRemoteName))
2583
+ scDevice.Name = newRemoteName
2584
+ scDevice.DeviceInfo.RemoteName = newRemoteName
2585
+
2586
+ # did the device id change?
2587
+ if (oldDeviceId != newDeviceId):
2588
+ _logsi.LogVerbose("Spotify Connect Zeroconf device Id changed from \"%s\" to \"%s\"" % (oldDeviceId, newDeviceId))
2535
2589
  scDevice.Id = newDeviceId
2536
2590
  scDevice.DeviceInfo.DeviceId = newDeviceId
2537
- scDevice.DeviceInfo.RemoteName = newDeviceName
2591
+
2592
+ # did the device zeroconf server listener port change?
2593
+ if (oldHostIpPort != zeroconfDiscoveryResult.HostIpPort):
2594
+ _logsi.LogVerbose("Spotify Connect Zeroconf device HostIpPort changed from \"%s\" to \"%s\"" % (oldHostIpPort, zeroconfDiscoveryResult.HostIpPort))
2538
2595
 
2539
2596
  # update existing Spotify Connect Device in devices collection.
2540
2597
  self._SpotifyConnectDevices.Items[idx] = scDevice
@@ -2566,6 +2623,10 @@ class SpotifyConnectDirectoryTask(threading.Thread):
2566
2623
 
2567
2624
  finally:
2568
2625
 
2626
+ # free resources.
2627
+ if (zconn is not None):
2628
+ zconn = None
2629
+
2569
2630
  # trace.
2570
2631
  _logsi.LeaveMethod(SILevel.Debug)
2571
2632
 
@@ -586,7 +586,7 @@ class ZeroconfConnect:
586
586
  _logsi.LogMethodParmList(SILevel.Verbose, "Issuing Spotify Connect Zeroconf addUser request (ip=%s:%s)" % (self._HostIpAddress, self._HostIpPort), apiMethodParms)
587
587
 
588
588
  # set request endpoint.
589
- endpoint:str = self.GetEndpoint('addUser')
589
+ endpoint:str = self.GetEndpoint('addUser', info.Version)
590
590
 
591
591
  # set request headers.
592
592
  reqHeaders:dict = {
@@ -677,6 +677,35 @@ class ZeroconfConnect:
677
677
  # get authorization_code access token.
678
678
  blob:str = self._GetSpotifyConnectAuthorizationCodeToken(info, loginId=loginId)
679
679
 
680
+ # special processing for tokenType "accesstoken":
681
+ elif info.TokenType == 'accesstoken':
682
+
683
+ # trace.
684
+ _logsi.LogVerbose("Spotify Connect token type is '%s' - using Spotify Desktop Client OAuth2 token to connect: '%s' (ip=%s)" % (info.TokenType, info.RemoteName, self._HostIpAddress))
685
+
686
+ # validations.
687
+ if (loginId is None) or (not isinstance(loginId,str)):
688
+ raise SpotifyApiError(SAAppMessages.MSG_SPOTIFY_ACTIVATE_CREDENTIAL_REQUIRED % ("SpotifyConnectLoginId"), logsi=_logsi)
689
+ if (username is None) or (not isinstance(username,str)):
690
+ raise SpotifyApiError(SAAppMessages.MSG_SPOTIFY_ACTIVATE_CREDENTIAL_REQUIRED % ("SpotifyConnectUsername"), logsi=_logsi)
691
+ if (password is None) or (not isinstance(password,str)):
692
+ raise SpotifyApiError(SAAppMessages.MSG_SPOTIFY_ACTIVATE_CREDENTIAL_REQUIRED % ("SpotifyConnectPassword"), logsi=_logsi)
693
+
694
+ # create credentials and builder objects.
695
+ credentials:Credentials = Credentials(username, password, AuthenticationTypes.SPOTIFY_TOKEN)
696
+ builder = BlobBuilder(credentials, info.DeviceId, info.PublicKey)
697
+
698
+ # set defaults used by token type "accesstoken".
699
+ # - use clientKey from getInfo response.
700
+ # - exclude origin device information from the addUser request.
701
+ # - use tokenType from getInfo response.
702
+ clientKey = info.ClientId or ''
703
+ includeOriginDeviceInfo = False
704
+ tokenType = info.TokenType
705
+
706
+ # get access token.
707
+ blob:str = self._GetSpotifyConnectAccessToken(info, loginId=loginId)
708
+
680
709
  else:
681
710
 
682
711
  # trace.
@@ -713,7 +742,7 @@ class ZeroconfConnect:
713
742
  # set request parameters.
714
743
  reqData={
715
744
  "action": "addUser",
716
- "version": self.Version, # info.Version,
745
+ "version": info.Version or self.Version,
717
746
  "tokenType": tokenType,
718
747
  "clientKey": clientKey,
719
748
  "loginId": loginId or '', # canonical login id (e.g. "31l77fd87g8h9j00k89f07jf87ge")
@@ -740,7 +769,7 @@ class ZeroconfConnect:
740
769
 
741
770
  # trace.
742
771
  _logsi.LogDictionary(SILevel.Debug, "ZeroconfConnect http request: '%s' (headers)" % (endpoint), reqHeaders)
743
- _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (data)" % (endpoint), reqData)
772
+ _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (json dict)" % (endpoint), reqData, prettyPrint=True)
744
773
 
745
774
  # The 10 second connect timeout prevents the application from hanging when
746
775
  # attempting to connect to an unreachable IP address.
@@ -771,6 +800,129 @@ class ZeroconfConnect:
771
800
  _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
772
801
 
773
802
 
803
+ def _GetSpotifyConnectAccessToken(
804
+ self,
805
+ info:ZeroconfGetInfo,
806
+ loginId:str,
807
+ ) -> str:
808
+ """
809
+ Get the Spotify OAuth2 access token value.
810
+ The resulting token will be used as the `blob` argument for the Spotify Connect Zeroconf `addUser` request.
811
+ """
812
+ apiMethodName:str = '_GetSpotifyConnectAccessToken'
813
+ apiMethodParms:SIMethodParmListContext = None
814
+ tracePrefix:str = 'SpotifyConnectAccessToken exchange'
815
+
816
+ # the access token is loaded from the token storage file (e.g. `spotifyplus_tokens.json`, etc).
817
+ # if not found, then an exception will be raised to indicate there is no access token stored.
818
+ # if found, the token is then checked to see if it has expired, and is automatically refreshed if so.
819
+ # the "access_token" value is then returned, and passed on the Spotify Connect Zeroconf "adduser"
820
+ # call as the "blob" parameter (e.g. `AuthenticationTypes.SPOTIFY_TOKEN`).
821
+
822
+ try:
823
+
824
+ # trace.
825
+ apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
826
+ apiMethodParms.AppendKeyValue("loginId", loginId)
827
+ apiMethodParms.AppendKeyValue("info.DeviceId", info.DeviceId)
828
+ apiMethodParms.AppendKeyValue("info.RemoteName", info.RemoteName)
829
+ apiMethodParms.AppendKeyValue("info.BrandDisplayName", info.BrandDisplayName)
830
+ apiMethodParms.AppendKeyValue("info.ModelDisplayName", info.ModelDisplayName)
831
+ apiMethodParms.AppendKeyValue("self.TokenAuthInBrowser", self.TokenAuthInBrowser)
832
+ _logsi.LogMethodParmList(SILevel.Verbose, "Preparing to generate a new access token for Spotify Connect device: '%s' (%s)" % (info.RemoteName, info.DeviceId), apiMethodParms)
833
+
834
+ # set variables for spotify access token generation.
835
+ # the redirect uri variables were obtained via Fiddler trace using Spotify Desktop App to initiate Spotify Connect to device.
836
+ redirectUriHost:str = '127.0.0.1'
837
+ redirectUriPort:str = 4381
838
+ redirectUriPath:str = '/login'
839
+ authorizationType:str = 'Authorization Code PKCE'
840
+
841
+ # Spotify Desktop App scopes requested for Spotify Connect (streaming only as of 2024/08/13)
842
+ SPOTIFY_SCOPES:list = \
843
+ [
844
+ 'streaming',
845
+ ]
846
+
847
+ # create oauth provider for spotify authentication code with pkce.
848
+ _logsi.LogVerbose('creating OAuth2 provider for Spotify Authentication Code with PKCE')
849
+ authClient:AuthClient = AuthClient(
850
+ authorizationType=authorizationType,
851
+ authorizationUrl=SPOTIFY_API_AUTHORIZE_URL,
852
+ tokenUrl=SPOTIFY_API_TOKEN_URL,
853
+ scope=SPOTIFY_SCOPES,
854
+ clientId=SPOTIFY_DESKTOP_APP_CLIENT_ID,
855
+ tokenStorageDir=self.TokenStorageDir,
856
+ tokenStorageFile=self.TokenStorageFile,
857
+ tokenProviderId='SpotifyWebApiAuthCodePkce',
858
+ tokenProfileId=loginId,
859
+ )
860
+
861
+ # raise an exception if the authorization token is not present, or the scope has changed.
862
+ # the user must create the authorization token outside of this process, as the Spotify
863
+ # authorization token is driven by responding to a web request for access using OAuth.
864
+ # as this process is running on a server, there is no way for the user to respond to the
865
+ # request (e.g. via browser nor command-line).
866
+ isAuthorized = authClient.IsAuthorized
867
+ _logsi.LogVerbose('Checking OAuth2 authorization status: IsAuthorized=%s' % isAuthorized)
868
+
869
+ if (isAuthorized == False):
870
+
871
+ # can the user interact with a local default browser to authorize the request?
872
+ if self.TokenAuthInBrowser:
873
+
874
+ # at this point, we need a new authorization token.
875
+ # the user has up to 2 minutes to respond to the request by copying / pasting the
876
+ # message / auth approval url from the system log into a browser window that is
877
+ # running on the local machine.
878
+ _logsi.LogVerbose('Preparing to retrieve a new OAuth2 authorization access token')
879
+ authClient.AuthorizeWithServer(
880
+ host=redirectUriHost,
881
+ port=redirectUriPort,
882
+ redirect_uri_path=redirectUriPath,
883
+ open_browser=True,
884
+ timeout_seconds=120
885
+ )
886
+
887
+ else:
888
+
889
+ # user cannot approve the request - raise an exception.
890
+ raise SpotifyZeroconfApiError(401, 'Spotify Desktop Client Application access token was not authorized', apiMethodName, 'Token Not Authorized', _logsi)
891
+
892
+ else:
893
+
894
+ _logsi.LogVerbose('OAuth2 authorization token has previously been authorized')
895
+
896
+ # process results.
897
+ oauth2token:dict = authClient.Session.token
898
+ authToken = SpotifyAuthToken(authClient.AuthorizationType, authClient.TokenProfileId, root=oauth2token)
899
+
900
+ # does token need to be refreshed?
901
+ if authToken.IsExpired:
902
+
903
+ # refresh the token.
904
+ # this will also store the refreshed token to disk to be used later if required.
905
+ _logsi.LogVerbose('OAuth2 authorization token has expired, or is about to; token will be refreshed')
906
+ oauth2token:dict = authClient.RefreshToken()
907
+ authToken = SpotifyAuthToken(authClient.AuthorizationType, authClient.TokenProfileId, root=oauth2token)
908
+ _logsi.LogObject(SILevel.Verbose, TRACE_METHOD_RESULT % apiMethodName, authToken, excludeNonPublic=True)
909
+
910
+ else:
911
+
912
+ _logsi.LogVerbose('OAuth2 authorization token has not expired')
913
+
914
+ # get access token value.
915
+ result = oauth2token.get("access_token", None)
916
+
917
+ # return access token to caller.
918
+ return result
919
+
920
+ finally:
921
+
922
+ # trace.
923
+ _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
924
+
925
+
774
926
  def _GetSpotifyConnectAuthorizationCodeToken(
775
927
  self,
776
928
  info:ZeroconfGetInfo,
@@ -803,7 +955,7 @@ class ZeroconfConnect:
803
955
  _logsi.LogMethodParmList(SILevel.Verbose, "Preparing to generate a new authorization_code access token for Spotify Connect device: '%s' (%s)" % (info.RemoteName, info.DeviceId), apiMethodParms)
804
956
 
805
957
  # set variables for spotify authorization_code token generation.
806
- # the redirecy uri variables were obtained via Fiddler trace using Spotify Desktop App to initiate Spotify Connect to device.
958
+ # the redirect uri variables were obtained via Fiddler trace using Spotify Desktop App to initiate Spotify Connect to device.
807
959
  redirectUriHost:str = '127.0.0.1'
808
960
  redirectUriPort:str = 4381
809
961
  redirectUriPath:str = '/login'
@@ -918,7 +1070,7 @@ class ZeroconfConnect:
918
1070
  # trace.
919
1071
  _logsi.LogVerbose('Exchanging OAuth2 authorization access token for an authorization_code access token')
920
1072
  _logsi.LogDictionary(SILevel.Debug, "%s http request: '%s' (headers)" % (tracePrefix, SPOTIFY_API_TOKEN_URL), tokHeaders)
921
- _logsi.LogDictionary(SILevel.Verbose, "%s http request: '%s' (data)" % (tracePrefix, SPOTIFY_API_TOKEN_URL), tokData)
1073
+ _logsi.LogDictionary(SILevel.Verbose, "%s http request: '%s' (json dict)" % (tracePrefix, SPOTIFY_API_TOKEN_URL), tokData, prettyPrint=True)
922
1074
 
923
1075
  # execute spotify access token request.
924
1076
  response = requests.post(
@@ -1109,7 +1261,7 @@ class ZeroconfConnect:
1109
1261
  "action": "resetUsers",
1110
1262
  "version": self.Version,
1111
1263
  }
1112
- _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (data)" % (endpoint), reqData)
1264
+ _logsi.LogDictionary(SILevel.Verbose, "ZeroconfConnect http request: '%s' (json dict)" % (endpoint), reqData, prettyPrint=True)
1113
1265
 
1114
1266
  # execute spotify zeroconf api request.
1115
1267
  response = requests.post(
@@ -1202,7 +1354,8 @@ class ZeroconfConnect:
1202
1354
 
1203
1355
  def GetEndpoint(
1204
1356
  self,
1205
- action:str
1357
+ action:str,
1358
+ version:str|None=None,
1206
1359
  ) -> str:
1207
1360
  """
1208
1361
  Gets a Spotify Zeroconf API endpoint uri.
@@ -1210,6 +1363,9 @@ class ZeroconfConnect:
1210
1363
  Args:
1211
1364
  action (str):
1212
1365
  Action parameter value (e.g. "getInfo", "addUser", "resetUsers", etc).
1366
+ version (str):
1367
+ Version parameter value (e.g. "2.7.1", "2.9.0", etc).
1368
+ For some devices, this is important and must match what is returned on the getInfo request!
1213
1369
 
1214
1370
  Returns:
1215
1371
  A string containing the endpoint.
@@ -1217,7 +1373,7 @@ class ZeroconfConnect:
1217
1373
  return "{uri}?action={action}&version={version}".format(
1218
1374
  uri=self._Uri,
1219
1375
  action=action,
1220
- version=self.Version,
1376
+ version=version or self.Version,
1221
1377
  )
1222
1378
 
1223
1379
 
@@ -1341,7 +1497,7 @@ class ZeroconfConnect:
1341
1497
  result = ZeroconfGetInfo(root=responseData)
1342
1498
 
1343
1499
  # trace.
1344
- _logsi.LogObject(SILevel.Verbose, '%s result (%s) - "%s" (%s)' % (apiMethodName, type(result).__name__, result.RemoteName, result.DeviceId), result, excludeNonPublic=True)
1500
+ _logsi.LogObject(SILevel.Verbose, '%s result (%s) - "%s" (%s)' % (apiMethodName, type(result).__name__, result.RemoteNameOrAlias, result.DeviceId), result, excludeNonPublic=True)
1345
1501
 
1346
1502
  # if result status is not ok, then raise an exception.
1347
1503
  if (result.Status != 101):
@@ -373,6 +373,32 @@ class ZeroconfGetInfo(ZeroconfResponse):
373
373
  if (isinstance(value, str)) or (value is None):
374
374
  self._RemoteName = value
375
375
 
376
+ @property
377
+ def RemoteNameOrAlias(self) -> str:
378
+ """
379
+ Name to be displayed for the device (e.g. "BOSE-ST10-1").
380
+
381
+ If a RemoteName value is not defined, then the first alias name will be
382
+ returned if one is defined.
383
+
384
+ This value will be null if the response does not contain a RemoteName value
385
+ and there are no Aliases defined.
386
+ """
387
+ if (self._RemoteName is not None) and ((self._RemoteName + "").strip() != ""):
388
+
389
+ # if remote name is defined, then return it.
390
+ return self._RemoteName
391
+
392
+ elif (self.HasAliases):
393
+
394
+ # if aliases are defined then return the first alias name.
395
+ return self._Aliases[0].Name
396
+
397
+ else:
398
+
399
+ # otherwise return null.
400
+ return None
401
+
376
402
 
377
403
  @property
378
404
  def ResolverVersion(self) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spotifywebapipython
3
- Version: 1.0.290
3
+ Version: 1.0.292
4
4
  Summary: Spotify Web API Python3 Library
5
5
  Author: Todd Lucas
6
6
  Author-email: <thlucas@yahoo.com>
@@ -1,5 +1,5 @@
1
1
  spotifywebapipython/__init__.py,sha256=ka7F2AsT-UU7KSYKBnfgl5Trojcep5aQspLutg8D-2k,1306
2
- spotifywebapipython/const.py,sha256=Hbc5cpl2gnUJRe3sQ6_bZFf3W9zoE5miVgi8T4hE__o,4100
2
+ spotifywebapipython/const.py,sha256=A1cvmzruhkRRN8tqgeYtpfLus8rUoD446j7HfFSodJ0,4100
3
3
  spotifywebapipython/saappmessages.py,sha256=mVprjpd5CdD8kYeAkLzRvMvIQPmaYtH4UCP5my3QhSk,5065
4
4
  spotifywebapipython/sautils.py,sha256=LZF3UvxaBK35WIg1hO37nbW8j1y-M-oOxRbi1dPR39w,8746
5
5
  spotifywebapipython/spotifyapierror.py,sha256=23_CYlxWN0BceYYCLpBpunl55vEwPr_wFf34l74xzeA,1855
@@ -12,25 +12,25 @@ spotifywebapipython/spotifytypeprefixes.py,sha256=3yfpxKF7jF694qBuo-PbKYvm70Ywwd
12
12
  spotifywebapipython/spotifywebapiauthenticationerror.py,sha256=5A0MKvYo3Ypv0ZeSSfLHuFLwzoUUu044bBoUgMfNXNw,3940
13
13
  spotifywebapipython/spotifywebapierror.py,sha256=ctYFvkBPlFFEoM_geP6hzwTyC2mPcrYR_zCCNvsmpwc,3870
14
14
  spotifywebapipython/spotifywebplayertoken.py,sha256=axj1j1lM7COiiZfpElkJRdnk4AmE6bAQcjKshUIz1Nk,35813
15
- spotifywebapipython/docs/__init__.html,sha256=Ldt-0oo7-t6Zl5owTtxRBwGtWu9KhnxICq6L2QqCo4Q,198472
15
+ spotifywebapipython/docs/__init__.html,sha256=Q07ibHoLeCq6xMDsnscQoKKKsBiVYqZRpBtppolBXHo,198988
16
16
  spotifywebapipython/docs/codecopy.js,sha256=ryuoZwLIL03YitjjFzn49UO5ta7-OBos6LNA3qHhFzs,3120
17
- spotifywebapipython/docs/index.html,sha256=cImjffXQJ5RDHAy-CYyzLvrxkJ_6T5JmNyQXxg008Xo,48779
18
- spotifywebapipython/docs/search.js,sha256=PApJpxDAaB8gClLQZHRgd-aoOiR6ng8q2fLK6yfrVPQ,9267299
17
+ spotifywebapipython/docs/index.html,sha256=2i080Y3zkBfhdjzzhh1_62RO2ffkOa5dIP-8wv-PAsU,48779
18
+ spotifywebapipython/docs/search.js,sha256=q4VZ2SMeeGus1p2MMdusjmjfymik-xpnyR1ZQu8v91o,9276225
19
19
  spotifywebapipython/docs/spotifywebapipython.ico,sha256=YImj39fqqSPSuB2-sMDlUYDd6YwRfQeJRtq4FjwWglg,16410
20
20
  spotifywebapipython/docs/spotifywebapipython_OLD.ico,sha256=YIv5LjcD4bTIJQSOr8jjwbWaeB3QkElRwkTOePiGbZo,14904
21
- spotifywebapipython/docs/spotifywebapipython/const.html,sha256=J5O3dWQFRViNYRJmOv-8clVJHN8RymfgCorJ54Z896M,60537
22
- spotifywebapipython/docs/spotifywebapipython/saappmessages.html,sha256=y8uutSNSmC1DWNkNsbBaN_j5J1s0ZbCKJqyR3mI-VBQ,62358
23
- spotifywebapipython/docs/spotifywebapipython/sautils.html,sha256=YTci1ibsEWIoc-QsrpjOyTj8xQ8QwqSwcFx9R5BCZzs,62031
24
- spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html,sha256=6OSzC7QXNvWYmNNdC4RpTJ_4Th4ihZwE1uTo64bBGjE,48054
25
- spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html,sha256=uV6DnZVd4872sFBoYbHwDb-czMOdq9SUF0BpM9Iow7s,59210
26
- spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html,sha256=vuwYtRwdKtJ5p7QONlgPjPZvmTfvzPAgLUzjXll-Htc,59256
27
- spotifywebapipython/docs/spotifywebapipython/spotifyclient.html,sha256=mNJY_sPa4r6Ib-WdgPuRVNlt_ep_6uWS41WfFDVYjaY,1677607
28
- spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html,sha256=qdA3_P2aX7w5YOW2tcq8phUBB812ai_uY-H2XMfEj2w,57662
29
- spotifywebapipython/docs/spotifywebapipython/spotifymediatypes.html,sha256=qjQ94ReI5YIAYHndjTkrsZtI2thVhTgoBDZo24o9Wf0,51532
30
- spotifywebapipython/docs/spotifywebapipython/spotifytypeprefixes.html,sha256=qZejg2IcTp1ZKjvJWC671Y14nUIV8P7Uroph1YX_kjw,52401
31
- spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html,sha256=gRkAZSLaoz0N-uGf5KVvWlNxMUlGC2xIRkeP2JBUeTA,52174
32
- spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html,sha256=aLTtIbojLtyUOp-EWXJarWCpj7rftwIORZ1ETcJl9Lg,51484
33
- spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html,sha256=T-kb_8Xt56vjcD1vu1LjKuJAI7llw9I3slLukdeV9uQ,62204
21
+ spotifywebapipython/docs/spotifywebapipython/const.html,sha256=VROpTx214OhAX50Ke5eWWgzg7H1Oj32bRJ-BClJJ1sM,60537
22
+ spotifywebapipython/docs/spotifywebapipython/saappmessages.html,sha256=L8oF4rwosC3z_vKhIfumlt4H-q0Mto4BU4vkHMEe5BI,62358
23
+ spotifywebapipython/docs/spotifywebapipython/sautils.html,sha256=AhrCNFPtfqyRZsWCIxWrGxyVlnfq136ybUxuQLj-_vQ,62031
24
+ spotifywebapipython/docs/spotifywebapipython/spotifyapierror.html,sha256=43spTeZ5re5j6GO-MNJOcJc8R-9nIXKkDp-caVza6yY,48054
25
+ spotifywebapipython/docs/spotifywebapipython/spotifyapimessage.html,sha256=KdbQPIL0TwVux1RElR9Dc-ProvihgWWNCzkFYsUcTqg,59210
26
+ spotifywebapipython/docs/spotifywebapipython/spotifyauthtoken.html,sha256=toVCb95bEAvbNxOMpVXIediauVBNl_6NU5h4rdi1a0U,59256
27
+ spotifywebapipython/docs/spotifywebapipython/spotifyclient.html,sha256=_DlyOletxlhhE2Xdo5RH0stbdU4x5FSIcHQQPhBdR5E,1677607
28
+ spotifywebapipython/docs/spotifywebapipython/spotifydiscovery.html,sha256=vihtraJlGUn4kp3A4tx7-GOqh4livctWGQ8H5rUCayU,57662
29
+ spotifywebapipython/docs/spotifywebapipython/spotifymediatypes.html,sha256=ss7-LPAqEixRTws6SHYEQFhAstSMdRdP1eDcBlHJhNA,51532
30
+ spotifywebapipython/docs/spotifywebapipython/spotifytypeprefixes.html,sha256=8WBQbIfh_i2SDlelt123pOARpKxdBQYQrJH4SoakYOA,52401
31
+ spotifywebapipython/docs/spotifywebapipython/spotifywebapiauthenticationerror.html,sha256=1Ka6gyOAeSMOGefzL_RVhTu0lbnFjqGUx4TPNPFa0ns,52174
32
+ spotifywebapipython/docs/spotifywebapipython/spotifywebapierror.html,sha256=koUkSqk89KM8jihcZM0kB9tD6Q4XiqvIG5qqDBz62Nw,51484
33
+ spotifywebapipython/docs/spotifywebapipython/spotifywebplayertoken.html,sha256=id1JsjfuL1D6KcPhrCiLw1dF__fKRSF6rGsZMFVQfbA,62204
34
34
  spotifywebapipython/models/__init__.py,sha256=RwRQhUX-6EUs8XvStVFpR8EPwoqIAz9K6ref-28qHKI,4423
35
35
  spotifywebapipython/models/album.py,sha256=U3Pdw0-EL33LdkeiN31fjZ23rlLk6DNGV8jRmZU7EyQ,5170
36
36
  spotifywebapipython/models/albumpagesaved.py,sha256=SBYMhkh5ZsMrRAXl-cm9zsxMDh8gO190ENq6Py2TyGY,2417
@@ -110,7 +110,7 @@ spotifywebapipython/oauthcli/__init__.py,sha256=K37XTxYAKT0n8otCNzWbjgJ10fAPN4WL
110
110
  spotifywebapipython/oauthcli/authclient.py,sha256=qGKa4FLBHHZQyP5ho1sIM_mNmyshQd720YwKcUFQJAw,56207
111
111
  spotifywebapipython/spotifyconnect/__init__.py,sha256=9Xl5KiJFvWLbsdb4YQ0xmF67i36ybpXCCXrWeNsKEvc,1108
112
112
  spotifywebapipython/spotifyconnect/spotifyconnectdeviceeventargs.py,sha256=35hKa0FAaPgy7W8D6MFXmga667pjwXITo8jSj1TDfE8,1988
113
- spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py,sha256=QXNpJSqUGuOYxCzvDH63Ka9L9Yo7Bb5iVBRtDhc4ILE,153923
113
+ spotifywebapipython/spotifyconnect/spotifyconnectdirectorytask.py,sha256=GY8t6nH4aEA77yd9ysTYE-5DUZE0_rpCtNn5aKmjQsA,157385
114
114
  spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastapptask.py,sha256=tCjqU7ubtfzBENdvqwVB0pMDGHRY_0u6N5OYlI5hbj8,23368
115
115
  spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastcontroller.py,sha256=e2wLmFq4393IdVa51w8X5UQl4KfnHRh34DMlxZfl9-g,34218
116
116
  spotifywebapipython/spotifyconnect/spotifyconnectzeroconfcastlistener.py,sha256=k_Lw52CqNliCrfqMP6s_BHynqHYBOf_NwdrJru6qDFA,16707
@@ -132,13 +132,13 @@ spotifywebapipython/zeroconfapi/credentials.py,sha256=MIXqZvFNDjoICpZGNXPveHNl9G
132
132
  spotifywebapipython/zeroconfapi/cryptodiffiehellman.py,sha256=k-nMR68GtzODYsQrwCpvFE2XNxv-XUFbMemplldtwp4,3340
133
133
  spotifywebapipython/zeroconfapi/helpers.py,sha256=9lJFOuKYrk-e7SbU94WmFv5oiBq4k5gMS8wE1it4Wbc,1429
134
134
  spotifywebapipython/zeroconfapi/spotifyzeroconfapierror.py,sha256=rTwIrr1qV0UiQS_zBufboyZ3A3yGrdCsTAOiVA5dvzY,3031
135
- spotifywebapipython/zeroconfapi/zeroconfconnect.py,sha256=DDuVljjg_SF_YJWWQjmCSG-6-93YBSTCLhjr77DI5xI,70266
136
- spotifywebapipython/zeroconfapi/zeroconfgetinfo.py,sha256=APayso_F7lCsdzzrkescgd-nKukUah3Kjfs-5F2tnvU,21415
135
+ spotifywebapipython/zeroconfapi/zeroconfconnect.py,sha256=JzAKVc7XC_HrTH1IwsuXpcgW0rFlRYocHL8likoYYvQ,79023
136
+ spotifywebapipython/zeroconfapi/zeroconfgetinfo.py,sha256=gTr3J3iVDR_ueP1tg-WHuCOke3eP864WGiESmOOYFUw,22243
137
137
  spotifywebapipython/zeroconfapi/zeroconfgetinfoalias.py,sha256=FZJM23HIPJSfhXIC3-EuS1XhawheoG5maSp7__k4yBo,2932
138
138
  spotifywebapipython/zeroconfapi/zeroconfgetinfodrmmediaformat.py,sha256=c5uk8ljsIuBK3w2jahHKQuoWvm245LOjUBitetXx5Mo,2347
139
139
  spotifywebapipython/zeroconfapi/zeroconfresponse.py,sha256=mAPIDj5MG53x8peiScrp7OJblwy_o_Sto4wqE0rGujU,6445
140
- spotifywebapipython-1.0.290.dist-info/LICENSE,sha256=KGcc9g0zwqNLcWnSn_ezv1UtKyhMENFhvtixpxUPNsE,1177
141
- spotifywebapipython-1.0.290.dist-info/METADATA,sha256=DqbHh-EKQJMOeJrCpZ8EMH1jzPskDqsAbD60SteZMDY,11644
142
- spotifywebapipython-1.0.290.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
143
- spotifywebapipython-1.0.290.dist-info/top_level.txt,sha256=jTBLFlijKTesYh2dteAz4vb17qjaqWKeDjpnnwTcrjs,20
144
- spotifywebapipython-1.0.290.dist-info/RECORD,,
140
+ spotifywebapipython-1.0.292.dist-info/LICENSE,sha256=KGcc9g0zwqNLcWnSn_ezv1UtKyhMENFhvtixpxUPNsE,1177
141
+ spotifywebapipython-1.0.292.dist-info/METADATA,sha256=XYS1qt1LwEsJDG61IbX34sMh5I0ZpQcWoZ0U4w0uMRI,11644
142
+ spotifywebapipython-1.0.292.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
143
+ spotifywebapipython-1.0.292.dist-info/top_level.txt,sha256=jTBLFlijKTesYh2dteAz4vb17qjaqWKeDjpnnwTcrjs,20
144
+ spotifywebapipython-1.0.292.dist-info/RECORD,,