mirror of
https://github.com/moparisthebest/SickRage
synced 2024-12-23 16:28:49 -05:00
Improved rTorrent support.
Now use requests library. Added SSL support with Basic and Digest support.
This commit is contained in:
parent
fb3cb22808
commit
3166f29d34
@ -466,12 +466,29 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair" id="torrent_auth_type">
|
||||
<label>
|
||||
<span class="component-title">Http Authentication</span>
|
||||
<span class="component-desc">
|
||||
<select name="torrent_auth_type" id="torrent_auth_type" class="form-control input-sm">
|
||||
#set $http_authtype = {'none': "None", 'basic': "Basic", 'digest': "Digest"}
|
||||
#for $authvalue,$authname in $http_authtype.items():
|
||||
#set $selected = $html_selected if $sickbeard.TORRENT_AUTH_TYPE == $authvalue else ''
|
||||
<option value="$authvalue"$selected>$authname</option>
|
||||
#end for
|
||||
</select>
|
||||
<p></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair" id="torrent_verify_cert_option">
|
||||
<label for="torrent_verify_cert">
|
||||
<span class="component-title">Verify certificate</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="torrent_verify_cert" class="enabler" id="torrent_verify_cert" <%= html_checked if sickbeard.TORRENT_VERIFY_CERT == True else '' %>/>
|
||||
<p>disable if you get "Deluge: Authentication Error" in your log</p>
|
||||
<p id="torrent_verify_deluge">disable if you get "Deluge: Authentication Error" in your log</p>
|
||||
<p id="torrent_verify_rtorrent">Verify SSL certificates for HTTPS requests</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -69,6 +69,9 @@ $(document).ready(function(){
|
||||
$(host_desc_rtorrent).hide();
|
||||
$(host_desc_torrent).show();
|
||||
$(torrent_verify_cert_option).hide();
|
||||
$(torrent_verify_deluge).hide();
|
||||
$(torrent_verify_rtorrent).hide();
|
||||
$(torrent_auth_type).hide();
|
||||
$(torrent_path_option).show();
|
||||
$(torrent_path_option).find('.fileBrowser').show();
|
||||
$(torrent_seed_time_option).hide();
|
||||
@ -96,6 +99,8 @@ $(document).ready(function(){
|
||||
} else if ('deluge' == selectedProvider){
|
||||
client = 'Deluge';
|
||||
$(torrent_verify_cert_option).show();
|
||||
$(torrent_verify_deluge).show();
|
||||
$(torrent_verify_rtorrent).hide();
|
||||
$(label_warning_deluge).show();
|
||||
$(label_anime_warning_deluge).show();
|
||||
$('#host_desc_torrent').text('URL to your Deluge client (e.g. http://localhost:8112)');
|
||||
@ -113,6 +118,10 @@ $(document).ready(function(){
|
||||
client = 'rTorrent';
|
||||
$(torrent_paused_option).hide();
|
||||
$('#host_desc_torrent').text('URL to your rTorrent client (e.g. scgi://localhost:5000 </br> or https://localhost/rutorrent/plugins/httprpc/action.php)');
|
||||
$(torrent_verify_cert_option).show();
|
||||
$(torrent_verify_deluge).hide();
|
||||
$(torrent_verify_rtorrent).show();
|
||||
$(torrent_auth_type).show();
|
||||
//$('#directory_title').text(client + directory);
|
||||
}
|
||||
$('#host_title').text(client + host);
|
||||
|
@ -22,15 +22,16 @@ import os.path
|
||||
import time
|
||||
import xmlrpclib
|
||||
|
||||
from rtorrent.common import find_torrent, \
|
||||
is_valid_port, convert_version_tuple_to_str
|
||||
from rtorrent.lib.torrentparser import TorrentParser
|
||||
from rtorrent.lib.xmlrpc.http import HTTPServerProxy
|
||||
from rtorrent.lib.xmlrpc.scgi import SCGIServerProxy
|
||||
from rtorrent.rpc import Method
|
||||
from rtorrent.lib.xmlrpc.basic_auth import BasicAuthTransport
|
||||
from rtorrent.torrent import Torrent
|
||||
from rtorrent.group import Group
|
||||
from rtorrent.common import (find_torrent, # @UnresolvedImport
|
||||
is_valid_port, # @UnresolvedImport
|
||||
convert_version_tuple_to_str) # @UnresolvedImport
|
||||
from rtorrent.lib.torrentparser import TorrentParser # @UnresolvedImport
|
||||
from rtorrent.lib.xmlrpc.http import HTTPServerProxy # @UnresolvedImport
|
||||
from rtorrent.lib.xmlrpc.scgi import SCGIServerProxy # @UnresolvedImport
|
||||
from rtorrent.rpc import Method # @UnresolvedImport
|
||||
from rtorrent.lib.xmlrpc.requests_transport import RequestsTransport # @UnresolvedImport @IgnorePep8
|
||||
from rtorrent.torrent import Torrent # @UnresolvedImport
|
||||
from rtorrent.group import Group # @UnresolvedImport
|
||||
import rtorrent.rpc # @UnresolvedImport
|
||||
|
||||
__version__ = "0.2.9"
|
||||
@ -43,11 +44,12 @@ MIN_RTORRENT_VERSION_STR = convert_version_tuple_to_str(MIN_RTORRENT_VERSION)
|
||||
|
||||
|
||||
class RTorrent:
|
||||
|
||||
""" Create a new rTorrent connection """
|
||||
rpc_prefix = None
|
||||
|
||||
def __init__(self, uri, username=None, password=None,
|
||||
verify=False, sp=None, sp_kwargs=None):
|
||||
verify=False, sp=None, sp_kwargs=None, tp_kwargs=None):
|
||||
self.uri = uri # : From X{__init__(self, url)}
|
||||
|
||||
self.username = username
|
||||
@ -59,6 +61,10 @@ class RTorrent:
|
||||
self.sp = sp
|
||||
elif self.schema in ['http', 'https']:
|
||||
self.sp = HTTPServerProxy
|
||||
if self.schema == 'https':
|
||||
self.isHttps = True
|
||||
else:
|
||||
self.isHttps = False
|
||||
elif self.schema == 'scgi':
|
||||
self.sp = SCGIServerProxy
|
||||
else:
|
||||
@ -66,6 +72,8 @@ class RTorrent:
|
||||
|
||||
self.sp_kwargs = sp_kwargs or {}
|
||||
|
||||
self.tp_kwargs = tp_kwargs or {}
|
||||
|
||||
self.torrents = [] # : List of L{Torrent} instances
|
||||
self._rpc_methods = [] # : List of rTorrent RPC methods
|
||||
self._torrent_cache = []
|
||||
@ -80,9 +88,30 @@ class RTorrent:
|
||||
if self.schema == 'scgi':
|
||||
raise NotImplementedError()
|
||||
|
||||
if 'authtype' not in self.tp_kwargs:
|
||||
authtype = None
|
||||
else:
|
||||
authtype = self.tp_kwargs['authtype']
|
||||
|
||||
if 'check_ssl_cert' not in self.tp_kwargs:
|
||||
check_ssl_cert = True
|
||||
else:
|
||||
check_ssl_cert = self.tp_kwargs['check_ssl_cert']
|
||||
|
||||
if 'proxies' not in self.tp_kwargs:
|
||||
proxies = None
|
||||
else:
|
||||
proxies = self.tp_kwargs['proxies']
|
||||
|
||||
return self.sp(
|
||||
self.uri,
|
||||
transport=BasicAuthTransport(self.username, self.password),
|
||||
transport=RequestsTransport(
|
||||
use_https=self.isHttps,
|
||||
authtype=authtype,
|
||||
username=self.username,
|
||||
password=self.password,
|
||||
check_ssl_cert=check_ssl_cert,
|
||||
proxies=proxies),
|
||||
**self.sp_kwargs
|
||||
)
|
||||
|
||||
@ -90,8 +119,10 @@ class RTorrent:
|
||||
|
||||
def _verify_conn(self):
|
||||
# check for rpc methods that should be available
|
||||
assert "system.client_version" in self._get_rpc_methods(), "Required RPC method not available."
|
||||
assert "system.library_version" in self._get_rpc_methods(), "Required RPC method not available."
|
||||
assert "system.client_version" in self._get_rpc_methods(
|
||||
), "Required RPC method not available."
|
||||
assert "system.library_version" in self._get_rpc_methods(
|
||||
), "Required RPC method not available."
|
||||
|
||||
# minimum rTorrent version check
|
||||
assert self._meets_version_requirement() is True,\
|
||||
@ -152,7 +183,8 @@ class RTorrent:
|
||||
for result in results:
|
||||
results_dict = {}
|
||||
# build results_dict
|
||||
for m, r in zip(retriever_methods, result[1:]): # result[0] is the info_hash
|
||||
# result[0] is the info_hash
|
||||
for m, r in zip(retriever_methods, result[1:]):
|
||||
results_dict[m.varname] = rtorrent.rpc.process_result(m, r)
|
||||
|
||||
self.torrents.append(
|
||||
@ -199,7 +231,7 @@ class RTorrent:
|
||||
|
||||
return(func_name)
|
||||
|
||||
def load_magnet(self, magneturl, info_hash, start=False, verbose=False, verify_load=True):
|
||||
def load_magnet(self, magneturl, info_hash, start=False, verbose=False, verify_load=True): # @IgnorePep8
|
||||
|
||||
p = self._get_conn()
|
||||
|
||||
@ -231,13 +263,13 @@ class RTorrent:
|
||||
while i < MAX_RETRIES:
|
||||
for torrent in self.get_torrents():
|
||||
if torrent.info_hash == info_hash:
|
||||
if str(info_hash) not in str(torrent.name) :
|
||||
if str(info_hash) not in str(torrent.name):
|
||||
time.sleep(1)
|
||||
i += 1
|
||||
|
||||
return(torrent)
|
||||
|
||||
def load_torrent(self, torrent, start=False, verbose=False, verify_load=True):
|
||||
def load_torrent(self, torrent, start=False, verbose=False, verify_load=True): # @IgnorePep8
|
||||
"""
|
||||
Loads torrent into rTorrent (with various enhancements)
|
||||
|
||||
@ -354,7 +386,7 @@ class RTorrent:
|
||||
if persistent is True:
|
||||
p.group.insert_persistent_view('', name)
|
||||
else:
|
||||
assert view is not None, "view parameter required on non-persistent groups"
|
||||
assert view is not None, "view parameter required on non-persistent groups" # @IgnorePep8
|
||||
p.group.insert('', name, view)
|
||||
|
||||
self._update_rpc_methods()
|
||||
|
188
lib/rtorrent/lib/xmlrpc/requests_transport.py
Normal file
188
lib/rtorrent/lib/xmlrpc/requests_transport.py
Normal file
@ -0,0 +1,188 @@
|
||||
# Copyright (c) 2013-2015 Alexandre Beloin, <alexandre.beloin@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""A transport for Python2/3 xmlrpc library using requests
|
||||
|
||||
Support:
|
||||
-SSL with Basic and Digest authentication
|
||||
-Proxies
|
||||
"""
|
||||
|
||||
try:
|
||||
import xmlrpc.client as xmlrpc_client
|
||||
except ImportError:
|
||||
import xmlrpclib as xmlrpc_client
|
||||
|
||||
import traceback
|
||||
|
||||
import requests
|
||||
from requests.exceptions import RequestException
|
||||
from requests.auth import HTTPBasicAuth
|
||||
from requests.auth import HTTPDigestAuth
|
||||
from requests.packages.urllib3 import disable_warnings # @UnresolvedImport
|
||||
|
||||
|
||||
class RequestsTransport(xmlrpc_client.Transport):
|
||||
|
||||
"""Transport class for xmlrpc using requests"""
|
||||
|
||||
def __init__(self, use_https=True, authtype=None, username=None,
|
||||
password=None, check_ssl_cert=True, proxies=None):
|
||||
"""Inits RequestsTransport.
|
||||
|
||||
Args:
|
||||
use_https: If true, https else http
|
||||
authtype: None, basic or digest
|
||||
username: Username
|
||||
password: Password
|
||||
check_ssl_cert: Check SSL certificate
|
||||
proxies: A dict of proxies(
|
||||
Ex: {"http": "http://10.10.1.10:3128",
|
||||
"https": "http://10.10.1.10:1080",})
|
||||
|
||||
Raises:
|
||||
ValueError: Invalid info
|
||||
"""
|
||||
# Python 2 can't use super on old style class.
|
||||
if issubclass(xmlrpc_client.Transport, object):
|
||||
super(RequestsTransport, self).__init__()
|
||||
else:
|
||||
xmlrpc_client.Transport.__init__(self)
|
||||
|
||||
self.user_agent = "Python Requests/" + requests.__version__
|
||||
|
||||
self._use_https = use_https
|
||||
self._check_ssl_cert = check_ssl_cert
|
||||
|
||||
if authtype == "basic" or authtype == "digest":
|
||||
self._authtype = authtype
|
||||
else:
|
||||
raise ValueError(
|
||||
"Supported authentication are: basic and digest")
|
||||
if authtype and (not username or not password):
|
||||
raise ValueError(
|
||||
"Username and password required when using authentication")
|
||||
|
||||
self._username = username
|
||||
self._password = password
|
||||
if proxies is None:
|
||||
self._proxies = {}
|
||||
else:
|
||||
self._proxies = proxies
|
||||
|
||||
def request(self, host, handler, request_body, verbose=0):
|
||||
"""Replace the xmlrpc request function.
|
||||
|
||||
Process xmlrpc request via requests library.
|
||||
|
||||
Args:
|
||||
host: Target host
|
||||
handler: Target PRC handler.
|
||||
request_body: XML-RPC request body.
|
||||
verbose: Debugging flag.
|
||||
|
||||
Returns:
|
||||
Parsed response.
|
||||
|
||||
Raises:
|
||||
RequestException: Error in requests
|
||||
"""
|
||||
if verbose:
|
||||
self._debug()
|
||||
|
||||
if not self._check_ssl_cert:
|
||||
disable_warnings()
|
||||
|
||||
headers = {'User-Agent': self.user_agent, 'Content-Type': 'text/xml', }
|
||||
|
||||
# Need to be done because the schema(http or https) is lost in
|
||||
# xmlrpc.Transport's init.
|
||||
if self._use_https:
|
||||
url = "https://{host}/{handler}".format(host=host, handler=handler)
|
||||
else:
|
||||
url = "http://{host}/{handler}".format(host=host, handler=handler)
|
||||
|
||||
# TODO Construct kwargs query instead
|
||||
try:
|
||||
if self._authtype == "basic":
|
||||
response = requests.post(
|
||||
url,
|
||||
data=request_body,
|
||||
headers=headers,
|
||||
verify=self._check_ssl_cert,
|
||||
auth=HTTPBasicAuth(
|
||||
self._username, self._password),
|
||||
proxies=self._proxies)
|
||||
elif self._authtype == "digest":
|
||||
response = requests.post(
|
||||
url,
|
||||
data=request_body,
|
||||
headers=headers,
|
||||
verify=self._check_ssl_cert,
|
||||
auth=HTTPDigestAuth(
|
||||
self._username, self._password),
|
||||
proxies=self._proxies)
|
||||
else:
|
||||
response = requests.post(
|
||||
url,
|
||||
data=request_body,
|
||||
headers=headers,
|
||||
verify=self._check_ssl_cert,
|
||||
proxies=self._proxies)
|
||||
|
||||
response.raise_for_status()
|
||||
except RequestException as error:
|
||||
raise xmlrpc_client.ProtocolError(url,
|
||||
error.message,
|
||||
traceback.format_exc(),
|
||||
response.headers)
|
||||
|
||||
return self.parse_response(response)
|
||||
|
||||
def parse_response(self, response):
|
||||
"""Replace the xmlrpc parse_response function.
|
||||
|
||||
Parse response.
|
||||
|
||||
Args:
|
||||
response: Requests return data
|
||||
|
||||
Returns:
|
||||
Response tuple and target method.
|
||||
"""
|
||||
p, u = self.getparser()
|
||||
p.feed(response.text)
|
||||
p.close()
|
||||
return u.close()
|
||||
|
||||
def _debug(self):
|
||||
"""Debug requests module.
|
||||
|
||||
Enable verbose logging from requests
|
||||
"""
|
||||
# TODO Ugly
|
||||
import logging
|
||||
try:
|
||||
import http.client as http_client
|
||||
except ImportError:
|
||||
import httplib as http_client
|
||||
|
||||
http_client.HTTPConnection.debuglevel = 1
|
||||
|
||||
logging.basicConfig()
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
requests_log = logging.getLogger("requests.packages.urllib3")
|
||||
requests_log.setLevel(logging.DEBUG)
|
||||
requests_log.propagate = True
|
@ -288,6 +288,7 @@ TORRENT_LABEL = ''
|
||||
TORRENT_LABEL_ANIME = ''
|
||||
TORRENT_VERIFY_CERT = False
|
||||
TORRENT_RPCURL = 'transmission'
|
||||
TORRENT_AUTH_TYPE = 'none'
|
||||
|
||||
USE_KODI = False
|
||||
KODI_ALWAYS_ON = True
|
||||
@ -503,7 +504,7 @@ def initialize(consoleLogging=True):
|
||||
HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, RANDOMIZE_PROVIDERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, TORRENT_METHOD, \
|
||||
SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_CATEGORY_ANIME, SAB_HOST, \
|
||||
NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_CATEGORY_ANIME, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \
|
||||
TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, TORRENT_RPCURL, \
|
||||
TORRENT_USERNAME, TORRENT_PASSWORD, TORRENT_HOST, TORRENT_PATH, TORRENT_SEED_TIME, TORRENT_PAUSED, TORRENT_HIGH_BANDWIDTH, TORRENT_LABEL, TORRENT_LABEL_ANIME, TORRENT_VERIFY_CERT, TORRENT_RPCURL, TORRENT_AUTH_TYPE, \
|
||||
USE_KODI, KODI_ALWAYS_ON, KODI_NOTIFY_ONSNATCH, KODI_NOTIFY_ONDOWNLOAD, KODI_NOTIFY_ONSUBTITLEDOWNLOAD, KODI_UPDATE_FULL, KODI_UPDATE_ONLYFIRST, \
|
||||
KODI_UPDATE_LIBRARY, KODI_HOST, KODI_USERNAME, KODI_PASSWORD, BACKLOG_FREQUENCY, \
|
||||
USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_REMOVE_WATCHLIST, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_DISABLE_SSL_VERIFY, TRAKT_TIMEOUT, \
|
||||
@ -816,6 +817,7 @@ def initialize(consoleLogging=True):
|
||||
TORRENT_LABEL_ANIME = check_setting_str(CFG, 'TORRENT', 'torrent_label_anime', '')
|
||||
TORRENT_VERIFY_CERT = bool(check_setting_int(CFG, 'TORRENT', 'torrent_verify_cert', 0))
|
||||
TORRENT_RPCURL = check_setting_str(CFG, 'TORRENT', 'torrent_rpcurl', 'transmission')
|
||||
TORRENT_AUTH_TYPE = check_setting_str(CFG, 'TORRENT', 'torrent_auth_type', '')
|
||||
|
||||
USE_KODI = bool(check_setting_int(CFG, 'KODI', 'use_kodi', 0))
|
||||
KODI_ALWAYS_ON = bool(check_setting_int(CFG, 'KODI', 'kodi_always_on', 1))
|
||||
@ -1681,6 +1683,7 @@ def save_config():
|
||||
new_config['TORRENT']['torrent_label_anime'] = TORRENT_LABEL_ANIME
|
||||
new_config['TORRENT']['torrent_verify_cert'] = int(TORRENT_VERIFY_CERT)
|
||||
new_config['TORRENT']['torrent_rpcurl'] = TORRENT_RPCURL
|
||||
new_config['TORRENT']['torrent_auth_type'] = TORRENT_AUTH_TYPE
|
||||
|
||||
new_config['KODI'] = {}
|
||||
new_config['KODI']['use_kodi'] = int(USE_KODI)
|
||||
|
@ -37,8 +37,15 @@ class rTorrentAPI(GenericClient):
|
||||
if not self.host:
|
||||
return
|
||||
|
||||
tp_kwargs = {}
|
||||
if sickbeard.TORRENT_AUTH_TYPE is not 'none':
|
||||
tp_kwargs['authtype'] = sickbeard.TORRENT_AUTH_TYPE
|
||||
|
||||
if not sickbeard.TORRENT_VERIFY_CERT:
|
||||
tp_kwargs['check_ssl_cert'] = False
|
||||
|
||||
if self.username and self.password:
|
||||
self.auth = RTorrent(self.host, self.username, self.password)
|
||||
self.auth = RTorrent(self.host, self.username, self.password, True, tp_kwargs=tp_kwargs)
|
||||
else:
|
||||
self.auth = RTorrent(self.host, None, None, True)
|
||||
|
||||
|
@ -3656,7 +3656,7 @@ class ConfigSearch(Config):
|
||||
torrent_dir=None, torrent_username=None, torrent_password=None, torrent_host=None,
|
||||
torrent_label=None, torrent_label_anime=None, torrent_path=None, torrent_verify_cert=None,
|
||||
torrent_seed_time=None, torrent_paused=None, torrent_high_bandwidth=None,
|
||||
torrent_rpcurl=None, ignore_words=None, require_words=None):
|
||||
torrent_rpcurl=None, torrent_auth_type = None, ignore_words=None, require_words=None):
|
||||
|
||||
results = []
|
||||
|
||||
@ -3717,6 +3717,7 @@ class ConfigSearch(Config):
|
||||
sickbeard.TORRENT_HIGH_BANDWIDTH = config.checkbox_to_value(torrent_high_bandwidth)
|
||||
sickbeard.TORRENT_HOST = config.clean_url(torrent_host)
|
||||
sickbeard.TORRENT_RPCURL = torrent_rpcurl
|
||||
sickbeard.TORRENT_AUTH_TYPE = torrent_auth_type
|
||||
|
||||
sickbeard.save_config()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user