diff --git a/beetsplug/subsonicupdate.py b/beetsplug/subsonicupdate.py index 004439bac..8f5d2cb4b 100644 --- a/beetsplug/subsonicupdate.py +++ b/beetsplug/subsonicupdate.py @@ -29,26 +29,46 @@ import string import requests +from binascii import hexlify from beets import config from beets.plugins import BeetsPlugin __author__ = 'https://github.com/maffo999' +AUTH_TOKEN_VERSION = (1, 12) class SubsonicUpdate(BeetsPlugin): def __init__(self): super(SubsonicUpdate, self).__init__() - # Set default configuration values config['subsonic'].add({ 'user': 'admin', 'pass': 'admin', 'url': 'http://localhost:4040', }) - config['subsonic']['pass'].redact = True + self._version = None + self._auth = None self.register_listener('import', self.start_scan) + @property + def version(self): + if (self._version is None): + self._version = self.__get_version() + return self._version + + @property + def auth(self): + if (self._auth is None): + if(self.version is not None): + if self.version > AUTH_TOKEN_VERSION: + self._auth = "token" + else: + self._auth = "password" + self._log.info( + u"using '{}' authentication method".format(self._auth)) + return self._auth + @staticmethod def __create_token(): """Create salt and token from given password. @@ -67,10 +87,10 @@ class SubsonicUpdate(BeetsPlugin): return salt, token @staticmethod - def __format_url(): - """Get the Subsonic URL to trigger a scan. Uses either the url - config option or the deprecated host, port, and context_path config - options together. + def __format_url(endpoint): + """Get the Subsonic URL to trigger the given endpoint. + Uses either the url config option or the deprecated host, port, + and context_path config options together. :return: Endpoint for updating Subsonic """ @@ -88,22 +108,55 @@ class SubsonicUpdate(BeetsPlugin): context_path = '' url = "http://{}:{}{}".format(host, port, context_path) - return url + '/rest/startScan' - - def start_scan(self): - user = config['subsonic']['user'].as_str() - url = self.__format_url() - salt, token = self.__create_token() + return url + '/rest/{}'.format(endpoint) + def __get_version(self): + url = self.__format_url("ping.view") payload = { - 'u': user, - 't': token, - 's': salt, - 'v': '1.15.0', # Subsonic 6.1 and newer. 'c': 'beets', 'f': 'json' } + try: + response = requests.get(url, params=payload) + if response.status_code == 200: + json = response.json() + version = json['subsonic-response']['version'] + self._log.info( + u'subsonic version:{0} '.format(version)) + return tuple(int(s) for s in version.split('.')) + else: + self._log.error(u'Error: {0}', json) + return None + except Exception as error: + self._log.error(u'Error: {0}'.format(error)) + return None + def start_scan(self): + user = config['subsonic']['user'].as_str() + url = self.__format_url("startScan.view") + + if self.auth == 'token': + salt, token = self.__create_token() + payload = { + 'u': user, + 't': token, + 's': salt, + 'v': self.version, # Subsonic 6.1 and newer. + 'c': 'beets', + 'f': 'json' + } + elif self.auth == 'password': + password = config['subsonic']['pass'].as_str() + encpass = hexlify(password.encode()).decode() + payload = { + 'u': user, + 'p': 'enc:{}'.format(encpass), + 'v': self.version, + 'c': 'beets', + 'f': 'json' + } + else: + return try: response = requests.get(url, params=payload) json = response.json() diff --git a/docs/changelog.rst b/docs/changelog.rst index 4f5fdbddc..45f7fe4c6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -15,6 +15,7 @@ New features: * :doc:`/plugins/chroma`: Update file metadata after generating fingerprints through the `submit` command. * :doc:`/plugins/lastgenre`: Added more heavy metal genres: https://en.wikipedia.org/wiki/Heavy_metal_genres to genres.txt and genres-tree.yaml * :doc:`/plugins/subsonicplaylist`: import playlist from a subsonic server. +* :doc:`/plugins/subsonicupdate`: manage tocken and password authentifications method by checking server version. * A new :ref:`reflink` config option instructs the importer to create fast, copy-on-write file clones on filesystems that support them. Thanks to :user:`rubdos`. diff --git a/test/test_subsonicupdate.py b/test/test_subsonicupdate.py index c47208e65..dd254d593 100644 --- a/test/test_subsonicupdate.py +++ b/test/test_subsonicupdate.py @@ -39,9 +39,21 @@ class SubsonicPluginTest(_common.TestCase, TestHelper): config["subsonic"]["user"] = "admin" config["subsonic"]["pass"] = "admin" config["subsonic"]["url"] = "http://localhost:4040" - + responses.add( + responses.GET, + 'http://localhost:4040/rest/ping.view', + status=200, + body=self.PING_BODY + ) self.subsonicupdate = subsonicupdate.SubsonicUpdate() - + PING_BODY = ''' +{ + "subsonic-response": { + "status": "failed", + "version": "1.15.0" + } +} +''' SUCCESS_BODY = ''' { "subsonic-response": {