diff --git a/libs/common/bin/beet.exe b/libs/common/bin/beet.exe new file mode 100644 index 00000000..42738327 Binary files /dev/null and b/libs/common/bin/beet.exe differ diff --git a/libs/common/bin/chardetect.exe b/libs/common/bin/chardetect.exe new file mode 100644 index 00000000..fcb8a3ac Binary files /dev/null and b/libs/common/bin/chardetect.exe differ diff --git a/libs/common/bin/guessit.exe b/libs/common/bin/guessit.exe index 550c3cb3..c8d7abb2 100644 Binary files a/libs/common/bin/guessit.exe and b/libs/common/bin/guessit.exe differ diff --git a/libs/common/bin/mid3cp.exe b/libs/common/bin/mid3cp.exe new file mode 100644 index 00000000..fe24f111 Binary files /dev/null and b/libs/common/bin/mid3cp.exe differ diff --git a/libs/common/bin/mid3iconv.exe b/libs/common/bin/mid3iconv.exe new file mode 100644 index 00000000..7f67db01 Binary files /dev/null and b/libs/common/bin/mid3iconv.exe differ diff --git a/libs/common/bin/mid3v2.exe b/libs/common/bin/mid3v2.exe new file mode 100644 index 00000000..0faee10a Binary files /dev/null and b/libs/common/bin/mid3v2.exe differ diff --git a/libs/common/bin/moggsplit.exe b/libs/common/bin/moggsplit.exe new file mode 100644 index 00000000..ca7be52f Binary files /dev/null and b/libs/common/bin/moggsplit.exe differ diff --git a/libs/common/bin/mutagen-inspect.exe b/libs/common/bin/mutagen-inspect.exe new file mode 100644 index 00000000..eaf4de02 Binary files /dev/null and b/libs/common/bin/mutagen-inspect.exe differ diff --git a/libs/common/bin/mutagen-pony.exe b/libs/common/bin/mutagen-pony.exe new file mode 100644 index 00000000..9d4f4e07 Binary files /dev/null and b/libs/common/bin/mutagen-pony.exe differ diff --git a/libs/common/bin/unidecode.exe b/libs/common/bin/unidecode.exe new file mode 100644 index 00000000..a488e3d5 Binary files /dev/null and b/libs/common/bin/unidecode.exe differ diff --git a/libs/common/qbittorrent/client.py b/libs/common/qbittorrent/client.py index 08df1e63..9c734c2f 100644 --- a/libs/common/qbittorrent/client.py +++ b/libs/common/qbittorrent/client.py @@ -1,6 +1,7 @@ import requests import json + class LoginRequired(Exception): def __str__(self): return 'Please login first.' @@ -8,14 +9,27 @@ class LoginRequired(Exception): class Client(object): """class to interact with qBittorrent WEB API""" - def __init__(self, url): + def __init__(self, url, verify=True, timeout=None): + """ + Initialize the client + + :param url: Base URL of the qBittorrent WEB API + :param verify: Boolean to specify if SSL verification should be done. + Defaults to True. + :param timeout: How many seconds to wait for the server to send data + before giving up, as a float, or a + `(connect timeout, read timeout)` tuple. + Defaults to None. + """ if not url.endswith('/'): url += '/' - self.url = url + self.url = url + 'api/v2/' + self.verify = verify + self.timeout = timeout session = requests.Session() - check_prefs = session.get(url+'api/v2/app/preferences') - + prefs_url = self.url + 'app/preferences' + check_prefs = session.get(prefs_url, verify=self.verify, timeout=self.timeout) if check_prefs.status_code == 200: self._is_authenticated = True self.session = session @@ -23,9 +37,9 @@ class Client(object): elif check_prefs.status_code == 404: self._is_authenticated = False raise RuntimeError(""" - This wrapper only supports qBittorrent applications with - version higher than 4.1.0 (which implemented Web API v2.0). - Please use the latest qBittorrent release. + This wrapper only supports qBittorrent applications + with version higher than 4.1.x. + Please use the latest qBittorrent release. """) else: @@ -34,8 +48,10 @@ class Client(object): def _get(self, endpoint, **kwargs): """ Method to perform GET request on the API. + :param endpoint: Endpoint of the API. :param kwargs: Other keyword arguments for requests. + :return: Response of the GET request. """ return self._request(endpoint, 'get', **kwargs) @@ -43,9 +59,11 @@ class Client(object): def _post(self, endpoint, data, **kwargs): """ Method to perform POST request on the API. + :param endpoint: Endpoint of the API. :param data: POST DATA for the request. :param kwargs: Other keyword arguments for requests. + :return: Response of the POST request. """ return self._request(endpoint, 'post', data, **kwargs) @@ -53,10 +71,12 @@ class Client(object): def _request(self, endpoint, method, data=None, **kwargs): """ Method to hanle both GET and POST requests. + :param endpoint: Endpoint of the API. :param method: Method of HTTP request. :param data: POST DATA for the request. :param kwargs: Other keyword arguments. + :return: Response for the request. """ final_url = self.url + endpoint @@ -64,11 +84,12 @@ class Client(object): if not self._is_authenticated: raise LoginRequired - rq = self.session + kwargs['verify'] = self.verify + kwargs['timeout'] = self.timeout if method == 'get': - request = rq.get(final_url, **kwargs) + request = self.session.get(final_url, **kwargs) else: - request = rq.post(final_url, data, **kwargs) + request = self.session.post(final_url, data, **kwargs) request.raise_for_status() request.encoding = 'utf_8' @@ -86,17 +107,21 @@ class Client(object): def login(self, username='admin', password='admin'): """ Method to authenticate the qBittorrent Client. + Declares a class attribute named ``session`` which stores the authenticated session if the login is correct. Else, shows the login error. + :param username: Username. :param password: Password. + :return: Response to login request to the API. """ self.session = requests.Session() - login = self.session.post(self.url+'api/v2/auth/login', + login = self.session.post(self.url + 'auth/login', data={'username': username, - 'password': password}) + 'password': password}, + verify=self.verify) if login.text == 'Ok.': self._is_authenticated = True else: @@ -106,7 +131,7 @@ class Client(object): """ Logout the current session. """ - response = self._get('api/v2/auth/logout') + response = self._get('auth/logout') self._is_authenticated = False return response @@ -115,32 +140,55 @@ class Client(object): """ Get qBittorrent version. """ - return self._get('api/v2/app/version') + return self._get('app/version') @property def api_version(self): """ Get WEB API version. """ - return self._get('api/v2/app/webapiVersion') + return self._get('app/webapiVersion') def shutdown(self): """ Shutdown qBittorrent. """ - return self._get('api/v2/app/shutdown') + return self._get('app/shutdown') + + def get_default_save_path(self): + """ + Get default save path. + """ + return self._get('app/defaultSavePath') + + def get_log(self, **params): + """ + Returns a list of log entries matching the supplied params. + + :param normal: Include normal messages (default: true). + :param info: Include info messages (default: true). + :param warning: Include warning messages (default: true). + :param critical: Include critical messages (default: true). + :param last_known_id: Exclude messages with "message id" <= last_known_id (default: -1). + + :return: list(). + For example: qb.get_log(normal='true', info='true') + """ + return self._get('log/main', params=params) def torrents(self, **filters): """ Returns a list of torrents matching the supplied filters. + :param filter: Current status of the torrents. :param category: Fetch all torrents with the supplied label. :param sort: Sort torrents by. :param reverse: Enable reverse sorting. :param limit: Limit the number of torrents returned. :param offset: Set offset (if less than 0, offset from end). - :param hashes: Filter by hashes. Can contain multiple hashes separated by |. + :return: list() of torrent with matching filter. + For example: qb.torrents(filter='downloading', sort='ratio'). """ params = {} for name, value in filters.items(): @@ -148,42 +196,65 @@ class Client(object): name = 'filter' if name == 'status' else name params[name] = value - return self._get('api/v2/torrents/info', params=params) + return self._get('torrents/info', params=params) def get_torrent(self, infohash): """ Get details of the torrent. + :param infohash: INFO HASH of the torrent. """ - return self._get('api/v2/torrents/properties', params={'hash': infohash.lower()}) + return self._get('torrents/properties?hash=' + infohash.lower()) def get_torrent_trackers(self, infohash): """ Get trackers for the torrent. + :param infohash: INFO HASH of the torrent. """ - return self._get('api/v2/torrents/trackers', params={'hash': infohash.lower()}) + return self._get('torrents/trackers?hash=' + infohash.lower()) def get_torrent_webseeds(self, infohash): """ Get webseeds for the torrent. + :param infohash: INFO HASH of the torrent. """ - return self._get('api/v2/torrents/webseeds', params={'hash': infohash.lower()}) + return self._get('torrents/webseeds?hash=' + infohash.lower()) def get_torrent_files(self, infohash): """ Get list of files for the torrent. + :param infohash: INFO HASH of the torrent. """ - return self._get('api/v2/torrents/files', params={'hash': infohash.lower()}) + return self._get('torrents/files?hash=' + infohash.lower()) + + def get_torrent_piece_states(self, infohash): + """ + Get list of all pieces (in order) of a specific torrent. + + :param infohash: INFO HASH of the torrent. + :return: array of states (integers). + """ + return self._get('torrents/pieceStates?hash=' + infohash.lower()) + + def get_torrent_piece_hashes(self, infohash): + """ + Get list of all hashes (in order) of a specific torrent. + + :param infohash: INFO HASH of the torrent. + :return: array of hashes (strings). + """ + return self._get('torrents/pieceHashes?hash=' + infohash.lower()) @property def global_transfer_info(self): """ - Get JSON data of the global transfer info of qBittorrent. + :return: dict{} of the global transfer info of qBittorrent. + """ - return self._get('api/v2/transfer/info') + return self._get('transfer/info') @property def preferences(self): @@ -192,31 +263,43 @@ class Client(object): Can also be used to assign individual preferences. For setting multiple preferences at once, see ``set_preferences`` method. + Note: Even if this is a ``property``, to fetch the current preferences dict, you are required to call it like a bound method. + Wrong:: + qb.preferences + Right:: + qb.preferences() + """ - prefs = self._get('api/v2/app/preferences') + prefs = self._get('app/preferences') class Proxy(Client): """ Proxy class to to allow assignment of individual preferences. this class overrides some methods to ease things. + Because of this, settings can be assigned like:: + In [5]: prefs = qb.preferences() + In [6]: prefs['autorun_enabled'] Out[6]: True + In [7]: prefs['autorun_enabled'] = False + In [8]: prefs['autorun_enabled'] Out[8]: False + """ def __init__(self, url, prefs, auth, session): - super(Proxy, self).__init__(url) + self.url = url self.prefs = prefs self._is_authenticated = auth self.session = session @@ -233,77 +316,118 @@ class Client(object): return Proxy(self.url, prefs, self._is_authenticated, self.session) - def sync(self, rid=0): + def sync_main_data(self, rid=0): """ - Sync the torrents by supplied LAST RESPONSE ID. + Sync the torrents main data by supplied LAST RESPONSE ID. Read more @ https://git.io/fxgB8 + :param rid: Response ID of last request. """ - return self._get('api/v2/sync/maindata', params={'rid': rid}) + return self._get('sync/maindata', params={'rid': rid}) + + def sync_peers_data(self, infohash, rid=0): + """ + Sync the torrent peers data by supplied LAST RESPONSE ID. + Read more @ https://git.io/fxgBg + + :param infohash: INFO HASH of torrent. + :param rid: Response ID of last request. + """ + params = {'hash': infohash.lower(), 'rid': rid} + return self._get('sync/torrentPeers', params=params) def download_from_link(self, link, **kwargs): """ Download torrent using a link. + :param link: URL Link or list of. :param savepath: Path to download the torrent. :param category: Label or Category of the torrent(s). + :return: Empty JSON data. """ - # qBittorrent requires adds to be done with multipath/form-data - # POST requests for both URLs and .torrent files. Info on this - # can be found here, and here: - # http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file - # http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files + # old:new format + old_arg_map = {'save_path': 'savepath'} # , 'label': 'category'} + + # convert old option names to new option names + options = kwargs.copy() + for old_arg, new_arg in old_arg_map.items(): + if options.get(old_arg) and not options.get(new_arg): + options[new_arg] = options[old_arg] + if isinstance(link, list): - links = '\n'.join(link) + options['urls'] = "\n".join(link) else: - links = link - torrent_data = {} - torrent_data['urls'] = (None, links) - for k, v in kwargs.iteritems(): - torrent_data[k] = (None, v) - return self._post('api/v2/torrents/add', data=None, files=torrent_data) + options['urls'] = link + + # workaround to send multipart/formdata request + # http://stackoverflow.com/a/23131823/4726598 + dummy_file = {'_dummy': (None, '_dummy')} + + return self._post('torrents/add', data=options, files=dummy_file) def download_from_file(self, file_buffer, **kwargs): """ Download torrent using a file. + :param file_buffer: Single file() buffer or list of. :param save_path: Path to download the torrent. :param label: Label of the torrent(s). + :return: Empty JSON data. """ - # qBittorrent requires adds to be done with multipath/form-data - # POST requests for both URLs and .torrent files. Info on this - # can be found here, and here: - # http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file - # http://docs.python-requests.org/en/master/user/advanced/#post-multiple-multipart-encoded-files if isinstance(file_buffer, list): - torrent_data = [] - for f in file_buffer: - fname = f.name - torrent_data.append(('torrents', (fname, f))) + torrent_files = {} + for i, f in enumerate(file_buffer): + torrent_files.update({'torrents%s' % i: f}) else: - fname = file_buffer.name - torrent_data = [('torrents', (fname, file_buffer))] - for k, v in kwargs.iteritems(): - torrent_data.append((k, (None, v))) + torrent_files = {'torrents': file_buffer} - return self._post('api/v2/torrents/add', data=None, files=torrent_data) + data = kwargs.copy() + + if data.get('save_path'): + data.update({'savepath': data['save_path']}) + return self._post('torrents/add', data=data, files=torrent_files) def add_trackers(self, infohash, trackers): """ Add trackers to a torrent. + :param infohash: INFO HASH of torrent. :param trackers: Trackers. + :note %0A (aka LF newline) between trackers. Ampersand in tracker urls MUST be escaped. """ data = {'hash': infohash.lower(), 'urls': trackers} - return self._post('api/v2/torrents/addTrackers', data=data) + return self._post('torrents/addTrackers', data=data) + + def set_torrent_location(self, infohash_list, location): + """ + Set the location for the torrent + + :param infohash: INFO HASH of torrent. + :param location: /mnt/nfs/media. + """ + data = self._process_infohash_list(infohash_list) + data['location'] = location + return self._post('torrents/setLocation', data=data) + + def set_torrent_name(self, infohash, name): + """ + Set the name for the torrent + + :param infohash: INFO HASH of torrent. + :param name: Whatever_name_you_want. + """ + data = {'hash': infohash.lower(), + 'name': name} + return self._post('torrents/rename', data=data) @staticmethod def _process_infohash_list(infohash_list): """ Method to convert the infohash_list to qBittorrent API friendly values. + :param infohash_list: List of infohash. """ if isinstance(infohash_list, list): @@ -315,122 +439,193 @@ class Client(object): def pause(self, infohash): """ Pause a torrent. + :param infohash: INFO HASH of torrent. """ - return self._post('api/v2/torrents/pause', data={'hashes': infohash.lower()}) + return self._post('torrents/pause', data={'hashes': infohash.lower()}) def pause_all(self): """ Pause all torrents. """ - return self._post('api/v2/torrents/pause', data={'hashes': 'all'}) + return self._post('torrents/pause', data={'hashes': 'all'}) def pause_multiple(self, infohash_list): """ Pause multiple torrents. + :param infohash_list: Single or list() of infohashes. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/pause', data=data) + return self._post('torrents/pause', data=data) def set_category(self, infohash_list, category): """ Set the category on multiple torrents. + + The category must exist before using set_category. As of v2.1.0,the API + returns a 409 Client Error for any valid category name that doesn't + already exist. + :param infohash_list: Single or list() of infohashes. + :param category: If category is set to empty string '', + the torrent(s) specified is/are removed from all categories. """ data = self._process_infohash_list(infohash_list) data['category'] = category - return self._post('api/v2/torrents/setCategory', data=data) + return self._post('torrents/setCategory', data=data) + + def create_category(self, category): + """ + Create a new category + :param category: category to create + """ + return self._post('torrents/createCategory', data={'category': category.lower()}) + + def remove_category(self, categories): + """ + Remove categories + + :param categories: can contain multiple cateogies separated by \n (%0A urlencoded). + """ + + return self._post('torrents/removeCategories', data={'categories': categories}) def resume(self, infohash): """ Resume a paused torrent. + :param infohash: INFO HASH of torrent. """ - return self._post('api/v2/torrents/resume', data={'hashes': infohash.lower()}) + return self._post('torrents/resume', data={'hashes': infohash.lower()}) def resume_all(self): """ Resume all torrents. """ - return self._get('api/v2/torrents/resume', data={'hashes': 'all'}) + return self._post('torrents/resume', data={'hashes': 'all'}) def resume_multiple(self, infohash_list): """ Resume multiple paused torrents. + :param infohash_list: Single or list() of infohashes. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/resume', data=data) + return self._post('torrents/resume', data=data) def delete(self, infohash_list): """ - Delete torrents. + Delete torrents. Does not remove files. + :param infohash_list: Single or list() of infohashes. """ - data = self._process_infohash_list(infohash_list) - data['deleteFiles'] = 'false' - return self._post('api/v2/torrents/delete', data=data) + return self._delete(infohash_list) + + def delete_all(self): + """ + Delete all torrents. Does not remove files. + """ + return self._delete('all') def delete_permanently(self, infohash_list): """ - Permanently delete torrents. - *** WARNING : This will instruct qBittorrent to delete files - *** from your hard disk. Use with caution. + Permanently delete torrents. Removes files. + :param infohash_list: Single or list() of infohashes. """ + return self._delete(infohash_list, True) + + def delete_all_permanently(self): + """ + Permanently delete torrents. + """ + return self._delete('all', True) + + def _delete(self, infohash_list, delete_files=False): + """ + Delete torrents. + + :param infohash_list: Single or list() of infohashes. + :param delete_files: Whether to delete files along with torrent. + """ data = self._process_infohash_list(infohash_list) - data['deleteFiles'] = 'true' - return self._post('api/v2/torrents/delete', data=data) + data['deleteFiles'] = json.dumps(delete_files) + return self._post('torrents/delete', data=data) def recheck(self, infohash_list): """ Recheck torrents. + :param infohash_list: Single or list() of infohashes. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/recheck', data=data) + return self._post('torrents/recheck', data=data) + + def recheck_all(self): + """ + Recheck all torrents. + """ + return self._post('torrents/recheck', data={'hashes': 'all'}) + + def reannounce(self, infohash_list): + """ + Recheck all torrents. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. + """ + + data = self._process_infohash_list(infohash_list) + return self._post('torrents/reannounce', data=data) def increase_priority(self, infohash_list): """ Increase priority of torrents. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/increasePrio', data=data) + return self._post('torrents/increasePrio', data=data) def decrease_priority(self, infohash_list): """ Decrease priority of torrents. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/decreasePrio', data=data) + return self._post('torrents/decreasePrio', data=data) def set_max_priority(self, infohash_list): """ Set torrents to maximum priority level. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/topPrio', data=data) + return self._post('torrents/topPrio', data=data) def set_min_priority(self, infohash_list): """ Set torrents to minimum priority level. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/bottomPrio', data=data) + return self._post('torrents/bottomPrio', data=data) def set_file_priority(self, infohash, file_id, priority): """ Set file of a torrent to a supplied priority level. + :param infohash: INFO HASH of torrent. :param file_id: ID of the file to set priority. :param priority: Priority level of the file. + + :note Priorities Don't download, Normal, High, Maximum + in 3.2.0-4.1+ are 0, 1, 6, 7 and in 3.1.x are 0, 1, 2, 7 """ - if priority not in [0, 1, 6, 7]: + if priority not in [0, 1, 2, 4, 6, 7]: raise ValueError("Invalid priority, refer WEB-UI docs for info.") elif not isinstance(file_id, int): raise TypeError("File ID must be an int") @@ -439,7 +634,18 @@ class Client(object): 'id': file_id, 'priority': priority} - return self._post('api/v2/torrents/filePrio', data=data) + return self._post('torrents/filePrio', data=data) + + def set_automatic_torrent_management(self, infohash_list, enable='false'): + """ + Set the category on multiple torrents. + + :param infohash_list: Single or list() of infohashes. + :param enable: is a boolean, affects the torrents listed in infohash_list, default is 'false' + """ + data = self._process_infohash_list(infohash_list) + data['enable'] = enable + return self._post('torrents/setAutoManagement', data=data) # Get-set global download and upload speed limits. @@ -447,14 +653,15 @@ class Client(object): """ Get global download speed limit. """ - return self._get('api/v2/transfer/downloadLimit') + return self._get('transfer/downloadLimit') def set_global_download_limit(self, limit): """ Set global download speed limit. + :param limit: Speed limit in bytes. """ - return self._post('api/v2/transfer/setDownloadLimit', data={'limit': limit}) + return self._post('transfer/setDownloadLimit', data={'limit': limit}) global_download_limit = property(get_global_download_limit, set_global_download_limit) @@ -463,14 +670,15 @@ class Client(object): """ Get global upload speed limit. """ - return self._get('api/v2/transfer/uploadLimit') + return self._get('transfer/uploadLimit') def set_global_upload_limit(self, limit): """ Set global upload speed limit. + :param limit: Speed limit in bytes. """ - return self._post('api/v2/transfer/setUploadLimit', data={'limit': limit}) + return self._post('transfer/setUploadLimit', data={'limit': limit}) global_upload_limit = property(get_global_upload_limit, set_global_upload_limit) @@ -479,56 +687,62 @@ class Client(object): def get_torrent_download_limit(self, infohash_list): """ Get download speed limit of the supplied torrents. + :param infohash_list: Single or list() of infohashes. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/downloadLimit', data=data) + return self._post('torrents/downloadLimit', data=data) def set_torrent_download_limit(self, infohash_list, limit): """ Set download speed limit of the supplied torrents. + :param infohash_list: Single or list() of infohashes. :param limit: Speed limit in bytes. """ data = self._process_infohash_list(infohash_list) data.update({'limit': limit}) - return self._post('api/v2/torrents/setDownloadLimit', data=data) + return self._post('torrents/setDownloadLimit', data=data) def get_torrent_upload_limit(self, infohash_list): """ Get upoload speed limit of the supplied torrents. + :param infohash_list: Single or list() of infohashes. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/uploadLimit', data=data) + return self._post('torrents/uploadLimit', data=data) def set_torrent_upload_limit(self, infohash_list, limit): """ Set upload speed limit of the supplied torrents. + :param infohash_list: Single or list() of infohashes. :param limit: Speed limit in bytes. """ data = self._process_infohash_list(infohash_list) data.update({'limit': limit}) - return self._post('api/v2/torrents/setUploadLimit', data=data) + return self._post('torrents/setUploadLimit', data=data) # setting preferences def set_preferences(self, **kwargs): """ Set preferences of qBittorrent. Read all possible preferences @ https://git.io/fx2Y9 + :param kwargs: set preferences in kwargs form. """ json_data = "json={}".format(json.dumps(kwargs)) headers = {'content-type': 'application/x-www-form-urlencoded'} - return self._post('api/v2/app/setPreferences', data=json_data, + return self._post('app/setPreferences', data=json_data, headers=headers) def get_alternative_speed_status(self): """ Get Alternative speed limits. (1/0) """ - return self._get('api/v2/transfer/speedLimitsMode') + + return self._get('transfer/speedLimitsMode') alternative_speed_status = property(get_alternative_speed_status) @@ -536,30 +750,44 @@ class Client(object): """ Toggle alternative speed limits. """ - return self._get('api/v2/transfer/toggleSpeedLimitsMode') + return self._get('transfer/toggleSpeedLimitsMode') def toggle_sequential_download(self, infohash_list): """ Toggle sequential download in supplied torrents. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/toggleSequentialDownload', data=data) + return self._post('torrents/toggleSequentialDownload', data=data) def toggle_first_last_piece_priority(self, infohash_list): """ Toggle first/last piece priority of supplied torrents. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. """ data = self._process_infohash_list(infohash_list) - return self._post('api/v2/torrents/toggleFirstLastPiecePrio', data=data) + return self._post('torrents/toggleFirstLastPiecePrio', data=data) - def force_start(self, infohash_list, value=True): + def force_start(self, infohash_list, value): """ Force start selected torrents. - :param infohash_list: Single or list() of infohashes. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. :param value: Force start value (bool) """ data = self._process_infohash_list(infohash_list) data.update({'value': json.dumps(value)}) - return self._post('api/v2/torrents/setForceStart', data=data) + return self._post('torrents/setForceStart', data=data) + + def set_super_seeding(self, infohash_list, value): + """ + Set super seeding for selected torrents. + + :param infohash_list: Single or list() of infohashes; pass 'all' for all torrents. + :param value: Force start value (bool) + """ + data = self._process_infohash_list(infohash_list) + data.update({'value': json.dumps(value)}) + return self._post('torrents/setSuperSeeding', data=data)