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("(.*?)(.*?)