spotifywebapiPython 1.0.100__py3-none-any.whl → 1.0.102__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of spotifywebapiPython might be problematic. Click here for more details.

Files changed (23) hide show
  1. {spotifywebapiPython-1.0.100.dist-info → spotifywebapiPython-1.0.102.dist-info}/METADATA +1 -1
  2. {spotifywebapiPython-1.0.100.dist-info → spotifywebapiPython-1.0.102.dist-info}/RECORD +23 -23
  3. spotifywebapipython/const.py +1 -1
  4. spotifywebapipython/docs/__init__.html +15 -2
  5. spotifywebapipython/docs/index.html +2 -2
  6. spotifywebapipython/docs/search.js +1 -1
  7. spotifywebapipython/docs/spotifywebapiPython/const.html +3 -3
  8. spotifywebapipython/docs/spotifywebapiPython/saappmessages.html +2 -2
  9. spotifywebapipython/docs/spotifywebapiPython/sautils.html +2 -2
  10. spotifywebapipython/docs/spotifywebapiPython/spotifyapierror.html +2 -2
  11. spotifywebapipython/docs/spotifywebapiPython/spotifyapimessage.html +2 -2
  12. spotifywebapipython/docs/spotifywebapiPython/spotifyauthtoken.html +2 -2
  13. spotifywebapipython/docs/spotifywebapiPython/spotifyclient.html +11 -3
  14. spotifywebapipython/docs/spotifywebapiPython/spotifydiscovery.html +2 -2
  15. spotifywebapipython/docs/spotifywebapiPython/spotifywebapiauthenticationerror.html +2 -2
  16. spotifywebapipython/docs/spotifywebapiPython/spotifywebapierror.html +2 -2
  17. spotifywebapipython/spotifyclient.py +37 -6
  18. spotifywebapipython/zeroconfapi/credentials.py +4 -1
  19. spotifywebapipython/zeroconfapi/helpers.py +7 -0
  20. spotifywebapipython/zeroconfapi/zeroconfconnect.py +126 -2
  21. {spotifywebapiPython-1.0.100.dist-info → spotifywebapiPython-1.0.102.dist-info}/LICENSE +0 -0
  22. {spotifywebapiPython-1.0.100.dist-info → spotifywebapiPython-1.0.102.dist-info}/WHEEL +0 -0
  23. {spotifywebapiPython-1.0.100.dist-info → spotifywebapiPython-1.0.102.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  # external package imports.
2
2
  import json
3
+ import os.path
3
4
  import requests
4
5
  from requests.models import Response
5
6
  import time
@@ -7,7 +8,7 @@ import time
7
8
  # our package imports.
8
9
  from .blobbuilder import BlobBuilder
9
10
  from .credentials import Credentials, AuthenticationTypes
10
- from .helpers import int_to_b64str
11
+ from .helpers import int_to_b64str, b64str_to_bytes
11
12
  from .spotifyzeroconfapierror import SpotifyZeroconfApiError
12
13
  from .zeroconfresponse import ZeroconfResponse
13
14
  from .zeroconfgetinfo import ZeroconfGetInfo
@@ -379,6 +380,12 @@ class ZeroconfConnect:
379
380
  .. include:: ../../docs/include/samplecode/ZeroconfConnect/Connect.py
380
381
  ```
381
382
  </details>
383
+ <details>
384
+ <summary>Sample Code - spotifyD</summary>
385
+ ```python
386
+ .. include:: ../../docs/include/samplecode/ZeroconfConnect/Connect_SPOTIFYD.py
387
+ ```
388
+ </details>
382
389
  """
383
390
  apiMethodName:str = 'Connect'
384
391
  apiMethodParms:SIMethodParmListContext = None
@@ -559,8 +566,37 @@ class ZeroconfConnect:
559
566
  if (info.PublicKey == 'SU5WQUxJRA==') and (info.Availability == 'NOT-LOADED'):
560
567
  includeOriginDeviceInfo = True
561
568
 
569
+ # special processing for librespot, spotifyd.
570
+ if info.ModelDisplayName == 'librespot':
571
+
572
+ # we will use credentials from a librespot credentials.json file.
573
+ # this requires that the user copy the "credentials.json" file from the
574
+ # librespot cache location (e.g. "/home/user/.cache/spotifyd/credentials.json", etc)
575
+ # and place it in the same folder as the token storage file with a new name
576
+ # of "SpotifyWebApiPython_librespot_credentials.json".
577
+
578
+ # get credentials from librespot credentials file.
579
+ credentials:dict = self._LoadLibrespotCredentials(loginId)
580
+
581
+ # create credentials and builder objects.
582
+ librespot_username = credentials.get('username','')
583
+ librespot_password = b64str_to_bytes(credentials.get('auth_data',''))
584
+ credentials:Credentials = Credentials(librespot_username, librespot_password, AuthenticationTypes.STORED_SPOTIFY_CREDENTIALS)
585
+ builder = BlobBuilder(credentials, info.DeviceId, info.PublicKey)
586
+
587
+ # set defaults used by this token type:
588
+ # - clientKey should be the getInfo response PublicKey (base64 encoded).
589
+ # - include origin device information in the addUser request.
590
+ # - tokenType should be set to '' (not used by librespot).
591
+ clientKey:str = builder.dh_keys.PublicKeyBase64String
592
+ includeOriginDeviceInfo = True
593
+ tokenType = ''
594
+
595
+ # formulate the blob.
596
+ blob = builder.build()
597
+
562
598
  # special processing for tokenType "authorization_code":
563
- if info.TokenType == 'authorization_code':
599
+ elif info.TokenType == 'authorization_code':
564
600
 
565
601
  # trace.
566
602
  _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))
@@ -819,6 +855,90 @@ class ZeroconfConnect:
819
855
  _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
820
856
 
821
857
 
858
+ def _LoadLibrespotCredentials(self, loginId:str) -> dict:
859
+ """
860
+ Loads spotify user credentials from a librespot credentials.json file.
861
+
862
+ Args:
863
+ loginId (str):
864
+ Spotify canonical login id of the authenticating user.
865
+ Example = `3758dfdsfjk435hjk6k79lm0n3c4`
866
+
867
+ Returns:
868
+ A credentials dictionary, if one was found in the credentials storage file;
869
+ otherwise, null.
870
+ """
871
+ apiMethodName:str = '_LoadLibrespotCredentials'
872
+ apiMethodParms:SIMethodParmListContext = None
873
+
874
+ try:
875
+
876
+ # trace.
877
+ apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
878
+ _logsi.LogMethodParmList(SILevel.Verbose, "Loading librespot credentials from storage", apiMethodParms)
879
+
880
+ # librespot credntials.json file format as of 2024/09/28
881
+ # {
882
+ # "username": "31l77xxxxxxxxxxxxxxxxxxxxxxx",
883
+ # "auth_type": 1,
884
+ # "auth_data": "QVFET2h6 ... aFp3OHg="
885
+ # }
886
+
887
+ # formulate the credentials file path.
888
+ filePath:str = os.path.join(self._TokenStorageDir, "SpotifyWebApiPython_librespot_credentials.json")
889
+
890
+ # does the file exist? if not, then it's an error as we are trying to authenticate!
891
+ if (not os.path.exists(filePath)):
892
+ raise SpotifyApiError("Could not find librespot authorization credentials file (%s). This file is required in order to authenticate to a device or service that utilizes the librespot application." % (filePath), logsi=_logsi)
893
+
894
+ # open the credentials file, and load its contents.
895
+ _logsi.LogVerbose('Opening librespot credentials file: "%s"' % (filePath))
896
+ with open(filePath, 'r') as f:
897
+ credentials:dict = json.load(f)
898
+ _logsi.LogDictionary(SILevel.Verbose, 'librespot credentials were loaded successfully', credentials, prettyPrint=True)
899
+
900
+ # prepare for comparison.
901
+ loginId = loginId.lower()
902
+ credential:dict = None
903
+
904
+ # check for multiple credential format.
905
+ if (isinstance(credentials, list)):
906
+
907
+ # file contents are an array of librespot credentials.
908
+ for credential in credentials:
909
+ if (credential.get('username','').lower() == loginId):
910
+ break
911
+
912
+ else:
913
+
914
+ # file contents are a single librespot credential.
915
+ credential = credentials
916
+
917
+ # does the found credential match the requested login? if not, then reset the credential.
918
+ if (credential is not None):
919
+ if (credential.get('username','').lower() != loginId):
920
+ credential = None
921
+
922
+ # if no credentials then it's an error.
923
+ if (credential is None):
924
+ raise SpotifyApiError("librespot authorization credentials file (%s) did not contain credentials for canonical loginid (%s)." % (filePath, loginId), logsi=_logsi)
925
+
926
+ # return credentials to caller.
927
+ return credential
928
+
929
+ except SpotifyApiError: raise # pass handled exceptions on thru
930
+ except Exception as ex:
931
+
932
+ # trace.
933
+ _logsi.LogException('Could not load librespot authorization credentials file (%s).' % (filePath), ex)
934
+ raise
935
+
936
+ finally:
937
+
938
+ # trace.
939
+ _logsi.LeaveMethod(SILevel.Debug, apiMethodName)
940
+
941
+
822
942
  def Disconnect(
823
943
  self,
824
944
  delay:float=0.50,
@@ -848,6 +968,10 @@ class ZeroconfConnect:
848
968
 
849
969
  The currently logged in user (if any) will be logged out of Spotify Connect, and the
850
970
  device id removed from the active Spotify Connect device list.
971
+
972
+ This method should not be attempted for devices or services that utilize `librespot`,
973
+ such as `spotifyd`. The `librespot` library does not implement the `resetUsers`
974
+ Spotify Connect Zeroconf endpoint, and will fail with a 404 error.
851
975
 
852
976
  <details>
853
977
  <summary>Sample Code</summary>