Merge pull request #834 from JackDandy/feature/ChangeRt

Change update rTorrent systems.
This commit is contained in:
JackDandy 2016-12-07 16:49:11 +00:00 committed by GitHub
commit 2c6016234f
11 changed files with 162 additions and 122 deletions

View file

@ -199,6 +199,7 @@
* Change refresh page when torrent providers are enabled/disabled * Change refresh page when torrent providers are enabled/disabled
* Change only display Search Settings/"Usenet retention" if Search NZBs is enabled * Change only display Search Settings/"Usenet retention" if Search NZBs is enabled
* Change sab API request to prevent naming mismatch * Change sab API request to prevent naming mismatch
* Change update rTorrent systems
[develop changelog] [develop changelog]
* Change send nzb data to NZBGet for Anizb instead of url * Change send nzb data to NZBGet for Anizb instead of url

View file

@ -17,10 +17,16 @@
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import urllib try:
import urllib.parse as urlparser
except ImportError:
import urllib as urlparser
import os.path import os.path
import time import time
import xmlrpclib try:
import xmlrpc.client as xmlrpclib
except ImportError:
import xmlrpclib
from rtorrent.common import find_torrent, \ from rtorrent.common import find_torrent, \
is_valid_port, convert_version_tuple_to_str is_valid_port, convert_version_tuple_to_str
@ -53,7 +59,7 @@ class RTorrent:
self.username = username self.username = username
self.password = password self.password = password
self.schema = urllib.splittype(uri)[0] self.schema = urlparser.splittype(uri)[0]
if sp: if sp:
self.sp = sp self.sp = sp
@ -210,34 +216,36 @@ class RTorrent:
# load magnet # load magnet
getattr(p, func_name)(magneturl) getattr(p, func_name)(magneturl)
t = None
if verify_load: if verify_load:
MAX_RETRIES = 3 max_retries = 3
i = 0 i = 0
while i < MAX_RETRIES: while i < max_retries:
for torrent in self.get_torrents(): for t in self.get_torrents():
if torrent.info_hash != info_hash: if t.info_hash != info_hash:
continue continue
time.sleep(1) time.sleep(1)
i += 1 i += 1
# Resolve magnet to torrent if t:
torrent.start() # Resolve magnet to torrent
t.start()
assert info_hash in [t.info_hash for t in self.torrents],\ assert info_hash in [t.info_hash for t in self.torrents],\
"Adding torrent was unsuccessful." "Adding torrent was unsuccessful."
MAX_RETRIES = 3 max_retries = 3
i = 0 i = 0
while i < MAX_RETRIES: while i < max_retries:
for torrent in self.get_torrents(): for t in self.get_torrents():
if torrent.info_hash == info_hash: if t.info_hash == info_hash:
if str(info_hash) not in str(torrent.name) : if str(info_hash) not in str(t.name) :
time.sleep(1) time.sleep(1)
i += 1 i += 1
return(torrent) return t
def load_torrent(self, torrent, start=False, verbose=False, verify_load=True): def load_torrent(self, torrent, start=False, verbose=False, verify_load=True, verify_retries=3):
""" """
Loads torrent into rTorrent (with various enhancements) Loads torrent into rTorrent (with various enhancements)
@ -282,9 +290,8 @@ class RTorrent:
getattr(p, func_name)(torrent) getattr(p, func_name)(torrent)
if verify_load: if verify_load:
MAX_RETRIES = 3
i = 0 i = 0
while i < MAX_RETRIES: while i < verify_retries:
self.get_torrents() self.get_torrents()
if info_hash in [t.info_hash for t in self.torrents]: if info_hash in [t.info_hash for t in self.torrents]:
break break

View file

@ -267,7 +267,7 @@ def _encode_dict(data):
def encode(data): def encode(data):
if isinstance(data, bool): if isinstance(data, bool):
return False return False
elif isinstance(data, int): elif isinstance(data, (int, long)):
return _encode_int(data) return _encode_int(data)
elif isinstance(data, bytes): elif isinstance(data, bytes):
return _encode_string(data) return _encode_string(data)

View file

