diff --git a/CHANGES.md b/CHANGES.md index a8f93743..93f07347 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -199,6 +199,7 @@ * Change refresh page when torrent providers are enabled/disabled * Change only display Search Settings/"Usenet retention" if Search NZBs is enabled * Change sab API request to prevent naming mismatch +* Change update rTorrent systems [develop changelog] * Change send nzb data to NZBGet for Anizb instead of url diff --git a/lib/rtorrent/__init__.py b/lib/rtorrent/__init__.py index 290ef115..26957bc9 100644 --- a/lib/rtorrent/__init__.py +++ b/lib/rtorrent/__init__.py @@ -17,10 +17,16 @@ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # 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 time -import xmlrpclib +try: + import xmlrpc.client as xmlrpclib +except ImportError: + import xmlrpclib from rtorrent.common import find_torrent, \ is_valid_port, convert_version_tuple_to_str @@ -53,7 +59,7 @@ class RTorrent: self.username = username self.password = password - self.schema = urllib.splittype(uri)[0] + self.schema = urlparser.splittype(uri)[0] if sp: self.sp = sp @@ -210,34 +216,36 @@ class RTorrent: # load magnet getattr(p, func_name)(magneturl) + t = None if verify_load: - MAX_RETRIES = 3 + max_retries = 3 i = 0 - while i < MAX_RETRIES: - for torrent in self.get_torrents(): - if torrent.info_hash != info_hash: + while i < max_retries: + for t in self.get_torrents(): + if t.info_hash != info_hash: continue time.sleep(1) i += 1 - # Resolve magnet to torrent - torrent.start() + if t: + # Resolve magnet to torrent + t.start() - assert info_hash in [t.info_hash for t in self.torrents],\ - "Adding torrent was unsuccessful." + assert info_hash in [t.info_hash for t in self.torrents],\ + "Adding torrent was unsuccessful." - MAX_RETRIES = 3 - i = 0 - 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) : - time.sleep(1) - i += 1 + max_retries = 3 + i = 0 + while i < max_retries: + for t in self.get_torrents(): + if t.info_hash == info_hash: + if str(info_hash) not in str(t.name) : + time.sleep(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) @@ -282,9 +290,8 @@ class RTorrent: getattr(p, func_name)(torrent) if verify_load: - MAX_RETRIES = 3 i = 0 - while i < MAX_RETRIES: + while i < verify_retries: self.get_torrents() if info_hash in [t.info_hash for t in self.torrents]: break diff --git a/lib/rtorrent/lib/bencode.py b/lib/rtorrent/lib/bencode.py index 97bd2f0e..ba99ef93 100644 --- a/lib/rtorrent/lib/bencode.py +++ b/lib/rtorrent/lib/bencode.py @@ -267,7 +267,7 @@ def _encode_dict(data): def encode(data): if isinstance(data, bool): return False - elif isinstance(data, int): + elif isinstance(data, (int, long)): return _encode_int(data) elif isinstance(data, bytes): return _encode_string(data) diff --git a/lib/rtorrent/lib/torrentparser.py b/lib/rtorrent/lib/torrentparser.py index 30170d32..b59ae221 100644 --- a/lib/rtorrent/lib/torrentparser.py +++ b/lib/rtorrent/lib/torrentparser.py @@ -77,7 +77,7 @@ class TorrentParser(): self.file_type = "file" self._raw_torrent = open(self.torrent, "rb").read() # 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._raw_torrent = urlopen(self.torrent).read() @@ -90,10 +90,10 @@ class TorrentParser(): def _calc_info_hash(self): self.info_hash = None 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: - self.info_hash = hashlib.sha1(info_encoded).hexdigest().upper() + if info_encoded: + self.info_hash = hashlib.sha1(info_encoded).hexdigest().upper() return(self.info_hash) diff --git a/lib/rtorrent/lib/xmlrpc/basic_auth.py b/lib/rtorrent/lib/xmlrpc/basic_auth.py index 20c02d9a..76344c53 100644 --- a/lib/rtorrent/lib/xmlrpc/basic_auth.py +++ b/lib/rtorrent/lib/xmlrpc/basic_auth.py @@ -22,7 +22,10 @@ from base64 import encodestring import string -import xmlrpclib +try: + import xmlrpc.client as xmlrpclib +except: + import xmlrpclib class BasicAuthTransport(xmlrpclib.Transport): diff --git a/lib/rtorrent/lib/xmlrpc/scgi.py b/lib/rtorrent/lib/xmlrpc/scgi.py index 5ba61fa5..5c647fb9 100644 --- a/lib/rtorrent/lib/xmlrpc/scgi.py +++ b/lib/rtorrent/lib/xmlrpc/scgi.py @@ -80,12 +80,25 @@ # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE # OF THIS SOFTWARE. +from __future__ import print_function -import httplib +try: + import http.client as httplib +except ImportError: + import httplib import re import socket -import urllib -import xmlrpclib +import sys +try: + import urllib.parse as urlparser +except ImportError: + import urllib as urlparser + +try: + import xmlrpc.client as xmlrpclib +except: + import xmlrpclib + import errno @@ -96,7 +109,7 @@ class SCGITransport(xmlrpclib.Transport): for i in (0, 1): try: 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): raise 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): # Add SCGI headers to the request. - headers = {'CONTENT_LENGTH': str(len(request_body)), 'SCGI': '1'} - header = '\x00'.join(('%s\x00%s' % item for item in headers.iteritems())) + '\x00' + headers = [('CONTENT_LENGTH', str(len(request_body))), ('SCGI', '1')] + header = '\x00'.join(['%s\x00%s' % (key, value) for key, value in headers]) + '\x00' header = '%d:%s' % (len(header), header) request_body = '%s,%s' % (header, request_body) @@ -114,7 +127,7 @@ class SCGITransport(xmlrpclib.Transport): try: if host: - host, port = urllib.splitport(host) + host, port = urlparser.splitport(host) addrinfo = socket.getaddrinfo(host, int(port), socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(*addrinfo[0][:3]) @@ -125,7 +138,10 @@ class SCGITransport(xmlrpclib.Transport): 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()) finally: if sock: @@ -142,11 +158,17 @@ class SCGITransport(xmlrpclib.Transport): response_body += data # Remove SCGI headers from the response. - response_header, response_body = re.split(r'\n\s*?\n', response_body, - maxsplit=1) 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.close() @@ -157,10 +179,10 @@ class SCGITransport(xmlrpclib.Transport): class SCGIServerProxy(xmlrpclib.ServerProxy): def __init__(self, uri, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False): - type, uri = urllib.splittype(uri) + type, uri = urlparser.splittype(uri) if type not in ('scgi'): raise IOError('unsupported XML-RPC protocol') - self.__host, self.__handler = urllib.splithost(uri) + self.__host, self.__handler = urlparser.splithost(uri) if not self.__handler: self.__handler = '/' diff --git a/lib/rtorrent/torrent.py b/lib/rtorrent/torrent.py index bd6bb689..a158651d 100644 --- a/lib/rtorrent/torrent.py +++ b/lib/rtorrent/torrent.py @@ -338,6 +338,24 @@ class Torrent: else: 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) ########################################################################## diff --git a/lib/rtorrent/tracker.py b/lib/rtorrent/tracker.py index 81af2e49..b3b7bf94 100644 --- a/lib/rtorrent/tracker.py +++ b/lib/rtorrent/tracker.py @@ -67,6 +67,21 @@ class Tracker: 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 = [ # RETRIEVERS Method(Tracker, 'is_enabled', 't.is_enabled', boolean=True), diff --git a/sickbeard/clients/generic.py b/sickbeard/clients/generic.py index 254875bd..222f3b9a 100644 --- a/sickbeard/clients/generic.py +++ b/sickbeard/clients/generic.py @@ -228,6 +228,6 @@ class GenericClient(object): # FIXME: This test is redundant if authenticated and self.auth: 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): return False, 'Error: Unable to connect to %s' % self.name diff --git a/sickbeard/clients/rtorrent.py b/sickbeard/clients/rtorrent.py index f6011c1e..cff92a06 100644 --- a/sickbeard/clients/rtorrent.py +++ b/sickbeard/clients/rtorrent.py @@ -16,100 +16,74 @@ # You should have received a copy of the GNU General Public License # along with SickGear. If not, see . -import sickbeard +import xmlrpclib +from sickbeard import helpers, TORRENT_LABEL, TORRENT_PATH from sickbeard.clients.generic import GenericClient from lib.rtorrent import RTorrent -class rTorrentAPI(GenericClient): +class RtorrentAPI(GenericClient): 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): + self.auth = None - - if self.auth is not None: - return self.auth - - if not self.host: - return - - if self.username and self.password: - self.auth = RTorrent(self.host, self.username, self.password) - else: - self.auth = RTorrent(self.host, None, None, True) + if self.host: + try: + if self.host and self.host.startswith('scgi:') and any([self.username, self.password]): + self.username = self.password = None + self.auth = RTorrent(self.host, self.username, self.password, True) + except (AssertionError, xmlrpclib.ProtocolError) as e: + pass return self.auth - def _add_torrent_uri(self, result): + def _add_torrent(self, cmd, **kwargs): + torrent = None - if not self.auth: - return False + if self.auth: + 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: - return False + if torrent: - try: - # Send magnet to rTorrent - torrent = self.auth.load_magnet(result.url, result.hash) + if TORRENT_LABEL: + torrent.set_custom(1, TORRENT_LABEL) - if not torrent: - return False + if TORRENT_PATH: + torrent.set_directory(TORRENT_PATH) - # Set label - if sickbeard.TORRENT_LABEL: - torrent.set_custom(1, sickbeard.TORRENT_LABEL) + torrent.start() - if sickbeard.TORRENT_PATH: - torrent.set_directory(sickbeard.TORRENT_PATH) + except (StandardError, Exception) as e: + pass - # Start torrent - torrent.start() - - return True - - except: - return False + return any([torrent]) def _add_torrent_file(self, result): - if not self.auth: - return False + if result: + return self._add_torrent('file', file=result.content) + return False - if not result: - return False + def _add_torrent_uri(self, result): - # group_name = 'sb_test' ##### Use provider instead of _test - # if not self._set_torrent_ratio(group_name): - # return False + if result: + return self._add_torrent('magnet', uri=result.url, btih=result.hash) + return False - # Send request to rTorrent - 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): + #def _set_torrent_ratio(self, name): # if not name: # return False @@ -143,18 +117,18 @@ class rTorrentAPI(GenericClient): # except: # return False - return True + # return True def test_authentication(self): try: self._get_auth() - if self.auth is not None: - return True, 'Success: Connected and Authenticated' - else: - return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!' - except Exception: - return False, 'Error: Unable to connect to ' + self.name + if None is self.auth: + return False, 'Error: Unable to get %s authentication, check your config!' % self.name + return True, 'Success: Connected and Authenticated' + + except (StandardError, Exception): + return False, 'Error: Unable to connect to %s' % self.name -api = rTorrentAPI() +api = RtorrentAPI() diff --git a/sickbeard/providers/freshontv.py b/sickbeard/providers/freshontv.py index f9d8aac3..7335dc09 100644 --- a/sickbeard/providers/freshontv.py +++ b/sickbeard/providers/freshontv.py @@ -55,7 +55,7 @@ class FreshOnTVProvider(generic.TorrentProvider): post_params={'form_tmpl': True}, 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 - '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'Failed to authenticate or parse a response from %s, abort provider'))