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.
This commit is contained in:
Sami Haahtinen 2015-05-06 20:25:12 +03:00
parent 1feb7a73d0
commit deda4bc65a
6 changed files with 203 additions and 191 deletions

View file

@ -31,6 +31,7 @@
* Add General Config/Interface/"Group show list shows into:"... to divide shows into groups on the Show List page * 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 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 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] [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 Fix issue changing a custom show list group name that is in use. The bug resulted in the db containing stale group names

View file

@ -22,6 +22,7 @@ from base64 import b64encode
import sickbeard import sickbeard
from sickbeard import logger from sickbeard import logger
from sickbeard.clients.generic import GenericClient from sickbeard.clients.generic import GenericClient
from lib.requests.exceptions import RequestException
class DelugeAPI(GenericClient): class DelugeAPI(GenericClient):
@ -33,134 +34,148 @@ class DelugeAPI(GenericClient):
def _get_auth(self): def _get_auth(self):
post_data = json.dumps({"method": "auth.login", post_data = json.dumps({'method': 'auth.login',
"params": [self.password], 'params': [self.password],
"id": 1 'id': 1})
})
try: try:
self.response = self.session.post(self.url, data=post_data.encode('utf-8'), verify=sickbeard.TORRENT_VERIFY_CERT) self.auth = self.session.post(
except: self.url,
return None 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", connected = self.session.post(
"params": [], self.url,
"id": 10 data=post_data.encode('utf-8'),
}) verify=sickbeard.TORRENT_VERIFY_CERT
try: ).json()['result']
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:
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: 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 return None
except RequestException:
return None
return self.auth return self.auth
def _add_torrent_uri(self, result): def _add_torrent_uri(self, result):
post_data = json.dumps({"method": "core.add_torrent_magnet", post_data = json.dumps({
"params": [result.url, {"move_completed": "true", 'method': 'core.add_torrent_magnet',
"move_completed_path": sickbeard.TV_DOWNLOAD_DIR}], 'params': [result.url, {
"id": 2 '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 result.hash
return self.response.json()['result']
def _add_torrent_file(self, result): def _add_torrent_file(self, result):
post_data = json.dumps({"method": "core.add_torrent_file", post_data = json.dumps({'method':
"params": [result.name + '.torrent', b64encode(result.content), 'core.add_torrent_file',
{"move_completed": "true", 'params': [result.name + '.torrent',
"move_completed_path": sickbeard.TV_DOWNLOAD_DIR}], b64encode(result.content),
"id": 2 {'move_completed': 'true',
}) 'move_completed_path':
self._request(method='post', data=post_data) sickbeard.TV_DOWNLOAD_DIR}],
'id': 2})
result.hash = self._request(method='post',
data=post_data).json()['result']
result.hash = self.response.json()['result'] return result.hash
return self.response.json()['result']
def _set_torrent_label(self, result): def _set_torrent_label(self, result):
label = sickbeard.TORRENT_LABEL label = sickbeard.TORRENT_LABEL
if ' ' in 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 return False
if label: if label:
# check if label already exists and create it if not # check if label already exists and create it if not
post_data = json.dumps({"method": 'label.get_labels', post_data = json.dumps({
"params": [], 'method': 'label.get_labels',
"id": 3 'params': [],
'id': 3
}) })
self._request(method='post', data=post_data) labels = self._request(method='post',
labels = self.response.json()['result'] data=post_data).json()['result']
if labels != None: if labels is not None:
if label not in labels: 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) logger.DEBUG)
post_data = json.dumps({"method": 'label.add', post_data = json.dumps({
"params": [label], 'method': 'label.add',
"id": 4 'params': [label],
'id': 4
}) })
self._request(method='post', data=post_data) 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 # add label to torrent
post_data = json.dumps({"method": 'label.set_torrent', post_data = json.dumps({
"params": [result.hash, label], 'method': 'label.set_torrent',
"id": 5 'params': [result.hash, label],
'id': 5
}) })
self._request(method='post', data=post_data) 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: 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 False
return not self.response.json()['error'] return True
def _set_torrent_ratio(self, result): def _set_torrent_ratio(self, result):
@ -169,53 +184,43 @@ class DelugeAPI(GenericClient):
ratio = result.ratio ratio = result.ratio
if ratio: if ratio:
post_data = json.dumps({"method": "core.set_torrent_stop_at_ratio", post_data = json.dumps({'method': 'core.set_torrent_stop_at_ratio',
"params": [result.hash, True], 'params': [result.hash, True],
"id": 5 'id': 5})
})
self._request(method='post', data=post_data) self._request(method='post', data=post_data)
post_data = json.dumps({"method": "core.set_torrent_stop_ratio", post_data = json.dumps({'method': 'core.set_torrent_stop_ratio',
"params": [result.hash, float(ratio)], 'params': [result.hash, float(ratio)],
"id": 6 'id': 6})
})
self._request(method='post', data=post_data) self._request(method='post', data=post_data)
return not self.response.json()['error']
return True return True
def _set_torrent_path(self, result): def _set_torrent_path(self, result):
if sickbeard.TORRENT_PATH: if sickbeard.TORRENT_PATH:
post_data = json.dumps({"method": "core.set_torrent_move_completed", post_data = json.dumps({
"params": [result.hash, True], 'method': 'core.set_torrent_move_completed',
"id": 7 'params': [result.hash, True],
'id': 7
}) })
self._request(method='post', data=post_data) self._request(method='post', data=post_data)
post_data = json.dumps({"method": "core.set_torrent_move_completed_path", post_data = json.dumps({
"params": [result.hash, sickbeard.TORRENT_PATH], 'method': 'core.set_torrent_move_completed_path',
"id": 8 'params': [result.hash, sickbeard.TORRENT_PATH],
'id': 8
}) })
self._request(method='post', data=post_data) self._request(method='post', data=post_data)
return not self.response.json()['error']
return True return True
def _set_torrent_pause(self, result): def _set_torrent_pause(self, result):
if sickbeard.TORRENT_PAUSED: if sickbeard.TORRENT_PAUSED:
post_data = json.dumps({"method": "core.pause_torrent", post_data = json.dumps({'method': 'core.pause_torrent',
"params": [[result.hash]], 'params': [[result.hash]],
"id": 9 'id': 9})
})
self._request(method='post', data=post_data) self._request(method='post', data=post_data)
return not self.response.json()['error']
return True return True
api = DelugeAPI() api = DelugeAPI()

View file

@ -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' 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: try:
self.response = self.session.get(auth_url, verify=False) response = self.session.get(auth_url, verify=False)
self.auth = self.response.json()['data']['sid'] self.auth = response.json()['data']['sid']
except: except:
return None return None
@ -53,9 +53,9 @@ class DownloadStationAPI(GenericClient):
} }
if sickbeard.TORRENT_PATH: if sickbeard.TORRENT_PATH:
data['destination'] = 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): def _add_torrent_file(self, result):
@ -68,8 +68,8 @@ class DownloadStationAPI(GenericClient):
if sickbeard.TORRENT_PATH: if sickbeard.TORRENT_PATH:
data['destination'] = sickbeard.TORRENT_PATH data['destination'] = sickbeard.TORRENT_PATH
files = {'file':(result.name + '.torrent', result.content)} 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() api = DownloadStationAPI()

View file

@ -9,7 +9,7 @@ from sickbeard.exceptions import ex
from sickbeard.clients import http_error_code from sickbeard.clients import http_error_code
from lib.bencode import bencode, bdecode from lib.bencode import bencode, bdecode
from lib import requests from lib import requests
from lib.requests import exceptions
class GenericClient(object): class GenericClient(object):
def __init__(self, name, host=None, username=None, password=None): 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.host = sickbeard.TORRENT_HOST if host is None else host
self.url = None self.url = None
self.response = None
self.auth = None self.auth = None
self.last_time = time.time() self.last_time = time.time()
self.session = requests.session() self.session = requests.session()
self.session.auth = (self.username, self.password) self.session.auth = (self.username, self.password)
def _request(self, method='get', params={}, data=None, files=None): def _request(self, method='get', params={}, data=None, files=None):
response = None
if time.time() > self.last_time + 1800 or not self.auth: if time.time() > self.last_time + 1800 or not self.auth:
self.last_time = time.time() self.last_time = time.time()
self._get_auth() self._get_auth()
logger.log( logger.log(
self.name + u': Requested a ' + method.upper() + ' connection to url ' + self.url + ' with Params= ' + str( self.name + u': Requested a ' + method.upper() +
params) + ' Data=' + str(data if data else 'None')[0:99] + ( ' connection to url ' + self.url + ' with Params= ' + str(params) +
'...' if len(data if data else 'None') > 200 else ''), logger.DEBUG) ' Data=' + str(data if data else 'None')[0:99] +
('...' if len(data if data else 'None') > 200 else ''),
logger.DEBUG
)
logger.log( logger.log(
self.name + u': Requested a ' + method.upper() + ' connection to url ' + self.url + ' with Params= ' + str( self.name + u': Requested a ' + method.upper() +
params) + ( ' 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 ""), ((' Data=' + str(data)[0:100] + ('...' if len(data) > 100 else ''))
logger.DEBUG) if data is not None else ''),
logger.DEBUG
)
if not self.auth: if not self.auth:
logger.log(self.name + u': Authentication Failed', logger.ERROR) logger.log(self.name + u': Authentication Failed', logger.ERROR)
return False return False
try: 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) timeout=120, verify=False)
except requests.exceptions.ConnectionError, e: except requests.exceptions.ConnectionError, e:
logger.log(self.name + u': Unable to connect ' + ex(e), logger.ERROR) logger.log(self.name + u': Unable to connect ' + ex(e), logger.ERROR)
@ -66,78 +70,78 @@ class GenericClient(object):
logger.ERROR) logger.ERROR)
return False 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) logger.log(self.name + u': Invalid Username or Password, check your config', logger.ERROR)
return False return False
if self.response.status_code in http_error_code.keys(): if response.status_code in http_error_code.keys():
logger.log(self.name + u': ' + http_error_code[self.response.status_code], logger.DEBUG) logger.log(self.name + u': ' + http_error_code[response.status_code], logger.DEBUG)
return False 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): def _get_auth(self):
""" '''
This should be overridden and should return the auth_id needed for the client This should be overridden and should return the auth_id needed for the client
""" '''
return None return None
def _add_torrent_uri(self, result): def _add_torrent_uri(self, result):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is added via url (magnet or .torrent link) when a torrent is added via url (magnet or .torrent link)
""" '''
return False return False
def _add_torrent_file(self, result): def _add_torrent_file(self, result):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is added via result.content (only .torrent file) when a torrent is added via result.content (only .torrent file)
""" '''
return False return False
def _set_torrent_label(self, result): def _set_torrent_label(self, result):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is set with label when a torrent is set with label
""" '''
return True return True
def _set_torrent_ratio(self, result): def _set_torrent_ratio(self, result):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is set with ratio when a torrent is set with ratio
""" '''
return True return True
def _set_torrent_seed_time(self, result): def _set_torrent_seed_time(self, result):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is set with a seed time when a torrent is set with a seed time
""" '''
return True return True
def _set_torrent_priority(self, result): def _set_torrent_priority(self, result):
""" '''
This should be overriden should return the True/False from the client 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) when a torrent is set with result.priority (-1 = low, 0 = normal, 1 = high)
""" '''
return True return True
def _set_torrent_path(self, torrent_path): def _set_torrent_path(self, torrent_path):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is set with path when a torrent is set with path
""" '''
return True return True
def _set_torrent_pause(self, result): def _set_torrent_pause(self, result):
""" '''
This should be overridden should return the True/False from the client This should be overridden should return the True/False from the client
when a torrent is set with pause when a torrent is set with pause
""" '''
return True return True
def _get_torrent_hash(self, result): def _get_torrent_hash(self, result):
@ -148,7 +152,7 @@ class GenericClient(object):
result.hash = b16encode(b32decode(result.hash)).lower() result.hash = b16encode(b32decode(result.hash)).lower()
else: else:
result.content = result.provider.getURL(result.url) result.content = result.provider.getURL(result.url)
info = bdecode(result.content)["info"] info = bdecode(result.content)['info']
result.hash = sha1(bencode(info)).hexdigest() result.hash = sha1(bencode(info)).hexdigest()
return result return result
@ -205,20 +209,21 @@ class GenericClient(object):
return r_code return r_code
def testAuthentication(self): def testAuthentication(self):
response = None
try: try:
self.response = self.session.get(self.url, timeout=120, verify=False) response = self.session.get(self.url, timeout=120, verify=False)
except requests.exceptions.ConnectionError, e:
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' return False, 'Error: ' + self.name + ' Connection Error'
except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL): except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL):
return False, 'Error: Invalid ' + self.name + ' host' 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: try:
self._get_auth() authenticated = self._get_auth()
if self.response.status_code == 200 and self.auth: # FIXME: This test is redundant
if authenticated and self.auth:
return True, 'Success: Connected and Authenticated' return True, 'Success: Connected and Authenticated'
else: else:
return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!' return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!'