@ -77,7 +77,7 @@ class TorrentParser():
self.file_type = "file" self.file_type = "file"
self._raw_torrent = open(self.torrent, "rb").read() self._raw_torrent = open(self.torrent, "rb").read()
# url? # url?
elif re.search("^(http|ftp):\/\/", self.torrent, re.I): elif re.search("^(http|ftp)s?:\/\/", self.torrent, re.I):
self.file_type = "url" self.file_type = "url"
self._raw_torrent = urlopen(self.torrent).read() self._raw_torrent = urlopen(self.torrent).read()
@ -90,10 +90,10 @@ class TorrentParser():
def _calc_info_hash(self): def _calc_info_hash(self):
self.info_hash = None self.info_hash = None
if "info" in self._torrent_decoded.keys(): if "info" in self._torrent_decoded.keys():
info_encoded = bencode.encode(self._torrent_decoded["info"]) info_encoded = bencode.encode(self._torrent_decoded["info"])
if info_encoded: if info_encoded:
self.info_hash = hashlib.sha1(info_encoded).hexdigest().upper() self.info_hash = hashlib.sha1(info_encoded).hexdigest().upper()
return(self.info_hash) return(self.info_hash)

View file

@ -22,7 +22,10 @@
from base64 import encodestring from base64 import encodestring
import string import string
import xmlrpclib try:
import xmlrpc.client as xmlrpclib
except:
import xmlrpclib
class BasicAuthTransport(xmlrpclib.Transport): class BasicAuthTransport(xmlrpclib.Transport):

View file

@ -80,12 +80,25 @@
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE. # OF THIS SOFTWARE.
from __future__ import print_function
import httplib try:
import http.client as httplib
except ImportError:
import httplib
import re import re
import socket import socket
import urllib import sys
import xmlrpclib try:
import urllib.parse as urlparser
except ImportError:
import urllib as urlparser
try:
import xmlrpc.client as xmlrpclib
except:
import xmlrpclib
import errno import errno
@ -96,7 +109,7 @@ class SCGITransport(xmlrpclib.Transport):
for i in (0, 1): for i in (0, 1):
try: try:
return self.single_request(host, handler, request_body, verbose) return self.single_request(host, handler, request_body, verbose)
except socket.error, e: except socket.error as e:
if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE): if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
raise raise
except httplib.BadStatusLine: #close after we sent request except httplib.BadStatusLine: #close after we sent request
@ -105,8 +118,8 @@ class SCGITransport(xmlrpclib.Transport):
def single_request(self, host, handler, request_body, verbose=0): def single_request(self, host, handler, request_body, verbose=0):
# Add SCGI headers to the request. # Add SCGI headers to the request.
headers = {'CONTENT_LENGTH': str(len(request_body)), 'SCGI': '1'} headers = [('CONTENT_LENGTH', str(len(request_body))), ('SCGI', '1')]
header = '\x00'.join(('%s\x00%s' % item for item in headers.iteritems())) + '\x00' header = '\x00'.join(['%s\x00%s' % (key, value) for key, value in headers]) + '\x00'
header = '%d:%s' % (len(header), header) header = '%d:%s' % (len(header), header)
request_body = '%s,%s' % (header, request_body) request_body = '%s,%s' % (header, request_body)
@ -114,7 +127,7 @@ class SCGITransport(xmlrpclib.Transport):
try: try:
if host: if host:
host, port = urllib.splitport(host) host, port = urlparser.splitport(host)
addrinfo = socket.getaddrinfo(host, int(port), socket.AF_INET, addrinfo = socket.getaddrinfo(host, int(port), socket.AF_INET,
socket.SOCK_STREAM) socket.SOCK_STREAM)
sock = socket.socket(*addrinfo[0][:3]) sock = socket.socket(*addrinfo[0][:3])
@ -125,7 +138,10 @@ class SCGITransport(xmlrpclib.Transport):
self.verbose = verbose self.verbose = verbose
sock.send(request_body) if sys.version_info[0] > 2:
sock.send(bytes(request_body, "utf-8"))
else:
sock.send(request_body)
return self.parse_response(sock.makefile()) return self.parse_response(sock.makefile())
finally: finally:
if sock: if sock:
@ -142,11 +158,17 @@ class SCGITransport(xmlrpclib.Transport):
response_body += data response_body += data
# Remove SCGI headers from the response. # Remove SCGI headers from the response.
response_header, response_body = re.split(r'\n\s*?\n', response_body,
maxsplit=1)
if self.verbose: if self.verbose:
print 'body:', repr(response_body) print('body:', repr(response_body))
try:
response_header, response_body = re.split(r'\n\s*?\n', response_body,
maxsplit=1)
except ValueError:
print("error in response: %s", response_body)
p.close()
u.close()
p.feed(response_body) p.feed(response_body)
p.close() p.close()
@ -157,10 +179,10 @@ class SCGITransport(xmlrpclib.Transport):
class SCGIServerProxy(xmlrpclib.ServerProxy): class SCGIServerProxy(xmlrpclib.ServerProxy):
def __init__(self, uri, transport=None, encoding=None, verbose=False, def __init__(self, uri, transport=None, encoding=None, verbose=False,
allow_none=False, use_datetime=False): allow_none=False, use_datetime=False):
type, uri = urllib.splittype(uri) type, uri = urlparser.splittype(uri)
if type not in ('scgi'): if type not in ('scgi'):
raise IOError('unsupported XML-RPC protocol') raise IOError('unsupported XML-RPC protocol')
self.__host, self.__handler = urllib.splithost(uri) self.__host, self.__handler = urlparser.splithost(uri)
if not self.__handler: if not self.__handler:
self.__handler = '/' self.__handler = '/'

