Change improve management of Transmission config/Search/Torrent Search "Downloaded files location".

This commit is contained in:
JackDandy 2016-03-09 11:16:02 +00:00
parent 944064d92d
commit 3bc0823670
8 changed files with 129 additions and 135 deletions

View file

@ -39,6 +39,7 @@
* Fix alternative unicode show names from breaking search * 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" * 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 * 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) ### 0.11.7 (2016-03-06 12:30:00 UTC)

View file

@ -489,8 +489,9 @@
<span class="component-title" id="directory_title">Downloaded files location</span> <span class="component-title" id="directory_title">Downloaded files location</span>
<span class="component-desc"> <span class="component-desc">
<input type="text" name="torrent_path" id="torrent_path" value="$sickbeard.TORRENT_PATH" class="form-control input-sm input350"> <input type="text" name="torrent_path" id="torrent_path" value="$sickbeard.TORRENT_PATH" class="form-control input-sm input350">
<p class="clear-left note">where <span id="torrent_client">the torrent client</span> will save downloaded files (blank for client default) <p class="clear-left note">where <span id="torrent_client">the torrent client</span> will save downloaded files <span id="path_blank">(blank for client default)</span>
<span id="path_synology"> <b>note:</b> the destination has to be a shared folder for Synology DS</span></p> <span id="path_synology"> <b>note:</b> the destination has to be a shared folder for Synology DS</span>
<span id="path_transmission" class="red-text"> (v2.92 and newer <em>cannot</em> be blank)</span></p>
</span> </span>
</label> </label>
</div> </div>

View file

