mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-16 09:55:05 +00:00
32987134ba
Cleanup most init warnings. Cleanup some vars, pythonic instead of js. Some typos and python var/func names for Scheduler. Remove legacy handlers deprecated in 2020. Remove some legacy tagged stuff. Cleanup ConfigParser and 23.py Change cleanup vendored scandir. Remove redundant pkg_resources.py in favour of the vendor folder. Remove backports. Remove trakt checker. Change remove redundant WindowsSelectorEventLoopPolicy from webserveInit. Cleanup varnames and providers Various minor tidy ups to remove ide warnings.
293 lines
11 KiB
Python
293 lines
11 KiB
Python
from hashlib import sha1
|
|
import re
|
|
import time
|
|
|
|
from . import http_error_code
|
|
from .. import logger
|
|
import sickgear
|
|
from exceptions_helper import ex
|
|
from lib.bencode import bencode, bdecode
|
|
from lib import requests
|
|
|
|
from _23 import make_btih
|
|
from six import string_types
|
|
|
|
|
|
class GenericClient(object):
|
|
def __init__(self, name, host=None, username=None, password=None):
|
|
|
|
self.name = name
|
|
self.username = sickgear.TORRENT_USERNAME if username is None else username
|
|
self.password = sickgear.TORRENT_PASSWORD if password is None else password
|
|
self.host = sickgear.TORRENT_HOST if host is None else host.rstrip('/') + '/'
|
|
|
|
self.url = None
|
|
self.auth = None
|
|
self.last_time = time.time()
|
|
self.session = requests.session()
|
|
self.session.auth = (self.username, self.password)
|
|
self.created_id = None
|
|
|
|
def _log_request_details(self, method, params=None, data=None, files=None, **kwargs):
|
|
|
|
output = []
|
|
output += ['%s: sending %s request to %s' % (self.name, method, self.url)]
|
|
|
|
lines = [('params', (str(params), '')[not params]),
|
|
('data', (str(data), '')[not data]),
|
|
('files', (str(files), '')[not files]),
|
|
('post_data', (str(kwargs.get('post_data')), '')[not kwargs.get('post_data')]),
|
|
('post_json', (str(kwargs.get('post_json')), '')[not kwargs.get('post_json')]),
|
|
('json', (str(kwargs.get('json')), '')[not kwargs.get('json')])]
|
|
m, c = 300, 100
|
|
type_chunks = [(linetype, [ln[i:i + c] for i in range(0, min(len(ln), m), c)]) for linetype, ln in lines if ln]
|
|
if type_chunks:
|
|
output[-1] += ' with ...'
|
|
for (arg, chunks) in type_chunks:
|
|
nch = len(chunks) - 1
|
|
for i, seg in enumerate(chunks):
|
|
if nch == i and 'files' == arg:
|
|
sample = ' ..excerpt(%s/%s)' % (m, len(lines[2][1]))
|
|
seg = seg[0:c - (len(sample) - 2)] + sample
|
|
output += ['%s: request %s= %s%s%s' % (self.name, arg, ('', '..')[bool(i)], seg, ('', '..')[i != nch])]
|
|
|
|
logger.log(output, logger.DEBUG)
|
|
|
|
def _request(self, method='get', params=None, data=None, files=None, **kwargs):
|
|
|
|
params = params or {}
|
|
|
|
if time.time() > self.last_time + 1800 or not self.auth:
|
|
self.last_time = time.time()
|
|
|
|
if not self._get_auth():
|
|
logger.log('%s: Authentication failed' % self.name, logger.ERROR)
|
|
return False
|
|
|
|
# self._log_request_details(method, params, data, files, **kwargs)
|
|
|
|
try:
|
|
response = self.session.__getattribute__(method)(self.url, params=params, data=data, files=files,
|
|
timeout=kwargs.pop('timeout', 120), verify=False, **kwargs)
|
|
except requests.exceptions.ConnectionError as e:
|
|
logger.log('%s: Unable to connect %s' % (self.name, ex(e)), logger.ERROR)
|
|
return False
|
|
except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL):
|
|
logger.log('%s: Invalid host' % self.name, logger.ERROR)
|
|
return False
|
|
except requests.exceptions.HTTPError as e:
|
|
logger.log('%s: Invalid HTTP request %s' % (self.name, ex(e)), logger.ERROR)
|
|
return False
|
|
except requests.exceptions.Timeout as e:
|
|
logger.log('%s: Connection timeout %s' % (self.name, ex(e)), logger.ERROR)
|
|
return False
|
|
except (BaseException, Exception) as e:
|
|
logger.log('%s: Unknown exception raised when sending torrent to %s: %s' % (self.name, self.name, ex(e)),
|
|
logger.ERROR)
|
|
return False
|
|
|
|
if 401 == response.status_code:
|
|
logger.log('%s: Invalid username or password, check your config' % self.name, logger.ERROR)
|
|
return False
|
|
|
|
if response.status_code in http_error_code:
|
|
logger.log('%s: %s' % (self.name, http_error_code[response.status_code]), logger.DEBUG)
|
|
return False
|
|
|
|
logger.log('%s: Response to %s request is %s' % (self.name, method.upper(), response.text), logger.DEBUG)
|
|
|
|
return response
|
|
|
|
def _tinf(self, ids=None):
|
|
"""
|
|
This should be overridden and return client fetched task information
|
|
|
|
:param ids: Optional id(s) to get task info for. None to get all task info
|
|
:type ids: list or None
|
|
:return: Zero or more task object(s) from response
|
|
:rtype: list
|
|
"""
|
|
return []
|
|
|
|
def _active_state(self, ids=None):
|
|
"""
|
|
This should be overridden to fetch state of items, return items that are actually downloading or seeding
|
|
:param ids: Optional id(s) to get state info for. None to get all
|
|
:type ids: list or None
|
|
:return: Zero or more object(s) assigned with state `down`loading or `seed`ing
|
|
:rtype: list
|
|
"""
|
|
return []
|
|
|
|
def _add_torrent_uri(self, result):
|
|
"""
|
|
This should be overridden to 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 to 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 to 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 to 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 to 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 overridden to 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 to 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 to return the True/False from the client
|
|
when a torrent is set with pause
|
|
"""
|
|
return True
|
|
|
|
def _resume_torrent(self, ids):
|
|
"""
|
|
This should be overridden to resume task(s) in client
|
|
|
|
:param ids: ID(s) to act on
|
|
:type ids: list or string
|
|
:return: True if success, ID(s) that could not be resumed, else Falsy if failure
|
|
:rtype: bool or list
|
|
"""
|
|
return False
|
|
|
|
def _delete_torrent(self, ids):
|
|
"""
|
|
This should be overridden to delete task(s) from client
|
|
:param ids: ID(s) to act on
|
|
:type ids: list or string
|
|
:return: True if success, ID(s) that could not be deleted, else Falsy if failure
|
|
:rtype: bool or list
|
|
"""
|
|
return False
|
|
|
|
@staticmethod
|
|
def _get_torrent_hash(result):
|
|
|
|
if result.url.startswith('magnet'):
|
|
result.hash = re.findall(r'urn:btih:(\w{32,40})', result.url)[0]
|
|
if 32 == len(result.hash):
|
|
result.hash = make_btih(result.hash).lower()
|
|
else:
|
|
info = bdecode(result.content)['info']
|
|
result.hash = sha1(bencode(info)).hexdigest()
|
|
|
|
return result
|
|
|
|
def send_torrent(self, result):
|
|
|
|
r_code = False
|
|
|
|
logger.log('Calling %s client' % self.name, logger.DEBUG)
|
|
|
|
if not self._get_auth():
|
|
logger.log('%s: Authentication failed' % self.name, logger.ERROR)
|
|
return r_code
|
|
|
|
try:
|
|
# Sets per provider seed ratio
|
|
result.ratio = result.provider.seed_ratio()
|
|
|
|
result = self._get_torrent_hash(result)
|
|
except (BaseException, Exception) as e:
|
|
logger.log('Bad torrent data: hash is %s for [%s]' % (result.hash, result.name), logger.ERROR)
|
|
logger.log('Exception raised when checking torrent data: %s' % (ex(e)), logger.DEBUG)
|
|
return r_code
|
|
|
|
try:
|
|
if result.url.startswith('magnet'):
|
|
r_code = self._add_torrent_uri(result)
|
|
else:
|
|
r_code = self._add_torrent_file(result)
|
|
|
|
self.created_id = isinstance(r_code, string_types) and r_code or None
|
|
if not r_code:
|
|
logger.log('%s: Unable to send torrent to client' % self.name, logger.ERROR)
|
|
return False
|
|
|
|
if not self._set_torrent_pause(result):
|
|
logger.log('%s: Unable to set the pause for torrent' % self.name, logger.ERROR)
|
|
|
|
if not self._set_torrent_label(result):
|
|
logger.log('%s: Unable to set the label for torrent' % self.name, logger.ERROR)
|
|
|
|
if not self._set_torrent_ratio(result):
|
|
logger.log('%s: Unable to set the ratio for torrent' % self.name, logger.ERROR)
|
|
|
|
if not self._set_torrent_seed_time(result):
|
|
logger.log('%s: Unable to set the seed time for torrent' % self.name, logger.ERROR)
|
|
|
|
if not self._set_torrent_path(result):
|
|
logger.log('%s: Unable to set the path for torrent' % self.name, logger.ERROR)
|
|
|
|
if 0 != result.priority and not self._set_torrent_priority(result):
|
|
logger.log('%s: Unable to set priority for torrent' % self.name, logger.ERROR)
|
|
|
|
except (BaseException, Exception) as e:
|
|
logger.log('%s: Failed sending torrent: %s - %s' % (self.name, result.name, result.hash), logger.ERROR)
|
|
logger.log('%s: Exception raised when sending torrent: %s' % (self.name, ex(e)), logger.DEBUG)
|
|
|
|
return r_code
|
|
|
|
def _get_auth(self):
|
|
"""
|
|
This may be overridden and should return the auth_id needed for the client
|
|
"""
|
|
try:
|
|
response = self.session.get(self.url, timeout=120, verify=False)
|
|
|
|
if 401 == response.status_code:
|
|
return False, 'Error: Invalid %s username or password, check your config!' % self.name
|
|
except requests.exceptions.ConnectionError:
|
|
return False, 'Error: Connecting to %s' % self.name
|
|
except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL):
|
|
return False, 'Error: Invalid %s host' % self.name
|
|
|
|
def test_authentication(self):
|
|
|
|
try:
|
|
result = self._get_auth()
|
|
if result:
|
|
return ((True, 'Success: Connected and authenticated to %s' % self.name),
|
|
result)[isinstance(result, tuple)]
|
|
|
|
failed_msg = 'Error: Failed %s authentication.%s' % (self.name, getattr(self, '_errmsg', None) or '')
|
|
except (BaseException, Exception):
|
|
failed_msg = 'Error: Unable to connect to %s' % self.name
|
|
|
|
return False, failed_msg
|