View file

@ -338,6 +338,24 @@ class Torrent:
else: else:
return p.view.set_not_visible(self.info_hash, view) return p.view.set_not_visible(self.info_hash, view)
def add_tracker(self, group, tracker):
"""
Add tracker to torrent
@param group: The group to add the tracker to
@type group: int
@param tracker: The tracker url
@type tracker: str
@return: if successful, 0
@rtype: int
"""
m = rtorrent.rpc.Multicall(self)
self.multicall_add(m, "d.tracker.insert", group, tracker)
return (m.call()[-1])
############################################################################ ############################################################################
# CUSTOM METHODS (Not part of the official rTorrent API) # CUSTOM METHODS (Not part of the official rTorrent API)
########################################################################## ##########################################################################

View file

@ -67,6 +67,21 @@ class Tracker:
multicall.call() multicall.call()
def append_tracker(self, tracker):
"""
Append tracker to current tracker group
@param tracker: The tracker url
@type tracker: str
@return: if successful, 0
@rtype: int
"""
m = rtorrent.rpc.Multicall(self)
self.multicall_add(m, "d.tracker.insert", self.index, tracker)
return (m.call()[-1])
methods = [ methods = [
# RETRIEVERS # RETRIEVERS
Method(Tracker, 'is_enabled', 't.is_enabled', boolean=True), Method(Tracker, 'is_enabled', 't.is_enabled', boolean=True),

View file

@ -228,6 +228,6 @@ class GenericClient(object):
# 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'
return False, 'Error: Unable to get %s Authentication, check your config!' % self.name return False, 'Error: Unable to get %s authentication, check your config!' % self.name
except (StandardError, Exception): except (StandardError, Exception):
return False, 'Error: Unable to connect to %s' % self.name return False, 'Error: Unable to connect to %s' % self.name

View file

@ -16,100 +16,74 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>. # along with SickGear. If not, see <http://www.gnu.org/licenses/>.
import sickbeard import xmlrpclib
from sickbeard import helpers, TORRENT_LABEL, TORRENT_PATH
from sickbeard.clients.generic import GenericClient from sickbeard.clients.generic import GenericClient
from lib.rtorrent import RTorrent from lib.rtorrent import RTorrent
class rTorrentAPI(GenericClient): class RtorrentAPI(GenericClient):
def __init__(self, host=None, username=None, password=None): def __init__(self, host=None, username=None, password=None):
super(rTorrentAPI, self).__init__('rTorrent', host, username, password)
if host and host.startswith('scgi:') and any([username, password]):
username = password = None
super(RtorrentAPI, self).__init__('rTorrent', host, username, password)
# self.url = self.host
def _get_auth(self): def _get_auth(self):
self.auth = None self.auth = None
if self.host:
if self.auth is not None: try:
return self.auth if self.host and self.host.startswith('scgi:') and any([self.username, self.password]):
self.username = self.password = None
if not self.host: self.auth = RTorrent(self.host, self.username, self.password, True)
return except (AssertionError, xmlrpclib.ProtocolError) as e:
pass
if self.username and self.password:
self.auth = RTorrent(self.host, self.username, self.password)
else:
self.auth = RTorrent(self.host, None, None, True)
return self.auth return self.auth
def _add_torrent_uri(self, result): def _add_torrent(self, cmd, **kwargs):
torrent = None
if not self.auth: if self.auth:
return False try:
# Send magnet to rTorrent
if 'file' == cmd:
torrent = self.auth.load_torrent(kwargs['file'])
elif 'magnet' == cmd:
torrent = self.auth.load_magnet(kwargs['url'], kwargs['btih'])
if not result: if torrent:
return False
try: if TORRENT_LABEL:
# Send magnet to rTorrent torrent.set_custom(1, TORRENT_LABEL)
torrent = self.auth.load_magnet(result.url, result.hash)
if not torrent: if TORRENT_PATH:
return False torrent.set_directory(TORRENT_PATH)
# Set label torrent.start()
if sickbeard.TORRENT_LABEL:
torrent.set_custom(1, sickbeard.TORRENT_LABEL)
if sickbeard.TORRENT_PATH: except (StandardError, Exception) as e:
torrent.set_directory(sickbeard.TORRENT_PATH) pass
# Start torrent return any([torrent])
torrent.start()
return True
except:
return False
def _add_torrent_file(self, result): def _add_torrent_file(self, result):
if not self.auth: if result:
return False return self._add_torrent('file', file=result.content)
return False
if not result: def _add_torrent_uri(self, result):
return False
# group_name = 'sb_test' ##### Use provider instead of _test if result:
# if not self._set_torrent_ratio(group_name): return self._add_torrent('magnet', uri=result.url, btih=result.hash)
# return False return False
# Send request to rTorrent #def _set_torrent_ratio(self, name):
try:
# Send torrent to rTorrent
torrent = self.auth.load_torrent(result.content)
if not torrent:
return False
# Set label
if sickbeard.TORRENT_LABEL:
torrent.set_custom(1, sickbeard.TORRENT_LABEL)
if sickbeard.TORRENT_PATH:
torrent.set_directory(sickbeard.TORRENT_PATH)
# Set Ratio Group
# torrent.set_visible(group_name)
# Start torrent
torrent.start()
return True
except:
return False
def _set_torrent_ratio(self, name):
# if not name: # if not name:
# return False # return False
@ -143,18 +117,18 @@ class rTorrentAPI(GenericClient):
# except: # except:
# return False # return False
return True # return True
def test_authentication(self): def test_authentication(self):
try: try:
self._get_auth() self._get_auth()
if self.auth is not None: if None is self.auth:
return True, 'Success: Connected and Authenticated' return False, 'Error: Unable to get %s authentication, check your config!' % self.name
else: return True, 'Success: Connected and Authenticated'
return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!'
except Exception: except (StandardError, Exception):
return False, 'Error: Unable to connect to ' + self.name return False, 'Error: Unable to connect to %s' % self.name
api = rTorrentAPI() api = RtorrentAPI()

View file

@ -55,7 +55,7 @@ class FreshOnTVProvider(generic.TorrentProvider):
post_params={'form_tmpl': True}, post_params={'form_tmpl': True},
failed_msg=(lambda y=None: 'DDoS protection by CloudFlare' in y and failed_msg=(lambda y=None: 'DDoS protection by CloudFlare' in y and
u'Unable to login to %s due to CloudFlare DDoS javascript check' or u'Unable to login to %s due to CloudFlare DDoS javascript check' or
'Username does not exist' in x and 'Username does not exist' in y and
u'Invalid username or password for %s. Check settings' or u'Invalid username or password for %s. Check settings' or
u'Failed to authenticate or parse a response from %s, abort provider')) u'Failed to authenticate or parse a response from %s, abort provider'))