From deda4bc65ac9aecad0979d4a3e10b5f48aafa6b4 Mon Sep 17 00:00:00 2001 From: Sami Haahtinen Date: Wed, 6 May 2015 20:25:12 +0300 Subject: [PATCH] Change request handling in torrent clients Simplify request responses within torrent clients. There is no need to pass the response through the parent class when dealing with requests. In many places this makes the code more readable and simplifies logic. --- CHANGES.md | 1 + sickbeard/clients/deluge.py | 241 +++++++++++++------------- sickbeard/clients/download_station.py | 14 +- sickbeard/clients/generic.py | 91 +++++----- sickbeard/clients/transmission.py | 32 ++-- sickbeard/clients/utorrent.py | 15 +- 6 files changed, 203 insertions(+), 191 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 28a3ee34..9f463eb7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,7 @@ * Add General Config/Interface/"Group show list shows into:"... to divide shows into groups on the Show List page * Change Show List progress bar code, smaller page load, efficient use of js render engine * Change values used for date sorting on home page and episode view for improved compatibility with posix systems +* Change response handling in downloaders to simplify logic. [develop changelog] Fix issue changing a custom show list group name that is in use. The bug resulted in the db containing stale group names diff --git a/sickbeard/clients/deluge.py b/sickbeard/clients/deluge.py index a735de1b..1db6f868 100644 --- a/sickbeard/clients/deluge.py +++ b/sickbeard/clients/deluge.py @@ -22,6 +22,7 @@ from base64 import b64encode import sickbeard from sickbeard import logger from sickbeard.clients.generic import GenericClient +from lib.requests.exceptions import RequestException class DelugeAPI(GenericClient): @@ -33,134 +34,148 @@ class DelugeAPI(GenericClient): def _get_auth(self): - post_data = json.dumps({"method": "auth.login", - "params": [self.password], - "id": 1 - }) + post_data = json.dumps({'method': 'auth.login', + 'params': [self.password], + 'id': 1}) + try: - self.response = self.session.post(self.url, data=post_data.encode('utf-8'), verify=sickbeard.TORRENT_VERIFY_CERT) - except: - return None + self.auth = self.session.post( + self.url, + data=post_data.encode('utf-8'), + verify=sickbeard.TORRENT_VERIFY_CERT + ).json()['result'] - self.auth = self.response.json()["result"] + post_data = json.dumps({'method': 'web.connected', + 'params': [], + 'id': 10}) - post_data = json.dumps({"method": "web.connected", - "params": [], - "id": 10 - }) - try: - self.response = self.session.post(self.url, data=post_data.encode('utf-8'), verify=sickbeard.TORRENT_VERIFY_CERT) - except: - return None + connected = self.session.post( + self.url, + data=post_data.encode('utf-8'), + verify=sickbeard.TORRENT_VERIFY_CERT + ).json()['result'] - connected = self.response.json()['result'] - - if not connected: - post_data = json.dumps({"method": "web.get_hosts", - "params": [], - "id": 11 - }) - try: - self.response = self.session.post(self.url, data=post_data.encode('utf-8'), verify=sickbeard.TORRENT_VERIFY_CERT) - except: - return None - hosts = self.response.json()['result'] - if len(hosts) == 0: - logger.log(self.name + u': WebUI does not contain daemons', logger.ERROR) - return None - - post_data = json.dumps({"method": "web.connect", - "params": [hosts[0][0]], - "id": 11 - }) - try: - self.response = self.session.post(self.url, data=post_data.encode('utf-8'), verify=sickbeard.TORRENT_VERIFY_CERT) - except: - return None - - post_data = json.dumps({"method": "web.connected", - "params": [], - "id": 10 - }) - try: - self.response = self.session.post(self.url, data=post_data.encode('utf-8'), verify=sickbeard.TORRENT_VERIFY_CERT) - except: - return None - - connected = self.response.json()['result'] if not connected: - logger.log(self.name + u': WebUI could not connect to daemon', logger.ERROR) + post_data = json.dumps({'method': 'web.get_hosts', + 'params': [], + 'id': 11}) + + hosts = self.session.post( + self.url, + data=post_data.encode('utf-8'), + verify=sickbeard.TORRENT_VERIFY_CERT + ).json()['result'] + + if len(hosts) == 0: + logger.log(self.name + u': WebUI does not contain daemons', + logger.ERROR) + return None + + post_data = json.dumps({'method': 'web.connect', + 'params': [hosts[0][0]], + 'id': 11}) + self.session.post(self.url, data=post_data.encode('utf-8'), + verify=sickbeard.TORRENT_VERIFY_CERT) + + post_data = json.dumps({'method': 'web.connected', + 'params': [], + 'id': 10}) + connected = self.session.post( + self.url, + data=post_data.encode('utf-8'), + verify=sickbeard.TORRENT_VERIFY_CERT + ).json()['result'] + + if not connected: + logger.log(self.name + u': WebUI could not connect to daemon', + logger.ERROR) return None + except RequestException: + return None return self.auth def _add_torrent_uri(self, result): - post_data = json.dumps({"method": "core.add_torrent_magnet", - "params": [result.url, {"move_completed": "true", - "move_completed_path": sickbeard.TV_DOWNLOAD_DIR}], - "id": 2 + post_data = json.dumps({ + 'method': 'core.add_torrent_magnet', + 'params': [result.url, { + 'move_completed': 'true', + 'move_completed_path': sickbeard.TV_DOWNLOAD_DIR + }], + 'id': 2 }) - self._request(method='post', data=post_data) + result.hash = self._request(method='post', + data=post_data).json()['result'] - result.hash = self.response.json()['result'] - - return self.response.json()['result'] + return result.hash def _add_torrent_file(self, result): - post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result.name + '.torrent', b64encode(result.content), - {"move_completed": "true", - "move_completed_path": sickbeard.TV_DOWNLOAD_DIR}], - "id": 2 - }) - self._request(method='post', data=post_data) + post_data = json.dumps({'method': + 'core.add_torrent_file', + 'params': [result.name + '.torrent', + b64encode(result.content), + {'move_completed': 'true', + 'move_completed_path': + sickbeard.TV_DOWNLOAD_DIR}], + 'id': 2}) + result.hash = self._request(method='post', + data=post_data).json()['result'] - result.hash = self.response.json()['result'] - - return self.response.json()['result'] + return result.hash def _set_torrent_label(self, result): label = sickbeard.TORRENT_LABEL if ' ' in label: - logger.log(self.name + u': Invalid label. Label must not contain a space', logger.ERROR) + logger.log(self.name + + u': Invalid label. Label must not contain a space', + logger.ERROR) return False if label: # check if label already exists and create it if not - post_data = json.dumps({"method": 'label.get_labels', - "params": [], - "id": 3 + post_data = json.dumps({ + 'method': 'label.get_labels', + 'params': [], + 'id': 3 }) - self._request(method='post', data=post_data) - labels = self.response.json()['result'] + labels = self._request(method='post', + data=post_data).json()['result'] - if labels != None: + if labels is not None: if label not in labels: - logger.log(self.name + ': ' + label + u" label does not exist in Deluge we must add it", + logger.log(self.name + ': ' + label + + u' label does not exist in ' + + u'Deluge we must add it', logger.DEBUG) - post_data = json.dumps({"method": 'label.add', - "params": [label], - "id": 4 + post_data = json.dumps({ + 'method': 'label.add', + 'params': [label], + 'id': 4 }) self._request(method='post', data=post_data) - logger.log(self.name + ': ' + label + u" label added to Deluge", logger.DEBUG) + logger.log(self.name + ': ' + label + + u' label added to Deluge', logger.DEBUG) - # add label to torrent - post_data = json.dumps({"method": 'label.set_torrent', - "params": [result.hash, label], - "id": 5 + # add label to torrent + post_data = json.dumps({ + 'method': 'label.set_torrent', + 'params': [result.hash, label], + 'id': 5 }) self._request(method='post', data=post_data) - logger.log(self.name + ': ' + label + u" label added to torrent", logger.DEBUG) + logger.log(self.name + ': ' + label + + u' label added to torrent', + logger.DEBUG) else: - logger.log(self.name + ': ' + u"label plugin not detected", logger.DEBUG) + logger.log(self.name + ': ' + + u'label plugin not detected', + logger.DEBUG) return False - return not self.response.json()['error'] - + return True def _set_torrent_ratio(self, result): @@ -169,53 +184,43 @@ class DelugeAPI(GenericClient): ratio = result.ratio if ratio: - post_data = json.dumps({"method": "core.set_torrent_stop_at_ratio", - "params": [result.hash, True], - "id": 5 - }) + post_data = json.dumps({'method': 'core.set_torrent_stop_at_ratio', + 'params': [result.hash, True], + 'id': 5}) self._request(method='post', data=post_data) - post_data = json.dumps({"method": "core.set_torrent_stop_ratio", - "params": [result.hash, float(ratio)], - "id": 6 - }) + post_data = json.dumps({'method': 'core.set_torrent_stop_ratio', + 'params': [result.hash, float(ratio)], + 'id': 6}) self._request(method='post', data=post_data) - - return not self.response.json()['error'] - return True def _set_torrent_path(self, result): if sickbeard.TORRENT_PATH: - post_data = json.dumps({"method": "core.set_torrent_move_completed", - "params": [result.hash, True], - "id": 7 + post_data = json.dumps({ + 'method': 'core.set_torrent_move_completed', + 'params': [result.hash, True], + 'id': 7 }) self._request(method='post', data=post_data) - post_data = json.dumps({"method": "core.set_torrent_move_completed_path", - "params": [result.hash, sickbeard.TORRENT_PATH], - "id": 8 + post_data = json.dumps({ + 'method': 'core.set_torrent_move_completed_path', + 'params': [result.hash, sickbeard.TORRENT_PATH], + 'id': 8 }) self._request(method='post', data=post_data) - - return not self.response.json()['error'] - return True def _set_torrent_pause(self, result): if sickbeard.TORRENT_PAUSED: - post_data = json.dumps({"method": "core.pause_torrent", - "params": [[result.hash]], - "id": 9 - }) + post_data = json.dumps({'method': 'core.pause_torrent', + 'params': [[result.hash]], + 'id': 9}) self._request(method='post', data=post_data) - - return not self.response.json()['error'] - return True -api = DelugeAPI() \ No newline at end of file +api = DelugeAPI() diff --git a/sickbeard/clients/download_station.py b/sickbeard/clients/download_station.py index 532d5628..4238f5b2 100644 --- a/sickbeard/clients/download_station.py +++ b/sickbeard/clients/download_station.py @@ -36,8 +36,8 @@ class DownloadStationAPI(GenericClient): auth_url = self.host + 'webapi/auth.cgi?api=SYNO.API.Auth&version=2&method=login&account=' + self.username + '&passwd=' + self.password + '&session=DownloadStation&format=sid' try: - self.response = self.session.get(auth_url, verify=False) - self.auth = self.response.json()['data']['sid'] + response = self.session.get(auth_url, verify=False) + self.auth = response.json()['data']['sid'] except: return None @@ -53,9 +53,9 @@ class DownloadStationAPI(GenericClient): } if sickbeard.TORRENT_PATH: data['destination'] = sickbeard.TORRENT_PATH - self._request(method='post', data=data) + response = self._request(method='post', data=data) - return self.response.json()['success'] + return response.json()['success'] def _add_torrent_file(self, result): @@ -68,8 +68,8 @@ class DownloadStationAPI(GenericClient): if sickbeard.TORRENT_PATH: data['destination'] = sickbeard.TORRENT_PATH files = {'file':(result.name + '.torrent', result.content)} - self._request(method='post', data=data, files=files) + response = self._request(method='post', data=data, files=files) - return self.response.json()['success'] + return response.json()['success'] -api = DownloadStationAPI() \ No newline at end of file +api = DownloadStationAPI() diff --git a/sickbeard/clients/generic.py b/sickbeard/clients/generic.py index d188d25d..d31bed14 100644 --- a/sickbeard/clients/generic.py +++ b/sickbeard/clients/generic.py @@ -9,7 +9,7 @@ from sickbeard.exceptions import ex from sickbeard.clients import http_error_code from lib.bencode import bencode, bdecode from lib import requests -from lib.requests import exceptions + class GenericClient(object): def __init__(self, name, host=None, username=None, password=None): @@ -20,34 +20,38 @@ class GenericClient(object): self.host = sickbeard.TORRENT_HOST if host is None else host self.url = None - self.response = None self.auth = None self.last_time = time.time() self.session = requests.session() self.session.auth = (self.username, self.password) def _request(self, method='get', params={}, data=None, files=None): - + response = None if time.time() > self.last_time + 1800 or not self.auth: self.last_time = time.time() self._get_auth() logger.log( - self.name + u': Requested a ' + method.upper() + ' connection to url ' + self.url + ' with Params= ' + str( - params) + ' Data=' + str(data if data else 'None')[0:99] + ( - '...' if len(data if data else 'None') > 200 else ''), logger.DEBUG) + self.name + u': Requested a ' + method.upper() + + ' connection to url ' + self.url + ' with Params= ' + str(params) + + ' Data=' + str(data if data else 'None')[0:99] + + ('...' if len(data if data else 'None') > 200 else ''), + logger.DEBUG + ) logger.log( - self.name + u': Requested a ' + method.upper() + ' connection to url ' + self.url + ' with Params= ' + str( - params) + ( - (' Data=' + str(data)[0:100] + ('...' if len(data) > 100 else '')) if data is not None else ""), - logger.DEBUG) + self.name + u': Requested a ' + method.upper() + + ' connection to url ' + self.url + ' with Params= ' + str(params) + + ((' Data=' + str(data)[0:100] + ('...' if len(data) > 100 else '')) + if data is not None else ''), + logger.DEBUG + ) if not self.auth: logger.log(self.name + u': Authentication Failed', logger.ERROR) return False try: - self.response = self.session.__getattribute__(method)(self.url, params=params, data=data, files=files, + response = self.session.__getattribute__(method)(self.url, params=params, data=data, files=files, timeout=120, verify=False) except requests.exceptions.ConnectionError, e: logger.log(self.name + u': Unable to connect ' + ex(e), logger.ERROR) @@ -66,78 +70,78 @@ class GenericClient(object): logger.ERROR) return False - if self.response.status_code == 401: + if response.status_code == 401: logger.log(self.name + u': Invalid Username or Password, check your config', logger.ERROR) return False - if self.response.status_code in http_error_code.keys(): - logger.log(self.name + u': ' + http_error_code[self.response.status_code], logger.DEBUG) + if response.status_code in http_error_code.keys(): + logger.log(self.name + u': ' + http_error_code[response.status_code], logger.DEBUG) return False - logger.log(self.name + u': Response to ' + method.upper() + ' request is ' + self.response.text, logger.DEBUG) + logger.log(self.name + u': Response to ' + method.upper() + ' request is ' + response.text, logger.DEBUG) - return True + return response def _get_auth(self): - """ + ''' This should be overridden and should return the auth_id needed for the client - """ + ''' return None def _add_torrent_uri(self, result): - """ + ''' This should be overridden should return the True/False from the client when a torrent is added via url (magnet or .torrent link) - """ + ''' return False def _add_torrent_file(self, result): - """ + ''' This should be overridden should return the True/False from the client when a torrent is added via result.content (only .torrent file) - """ + ''' return False def _set_torrent_label(self, result): - """ + ''' This should be overridden should return the True/False from the client when a torrent is set with label - """ + ''' return True def _set_torrent_ratio(self, result): - """ + ''' This should be overridden should return the True/False from the client when a torrent is set with ratio - """ + ''' return True def _set_torrent_seed_time(self, result): - """ + ''' This should be overridden should return the True/False from the client when a torrent is set with a seed time - """ + ''' return True def _set_torrent_priority(self, result): - """ + ''' This should be overriden should return the True/False from the client when a torrent is set with result.priority (-1 = low, 0 = normal, 1 = high) - """ + ''' return True def _set_torrent_path(self, torrent_path): - """ + ''' This should be overridden should return the True/False from the client when a torrent is set with path - """ + ''' return True def _set_torrent_pause(self, result): - """ + ''' This should be overridden should return the True/False from the client when a torrent is set with pause - """ + ''' return True def _get_torrent_hash(self, result): @@ -148,7 +152,7 @@ class GenericClient(object): result.hash = b16encode(b32decode(result.hash)).lower() else: result.content = result.provider.getURL(result.url) - info = bdecode(result.content)["info"] + info = bdecode(result.content)['info'] result.hash = sha1(bencode(info)).hexdigest() return result @@ -205,20 +209,21 @@ class GenericClient(object): return r_code def testAuthentication(self): - + response = None try: - self.response = self.session.get(self.url, timeout=120, verify=False) - except requests.exceptions.ConnectionError, e: + response = self.session.get(self.url, timeout=120, verify=False) + + if response.status_code == 401: + return False, 'Error: Invalid ' + self.name + ' Username or Password, check your config!' + except requests.exceptions.ConnectionError: return False, 'Error: ' + self.name + ' Connection Error' except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL): return False, 'Error: Invalid ' + self.name + ' host' - if self.response.status_code == 401: - return False, 'Error: Invalid ' + self.name + ' Username or Password, check your config!' - try: - self._get_auth() - if self.response.status_code == 200 and self.auth: + authenticated = self._get_auth() + # FIXME: This test is redundant + if authenticated and self.auth: return True, 'Success: Connected and Authenticated' else: return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!' diff --git a/sickbeard/clients/transmission.py b/sickbeard/clients/transmission.py index bce6b9fa..da551e54 100755 --- a/sickbeard/clients/transmission.py +++ b/sickbeard/clients/transmission.py @@ -37,9 +37,9 @@ class TransmissionAPI(GenericClient): post_data = json.dumps({'method': 'session-get', }) try: - self.response = self.session.post(self.url, data=post_data.encode('utf-8'), timeout=120, + response = self.session.post(self.url, data=post_data.encode('utf-8'), timeout=120, verify=sickbeard.TORRENT_VERIFY_CERT) - self.auth = re.search('X-Transmission-Session-Id:\s*(\w+)', self.response.text).group(1) + self.auth = re.search('X-Transmission-Session-Id:\s*(\w+)', response.text).group(1) except: return None @@ -62,9 +62,9 @@ class TransmissionAPI(GenericClient): post_data = json.dumps({'arguments': arguments, 'method': 'torrent-add', }) - self._request(method='post', data=post_data) + response = self._request(method='post', data=post_data) - return self.response.json()['result'] == "success" + return response.json()['result'] == 'success' def _add_torrent_file(self, result): @@ -75,9 +75,9 @@ class TransmissionAPI(GenericClient): post_data = json.dumps({'arguments': arguments, 'method': 'torrent-add', }) - self._request(method='post', data=post_data) + response = self._request(method='post', data=post_data) - return self.response.json()['result'] == "success" + return response.json()['result'] == 'success' def _set_torrent_ratio(self, result): @@ -101,9 +101,9 @@ class TransmissionAPI(GenericClient): post_data = json.dumps({'arguments': arguments, 'method': 'torrent-set', }) - self._request(method='post', data=post_data) + response = self._request(method='post', data=post_data) - return self.response.json()['result'] == "success" + return response.json()['result'] == 'success' def _set_torrent_seed_time(self, result): @@ -117,9 +117,9 @@ class TransmissionAPI(GenericClient): post_data = json.dumps({'arguments': arguments, 'method': 'torrent-set', }) - self._request(method='post', data=post_data) + response = self._request(method='post', data=post_data) - return self.response.json()['result'] == "success" + return response.json()['result'] == 'success' else: return True @@ -139,12 +139,14 @@ class TransmissionAPI(GenericClient): else: arguments['priority-normal'] = [] - post_data = json.dumps({'arguments': arguments, - 'method': 'torrent-set', - }) - self._request(method='post', data=post_data) + post_data = json.dumps({ + 'arguments': arguments, + 'method': 'torrent-set', - return self.response.json()['result'] == "success" + }) + response = self._request(method='post', data=post_data) + + return response.json()['result'] == 'success' api = TransmissionAPI() diff --git a/sickbeard/clients/utorrent.py b/sickbeard/clients/utorrent.py index 010087c7..73781768 100644 --- a/sickbeard/clients/utorrent.py +++ b/sickbeard/clients/utorrent.py @@ -42,13 +42,12 @@ class uTorrentAPI(GenericClient): def _get_auth(self): try: - self.response = self.session.get(self.url + 'token.html', verify=False) - self.auth = re.findall("(.*?)(.*?)