zspotify.py 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import os
  2. import os.path
  3. from getpass import getpass
  4. import time
  5. import requests
  6. from librespot.audio.decoders import VorbisOnlyAudioQuality
  7. from librespot.core import Session
  8. from const import TYPE, \
  9. PREMIUM, USER_READ_EMAIL, OFFSET, LIMIT, \
  10. PLAYLIST_READ_PRIVATE, USER_LIBRARY_READ
  11. from config import Config
  12. class ZSpotify:
  13. SESSION: Session = None
  14. DOWNLOAD_QUALITY = None
  15. CONFIG: Config = Config()
  16. def __init__(self, args):
  17. ZSpotify.CONFIG.load(args)
  18. ZSpotify.login()
  19. @classmethod
  20. def login(cls):
  21. """ Authenticates with Spotify and saves credentials to a file """
  22. cred_location = Config.get_credentials_location()
  23. if os.path.isfile(cred_location):
  24. try:
  25. cls.SESSION = Session.Builder().stored_file(cred_location).create()
  26. return
  27. except RuntimeError:
  28. pass
  29. while True:
  30. user_name = ''
  31. while len(user_name) == 0:
  32. user_name = input('Username: ')
  33. password = getpass()
  34. try:
  35. conf = Session.Configuration.Builder().set_stored_credential_file(cred_location).build()
  36. cls.SESSION = Session.Builder(conf).user_pass(user_name, password).create()
  37. return
  38. except RuntimeError:
  39. pass
  40. @classmethod
  41. def get_content_stream(cls, content_id, quality):
  42. return cls.SESSION.content_feeder().load(content_id, VorbisOnlyAudioQuality(quality), False, None)
  43. @classmethod
  44. def __get_auth_token(cls):
  45. return cls.SESSION.tokens().get_token(USER_READ_EMAIL, PLAYLIST_READ_PRIVATE, USER_LIBRARY_READ).access_token
  46. @classmethod
  47. def get_auth_header(cls):
  48. return {
  49. 'Authorization': f'Bearer {cls.__get_auth_token()}',
  50. 'Accept-Language': f'{cls.CONFIG.get_language()}'
  51. }
  52. @classmethod
  53. def get_auth_header_and_params(cls, limit, offset):
  54. return {
  55. 'Authorization': f'Bearer {cls.__get_auth_token()}',
  56. 'Accept-Language': f'{cls.CONFIG.get_language()}'
  57. }, {LIMIT: limit, OFFSET: offset}
  58. @classmethod
  59. def invoke_url_with_params(cls, url, limit, offset, **kwargs):
  60. headers, params = cls.get_auth_header_and_params(limit=limit, offset=offset)
  61. params.update(kwargs)
  62. return requests.get(url, headers=headers, params=params).json()
  63. @classmethod
  64. def invoke_url(cls, url, tryCount=0):
  65. # we need to import that here, otherwise we will get circular imports!
  66. from termoutput import Printer, PrintChannel
  67. headers = cls.get_auth_header()
  68. response = requests.get(url, headers=headers)
  69. responsetext = response.text
  70. responsejson = response.json()
  71. if 'error' in responsejson:
  72. if tryCount < (cls.CONFIG.get_retry_attempts() - 1):
  73. Printer.print(PrintChannel.WARNINGS, f"Spotify API Error (try {tryCount + 1}) ({responsejson['error']['status']}): {responsejson['error']['message']}")
  74. time.sleep(5)
  75. return cls.invoke_url(url, tryCount + 1)
  76. Printer.print(PrintChannel.API_ERRORS, f"Spotify API Error ({responsejson['error']['status']}): {responsejson['error']['message']}")
  77. return responsetext, responsejson
  78. @classmethod
  79. def check_premium(cls) -> bool:
  80. """ If user has spotify premium return true """
  81. return (cls.SESSION.get_user_attribute(TYPE) == PREMIUM) or cls.CONFIG.get_force_premium()