@ -57,6 +57,8 @@ $(document).ready(function(){
torrent_seed_time_option = '#torrent_seed_time_option', torrent_seed_time_option = '#torrent_seed_time_option',
torrent_high_bandwidth_option = '#torrent_high_bandwidth_option', torrent_high_bandwidth_option = '#torrent_high_bandwidth_option',
torrent_label_option = '#torrent_label_option', torrent_label_option = '#torrent_label_option',
path_blank = '#path_blank',
path_transmission = '#path_transmission',
path_synology = '#path_synology', path_synology = '#path_synology',
torrent_paused_option = '#torrent_paused_option'; torrent_paused_option = '#torrent_paused_option';
@ -71,6 +73,8 @@ $(document).ready(function(){
$(torrent_seed_time_option).hide(); $(torrent_seed_time_option).hide();
$(torrent_high_bandwidth_option).hide(); $(torrent_high_bandwidth_option).hide();
$(torrent_label_option).show(); $(torrent_label_option).show();
$(path_blank).show();
$(path_transmission).hide();
$(path_synology).hide(); $(path_synology).hide();
$(torrent_paused_option).show(); $(torrent_paused_option).show();
@ -83,6 +87,8 @@ $(document).ready(function(){
$(torrent_high_bandwidth_option).show(); $(torrent_high_bandwidth_option).show();
$(torrent_label_option).hide(); $(torrent_label_option).hide();
//$('#directory_title').text(client + directory); //$('#directory_title').text(client + directory);
$(path_blank).hide();
$(path_transmission).show();
} else if ('deluge' == selectedProvider){ } else if ('deluge' == selectedProvider){
client = 'Deluge'; client = 'Deluge';
$(torrent_verify_cert_option).show(); $(torrent_verify_cert_option).show();

View file

@ -25,130 +25,134 @@ class GenericClient(object):
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=None, data=None, files=None, **kwargs):
response = None
params = params or {}
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() + '%s: Requested a %s connection to url %s with Params= %s' % (self.name, method.upper(), self.url, params) +
' connection to url ' + self.url + ' with Params= ' + str(params) + ' Data= ' + ('None' if not data else '%s%s' % (data[0:99], ('', '...')[200 < len(data)])) +
' Data=' + str(data if data else 'None')[0:99] + ' Json= ' + ('None' if not kwargs.get('json') else '%s%s' %
('...' if len(data if data else 'None') > 200 else ''), (str(kwargs.get('json'))[0:99],
('', '...')[200 < len(str(kwargs.get('json')))])),
logger.DEBUG logger.DEBUG
) )
logger.log( logger.log(
self.name + u': Requested a ' + method.upper() + '%s: Requested a %s connection to url %s with Params= %s' % (self.name, method.upper(), self.url, params) +
' connection to url ' + self.url + ' with Params= ' + str(params) + ('' if not data else ' Data= %s%s' % (data[0:100], ('', '...')[100 < len(data)])) +
((' Data=' + str(data)[0:100] + ('...' if len(data) > 100 else '')) ('' if not kwargs.get('json') else ' Json= %s%s' % (str(kwargs.get('json'))[0:100],
if data is not None else ''), ('', '...')[100 < len(str(kwargs.get('json')))])),
logger.DEBUG logger.DEBUG
) )
if not self.auth: if not self.auth:
logger.log(self.name + u': Authentication Failed', logger.ERROR) logger.log('%s: Authentication Failed' % self.name, logger.ERROR)
return False return False
try: try:
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, **kwargs)
except requests.exceptions.ConnectionError as e: 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 return False
except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL): 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 return False
except requests.exceptions.HTTPError as e: 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 return False
except requests.exceptions.Timeout as e: 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 return False
except Exception as e: 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) logger.ERROR)
return False return False
if response.status_code == 401: if 401 == response.status_code:
logger.log(self.name + u': Invalid Username or Password, check your config', logger.ERROR) logger.log('%s: Invalid Username or Password, check your config' % self.name, logger.ERROR)
return False return False
if 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[response.status_code], logger.DEBUG) logger.log('%s: %s' % (self.name, http_error_code[response.status_code]), logger.DEBUG)
return False 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 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): @staticmethod
def _get_torrent_hash(result):
if result.url.startswith('magnet'): if result.url.startswith('magnet'):
result.hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0] 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() result.hash = b16encode(b32decode(result.hash)).lower()
else: else:
info = bdecode(result.content)['info'] info = bdecode(result.content)['info']
@ -156,14 +160,14 @@ class GenericClient(object):
return result return result
def sendTORRENT(self, result): def send_torrent(self, result):
r_code = False 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(): 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 return r_code
try: try:
@ -178,52 +182,51 @@ class GenericClient(object):
r_code = self._add_torrent_file(result) r_code = self._add_torrent_file(result)
if not r_code: 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 return False
if not self._set_torrent_pause(result): 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): 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): 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): 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): 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): if 0 != result.priority and not self._set_torrent_priority(result):
logger.log(self.name + u': Unable to set priority for Torrent', logger.ERROR) logger.log('%s: Unable to set priority for Torrent' % self.name, logger.ERROR)
except Exception as e: except Exception as e:
logger.log(self.name + u': Failed Sending Torrent: ' + result.name + ' - ' + result.hash, logger.ERROR) logger.log('%s: Failed sending torrent: %s - %s' % (self.name, result.name, result.hash), logger.ERROR)
logger.log(self.name + u': Exception raised when sending torrent: ' + ex(e), logger.DEBUG) logger.log('%s: Exception raised when sending torrent: %s' % (self.name, ex(e)), logger.DEBUG)
return r_code return r_code
return r_code return r_code
def testAuthentication(self): def test_authentication(self):
response = None
try: try:
response = self.session.get(self.url, timeout=120, verify=False) response = self.session.get(self.url, timeout=120, verify=False)
if response.status_code == 401: if 401 == response.status_code:
return False, 'Error: Invalid ' + self.name + ' Username or Password, check your config!' return False, 'Error: Invalid %s Username or Password, check your config!' % self.name
except requests.exceptions.ConnectionError: 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): except (requests.exceptions.MissingSchema, requests.exceptions.InvalidURL):
return False, 'Error: Invalid ' + self.name + ' host' return False, 'Error: Invalid %s host' % self.name
try: try:
authenticated = self._get_auth() authenticated = self._get_auth()
# FIXME: This test is redundant # FIXME: This test is redundant
if authenticated and self.auth: if authenticated and self.auth:
return True, 'Success: Connected and Authenticated' return True, 'Success: Connected and Authenticated'
else: return False, 'Error: Unable to get %s Authentication, check your config!' % self.name
return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!'
except Exception: except Exception:
return False, 'Error: Unable to connect to ' + self.name return False, 'Error: Unable to connect to %s' % self.name

View file

@ -145,7 +145,7 @@ class rTorrentAPI(GenericClient):
return True return True
def testAuthentication(self): def test_authentication(self):
try: try:
self._get_auth() self._get_auth()

View file

