diff --git a/CHANGES.md b/CHANGES.md index 7042e600..bcf7d066 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -39,6 +39,7 @@ * Fix alternative unicode show names from breaking search * Change show update, set shows with newly added airdate or existing episodes with future or never dates, to "Wanted" * Fix rare NameParser case where numeric episode name was parsed as episode number +* Change improve management of Transmission config/Search/Torrent Search "Downloaded files location" ### 0.11.7 (2016-03-06 12:30:00 UTC) diff --git a/gui/slick/interfaces/default/config_search.tmpl b/gui/slick/interfaces/default/config_search.tmpl index 553339ed..5dd5e363 100755 --- a/gui/slick/interfaces/default/config_search.tmpl +++ b/gui/slick/interfaces/default/config_search.tmpl @@ -489,8 +489,9 @@ Downloaded files location -

where the torrent client will save downloaded files (blank for client default) - note: the destination has to be a shared folder for Synology DS

+

where the torrent client will save downloaded files (blank for client default) + note: the destination has to be a shared folder for Synology DS + (v2.92 and newer cannot be blank)

diff --git a/gui/slick/js/configSearch.js b/gui/slick/js/configSearch.js index 6792df94..037606c5 100644 --- a/gui/slick/js/configSearch.js +++ b/gui/slick/js/configSearch.js @@ -57,6 +57,8 @@ $(document).ready(function(){ torrent_seed_time_option = '#torrent_seed_time_option', torrent_high_bandwidth_option = '#torrent_high_bandwidth_option', torrent_label_option = '#torrent_label_option', + path_blank = '#path_blank', + path_transmission = '#path_transmission', path_synology = '#path_synology', torrent_paused_option = '#torrent_paused_option'; @@ -71,6 +73,8 @@ $(document).ready(function(){ $(torrent_seed_time_option).hide(); $(torrent_high_bandwidth_option).hide(); $(torrent_label_option).show(); + $(path_blank).show(); + $(path_transmission).hide(); $(path_synology).hide(); $(torrent_paused_option).show(); @@ -83,6 +87,8 @@ $(document).ready(function(){ $(torrent_high_bandwidth_option).show(); $(torrent_label_option).hide(); //$('#directory_title').text(client + directory); + $(path_blank).hide(); + $(path_transmission).show(); } else if ('deluge' == selectedProvider){ client = 'Deluge'; $(torrent_verify_cert_option).show(); diff --git a/sickbeard/clients/generic.py b/sickbeard/clients/generic.py index eda18b8d..ce0ebaf4 100644 --- a/sickbeard/clients/generic.py +++ b/sickbeard/clients/generic.py @@ -25,130 +25,134 @@ class GenericClient(object): self.session = requests.session() self.session.auth = (self.username, self.password) - def _request(self, method='get', params={}, data=None, files=None): - response = None + 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() 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 ''), + '%s: Requested a %s connection to url %s with Params= %s' % (self.name, method.upper(), self.url, params) + + ' Data= ' + ('None' if not data else '%s%s' % (data[0:99], ('', '...')[200 < len(data)])) + + ' Json= ' + ('None' if not kwargs.get('json') else '%s%s' % + (str(kwargs.get('json'))[0:99], + ('', '...')[200 < len(str(kwargs.get('json')))])), 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 ''), + '%s: Requested a %s connection to url %s with Params= %s' % (self.name, method.upper(), self.url, params) + + ('' if not data else ' Data= %s%s' % (data[0:100], ('', '...')[100 < len(data)])) + + ('' if not kwargs.get('json') else ' Json= %s%s' % (str(kwargs.get('json'))[0:100], + ('', '...')[100 < len(str(kwargs.get('json')))])), logger.DEBUG ) if not self.auth: - logger.log(self.name + u': Authentication Failed', logger.ERROR) + logger.log('%s: Authentication Failed' % self.name, logger.ERROR) return False try: response = self.session.__getattribute__(method)(self.url, params=params, data=data, files=files, - timeout=120, verify=False) + timeout=120, verify=False, **kwargs) except requests.exceptions.ConnectionError as e: - logger.log(self.name + u': Unable to connect ' + ex(e), logger.ERROR) + logger.log('%s: Unable to connect %s' % (self.name, ex(e)), logger.ERROR) return False except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL): - logger.log(self.name + u': Invalid Host', logger.ERROR) + logger.log('%s: Invalid Host' % self.name, logger.ERROR) return False except requests.exceptions.HTTPError as e: - logger.log(self.name + u': Invalid HTTP Request ' + ex(e), logger.ERROR) + logger.log('%s: Invalid HTTP Request %s' % (self.name, ex(e)), logger.ERROR) return False except requests.exceptions.Timeout as e: - logger.log(self.name + u': Connection Timeout ' + ex(e), logger.ERROR) + logger.log('%s: Connection Timeout %s' % (self.name, ex(e)), logger.ERROR) return False except Exception as e: - logger.log(self.name + u': Unknown exception raised when sending torrent to ' + self.name + ': ' + ex(e), + logger.log('%s: Unknown exception raised when sending torrent to %s: %s' % (self.name, self.name, ex(e)), logger.ERROR) return False - if response.status_code == 401: - logger.log(self.name + u': Invalid Username or Password, check your config', logger.ERROR) + 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.keys(): - logger.log(self.name + u': ' + http_error_code[response.status_code], logger.DEBUG) + logger.log('%s: %s' % (self.name, http_error_code[response.status_code]), logger.DEBUG) return False - logger.log(self.name + u': Response to ' + method.upper() + ' request is ' + response.text, logger.DEBUG) + logger.log('%s: Response to %s request is %s' % (self.name, method.upper(), response.text), logger.DEBUG) 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): + @staticmethod + def _get_torrent_hash(result): if result.url.startswith('magnet'): result.hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0] - if len(result.hash) == 32: + if 32 == len(result.hash): result.hash = b16encode(b32decode(result.hash)).lower() else: info = bdecode(result.content)['info'] @@ -156,14 +160,14 @@ class GenericClient(object): return result - def sendTORRENT(self, result): + def send_torrent(self, result): r_code = False - logger.log(u'Calling ' + self.name + ' Client', logger.DEBUG) + logger.log('Calling %s Client' % self.name, logger.DEBUG) if not self._get_auth(): - logger.log(self.name + u': Authentication Failed', logger.ERROR) + logger.log('%s: Authentication Failed' % self.name, logger.ERROR) return r_code try: @@ -178,52 +182,51 @@ class GenericClient(object): r_code = self._add_torrent_file(result) if not r_code: - logger.log(self.name + u': Unable to send Torrent: Return code undefined', logger.ERROR) + logger.log('%s: Unable to send Torrent: Return code undefined' % self.name, logger.ERROR) return False if not self._set_torrent_pause(result): - logger.log(self.name + u': Unable to set the pause for Torrent', logger.ERROR) + logger.log('%s: Unable to set the pause for Torrent' % self.name, logger.ERROR) if not self._set_torrent_label(result): - logger.log(self.name + u': Unable to set the label for Torrent', logger.ERROR) + logger.log('%s: Unable to set the label for Torrent' % self.name, logger.ERROR) if not self._set_torrent_ratio(result): - logger.log(self.name + u': Unable to set the ratio for Torrent', logger.ERROR) + logger.log('%s: Unable to set the ratio for Torrent' % self.name, logger.ERROR) if not self._set_torrent_seed_time(result): - logger.log(self.name + u': Unable to set the seed time for Torrent', logger.ERROR) + logger.log('%s: Unable to set the seed time for Torrent' % self.name, logger.ERROR) if not self._set_torrent_path(result): - logger.log(self.name + u': Unable to set the path for Torrent', logger.ERROR) + logger.log('%s: Unable to set the path for Torrent' % self.name, logger.ERROR) - if result.priority != 0 and not self._set_torrent_priority(result): - logger.log(self.name + u': Unable to set priority for Torrent', 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 Exception as e: - logger.log(self.name + u': Failed Sending Torrent: ' + result.name + ' - ' + result.hash, logger.ERROR) - logger.log(self.name + u': Exception raised when sending torrent: ' + ex(e), logger.DEBUG) + 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 return r_code - def testAuthentication(self): - response = None + def test_authentication(self): + try: 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!' + 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: ' + self.name + ' Connection Error' + return False, 'Error: %s Connection Error' % self.name except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL): - return False, 'Error: Invalid ' + self.name + ' host' + return False, 'Error: Invalid %s host' % self.name try: 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!' + return False, 'Error: Unable to get %s Authentication, check your config!' % self.name except Exception: - return False, 'Error: Unable to connect to ' + self.name + return False, 'Error: Unable to connect to %s' % self.name diff --git a/sickbeard/clients/rtorrent.py b/sickbeard/clients/rtorrent.py index 5e8e1fca..f6011c1e 100644 --- a/sickbeard/clients/rtorrent.py +++ b/sickbeard/clients/rtorrent.py @@ -145,7 +145,7 @@ class rTorrentAPI(GenericClient): return True - def testAuthentication(self): + def test_authentication(self): try: self._get_auth() diff --git a/sickbeard/clients/transmission.py b/sickbeard/clients/transmission.py index defe3149..4bb64159 100755 --- a/sickbeard/clients/transmission.py +++ b/sickbeard/clients/transmission.py @@ -17,7 +17,6 @@ # along with SickGear. If not, see . import re -import json from base64 import b64encode import sickbeard @@ -31,93 +30,82 @@ class TransmissionAPI(GenericClient): super(TransmissionAPI, self).__init__('Transmission', host, username, password) self.url = self.host + 'transmission/rpc' + self.blankable, self.download_dir = None, None def _get_auth(self): - post_data = json.dumps({'method': 'session-get', }) - try: - 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+)', response.text).group(1) + response = self.session.post(self.url, json={'method': 'session-get'}, + timeout=120, verify=sickbeard.TORRENT_VERIFY_CERT) + self.auth = re.search(r'(?i)X-Transmission-Session-Id:\s*(\w+)', response.text).group(1) except: return None self.session.headers.update({'x-transmission-session-id': self.auth}) - #Validating Transmission authorization - post_data = json.dumps({'arguments': {}, - 'method': 'session-get', - }) - self._request(method='post', data=post_data) + # Validating Transmission authorization + response = self._request(method='post', json={'method': 'session-get', 'arguments': {}}) + + try: + resp = response.json() + self.blankable = 14386 >= int(re.findall(r'.*[(](\d+)', resp.get('arguments', {}).get('version', '(0)'))[0]) + self.download_dir = resp.get('arguments', {}).get('download-dir', '') + except: + pass return self.auth def _add_torrent_uri(self, result): - arguments = {'filename': result.url, - 'paused': 1 if sickbeard.TORRENT_PAUSED else 0, - 'download-dir': sickbeard.TORRENT_PATH - } - post_data = json.dumps({'arguments': arguments, - 'method': 'torrent-add', - }) - response = self._request(method='post', data=post_data) - - return response.json()['result'] == 'success' + return self._add_torrent({'filename': result.url}) def _add_torrent_file(self, result): - arguments = {'metainfo': b64encode(result.content), - 'paused': 1 if sickbeard.TORRENT_PAUSED else 0, - 'download-dir': sickbeard.TORRENT_PATH - } - post_data = json.dumps({'arguments': arguments, - 'method': 'torrent-add', - }) - response = self._request(method='post', data=post_data) + return self._add_torrent({'metainfo': b64encode(result.content)}) - return response.json()['result'] == 'success' + def _add_torrent(self, t_object): + + download_dir = None + if sickbeard.TORRENT_PATH or self.blankable: + download_dir = sickbeard.TORRENT_PATH + elif self.download_dir: + download_dir = self.download_dir + else: + logger.log('Path required for Transmission Downloaded files location', logger.ERROR) + + if not download_dir and not self.blankable: + return False + + t_object.update({'paused': (0, 1)[sickbeard.TORRENT_PAUSED], 'download-dir': download_dir}) + response = self._request(method='post', json={'method': 'torrent-add', 'arguments': t_object}) + + return 'success' == response.json().get('result', '') def _set_torrent_ratio(self, result): - ratio = None - if result.ratio: - ratio = result.ratio - - mode = 0 + ratio, mode = (result.ratio, None)[not result.ratio], 0 if ratio: - if float(ratio) == -1: - ratio = 0 - mode = 2 - elif float(ratio) >= 0: - ratio = float(ratio) - mode = 1 # Stop seeding at seedRatioLimit + if -1 == float(ratio): + ratio, mode = 0, 2 + elif 0 <= float(ratio): + ratio, mode = float(ratio), 1 # Stop seeding at seedRatioLimit - arguments = {'ids': [result.hash], - 'seedRatioLimit': ratio, - 'seedRatioMode': mode - } - post_data = json.dumps({'arguments': arguments, - 'method': 'torrent-set', - }) - response = self._request(method='post', data=post_data) + response = self._request(method='post', json={ + 'method': 'torrent-set', + 'arguments': {'ids': [result.hash], 'seedRatioLimit': ratio, 'seedRatioMode': mode}}) - return response.json()['result'] == 'success' + return 'success' == response.json().get('result', '') def _set_torrent_seed_time(self, result): - if result.provider.seed_time or (sickbeard.TORRENT_SEED_TIME and sickbeard.TORRENT_SEED_TIME != -1): + if result.provider.seed_time or (sickbeard.TORRENT_SEED_TIME and -1 != sickbeard.TORRENT_SEED_TIME): seed_time = result.provider.seed_time or sickbeard.TORRENT_SEED_TIME - arguments = {'ids': [result.hash], - 'seedIdleLimit': int(seed_time) * 60, - 'seedIdleMode': 1} - post_data = json.dumps({'arguments': arguments, - 'method': 'torrent-set'}) - response = self._request(method='post', data=post_data) + response = self._request(method='post', json={ + 'method': 'torrent-set', + 'arguments': {'ids': [result.hash], 'seedIdleLimit': int(seed_time) * 60, 'seedIdleMode': 1}}) - return response.json()['result'] == 'success' + return 'success' == response.json().get('result', '') else: return True @@ -125,9 +113,9 @@ class TransmissionAPI(GenericClient): arguments = {'ids': [result.hash]} - if result.priority == -1: + if -1 == result.priority: arguments['priority-low'] = [] - elif result.priority == 1: + elif 1 == result.priority: # set high priority for all files in torrent arguments['priority-high'] = [] # move torrent to the top if the queue @@ -137,14 +125,9 @@ class TransmissionAPI(GenericClient): else: arguments['priority-normal'] = [] - post_data = json.dumps({ - 'arguments': arguments, - 'method': 'torrent-set', + response = self._request(method='post', json={'method': 'torrent-set', 'arguments': arguments}) - }) - response = self._request(method='post', data=post_data) - - return response.json()['result'] == 'success' + return 'success' == response.json().get('result', '') api = TransmissionAPI() diff --git a/sickbeard/search.py b/sickbeard/search.py index cb912061..b0cfa869 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -139,7 +139,7 @@ def snatch_episode(result, end_status=SNATCHED): return False # Snatches torrent with client client = clients.getClientIstance(sickbeard.TORRENT_METHOD)() - dl_result = client.sendTORRENT(result) + dl_result = client.send_torrent(result) else: logger.log(u'Unknown result type, unable to download it', logger.ERROR) dl_result = False diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 4a6d7864..fd76b26b 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -676,7 +676,7 @@ class Home(MainHandler): client = clients.getClientIstance(torrent_method) - connection, accesMsg = client(host, username, password).testAuthentication() + connection, accesMsg = client(host, username, password).test_authentication() return accesMsg