View file

@ -37,9 +37,9 @@ class TransmissionAPI(GenericClient):
post_data = json.dumps({'method': 'session-get', }) post_data = json.dumps({'method': 'session-get', })
try: 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) 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: except:
return None return None
@ -62,9 +62,9 @@ class TransmissionAPI(GenericClient):
post_data = json.dumps({'arguments': arguments, post_data = json.dumps({'arguments': arguments,
'method': 'torrent-add', '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): def _add_torrent_file(self, result):
@ -75,9 +75,9 @@ class TransmissionAPI(GenericClient):
post_data = json.dumps({'arguments': arguments, post_data = json.dumps({'arguments': arguments,
'method': 'torrent-add', '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): def _set_torrent_ratio(self, result):
@ -101,9 +101,9 @@ class TransmissionAPI(GenericClient):
post_data = json.dumps({'arguments': arguments, post_data = json.dumps({'arguments': arguments,
'method': 'torrent-set', '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): def _set_torrent_seed_time(self, result):
@ -117,9 +117,9 @@ class TransmissionAPI(GenericClient):
post_data = json.dumps({'arguments': arguments, post_data = json.dumps({'arguments': arguments,
'method': 'torrent-set', '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: else:
return True return True
@ -139,12 +139,14 @@ class TransmissionAPI(GenericClient):
else: else:
arguments['priority-normal'] = [] arguments['priority-normal'] = []
post_data = json.dumps({'arguments': arguments, post_data = json.dumps({
'method': 'torrent-set', 'arguments': arguments,
}) 'method': 'torrent-set',
self._request(method='post', data=post_data)
return self.response.json()['result'] == "success" })
response = self._request(method='post', data=post_data)
return response.json()['result'] == 'success'
api = TransmissionAPI() api = TransmissionAPI()

View file

@ -42,13 +42,12 @@ class uTorrentAPI(GenericClient):
def _get_auth(self): def _get_auth(self):
try: try:
self.response = self.session.get(self.url + 'token.html', verify=False) response = self.session.get(self.url + 'token.html', verify=False)
self.auth = re.findall("<div.*?>(.*?)</", self.response.text)[0] self.auth = re.findall('<div.*?>(.*?)</', response.text)[0]
return self.auth if not response.status_code == 404 else None
except: except:
return None return None
return self.auth if not self.response.status_code == 404 else None
def _add_torrent_uri(self, result): def _add_torrent_uri(self, result):
params = {'action': 'add-url', 's': result.url} params = {'action': 'add-url', 's': result.url}
@ -112,15 +111,15 @@ class uTorrentAPI(GenericClient):
else: else:
return False return False
else: else:
return True return True
def _set_torrent_priority(self, result): def _set_torrent_priority(self, result):
if result.priority == 1: if result.priority == 1:
params = {'action': 'queuetop', 'hash': result.hash} params = {'action': 'queuetop', 'hash': result.hash}
return self._request(params=params) return self._request(params=params)
else: else:
return True return True
def _set_torrent_pause(self, result): def _set_torrent_pause(self, result):
@ -132,4 +131,4 @@ class uTorrentAPI(GenericClient):
return self._request(params=params) return self._request(params=params)
api = uTorrentAPI() api = uTorrentAPI()