@ -17,7 +17,6 @@
# along with SickGear. If not, see <http://www.gnu.org/licenses/>. # along with SickGear. If not, see <http://www.gnu.org/licenses/>.
import re import re
import json
from base64 import b64encode from base64 import b64encode
import sickbeard import sickbeard
@ -31,93 +30,82 @@ class TransmissionAPI(GenericClient):
super(TransmissionAPI, self).__init__('Transmission', host, username, password) super(TransmissionAPI, self).__init__('Transmission', host, username, password)
self.url = self.host + 'transmission/rpc' self.url = self.host + 'transmission/rpc'
self.blankable, self.download_dir = None, None
def _get_auth(self): def _get_auth(self):
post_data = json.dumps({'method': 'session-get', })
try: try:
response = self.session.post(self.url, data=post_data.encode('utf-8'), timeout=120, response = self.session.post(self.url, json={'method': 'session-get'},
verify=sickbeard.TORRENT_VERIFY_CERT) timeout=120, verify=sickbeard.TORRENT_VERIFY_CERT)
self.auth = re.search('X-Transmission-Session-Id:\s*(\w+)', response.text).group(1) self.auth = re.search(r'(?i)X-Transmission-Session-Id:\s*(\w+)', response.text).group(1)
except: except:
return None return None
self.session.headers.update({'x-transmission-session-id': self.auth}) self.session.headers.update({'x-transmission-session-id': self.auth})
#Validating Transmission authorization # Validating Transmission authorization
post_data = json.dumps({'arguments': {}, response = self._request(method='post', json={'method': 'session-get', 'arguments': {}})
'method': 'session-get',
}) try:
self._request(method='post', data=post_data) 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 return self.auth
def _add_torrent_uri(self, result): def _add_torrent_uri(self, result):
arguments = {'filename': result.url, return self._add_torrent({'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'
def _add_torrent_file(self, result): def _add_torrent_file(self, result):
arguments = {'metainfo': b64encode(result.content), return self._add_torrent({'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 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): def _set_torrent_ratio(self, result):
ratio = None ratio, mode = (result.ratio, None)[not result.ratio], 0
if result.ratio:
ratio = result.ratio
mode = 0
if ratio: if ratio:
if float(ratio) == -1: if -1 == float(ratio):
ratio = 0 ratio, mode = 0, 2
mode = 2 elif 0 <= float(ratio):
elif float(ratio) >= 0: ratio, mode = float(ratio), 1 # Stop seeding at seedRatioLimit
ratio = float(ratio)
mode = 1 # Stop seeding at seedRatioLimit
arguments = {'ids': [result.hash], response = self._request(method='post', json={
'seedRatioLimit': ratio, 'method': 'torrent-set',
'seedRatioMode': mode '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)
return response.json()['result'] == 'success' return 'success' == response.json().get('result', '')
def _set_torrent_seed_time(self, 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 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, response = self._request(method='post', json={
'method': 'torrent-set'}) 'method': 'torrent-set',
response = self._request(method='post', data=post_data) 'arguments': {'ids': [result.hash], 'seedIdleLimit': int(seed_time) * 60, 'seedIdleMode': 1}})
return response.json()['result'] == 'success' return 'success' == response.json().get('result', '')
else: else:
return True return True
@ -125,9 +113,9 @@ class TransmissionAPI(GenericClient):
arguments = {'ids': [result.hash]} arguments = {'ids': [result.hash]}
if result.priority == -1: if -1 == result.priority:
arguments['priority-low'] = [] arguments['priority-low'] = []
elif result.priority == 1: elif 1 == result.priority:
# set high priority for all files in torrent # set high priority for all files in torrent
arguments['priority-high'] = [] arguments['priority-high'] = []
# move torrent to the top if the queue # move torrent to the top if the queue
@ -137,14 +125,9 @@ class TransmissionAPI(GenericClient):
else: else:
arguments['priority-normal'] = [] arguments['priority-normal'] = []
post_data = json.dumps({ response = self._request(method='post', json={'method': 'torrent-set', 'arguments': arguments})
'arguments': arguments,
'method': 'torrent-set',
}) return 'success' == response.json().get('result', '')
response = self._request(method='post', data=post_data)
return response.json()['result'] == 'success'
api = TransmissionAPI() api = TransmissionAPI()

View file

@ -139,7 +139,7 @@ def snatch_episode(result, end_status=SNATCHED):
return False return False
# Snatches torrent with client # Snatches torrent with client
client = clients.getClientIstance(sickbeard.TORRENT_METHOD)() client = clients.getClientIstance(sickbeard.TORRENT_METHOD)()
dl_result = client.sendTORRENT(result) dl_result = client.send_torrent(result)
else: else:
logger.log(u'Unknown result type, unable to download it', logger.ERROR) logger.log(u'Unknown result type, unable to download it', logger.ERROR)
dl_result = False dl_result = False

View file

@ -676,7 +676,7 @@ class Home(MainHandler):
client = clients.getClientIstance(torrent_method) client = clients.getClientIstance(torrent_method)
connection, accesMsg = client(host, username, password).testAuthentication() connection, accesMsg = client(host, username, password).test_authentication()
return accesMsg return accesMsg