From ca2c4a0b3ea49e5f23665c9af414980a4745e15c Mon Sep 17 00:00:00 2001 From: Dean Gardiner Date: Wed, 12 Feb 2014 02:27:32 +1300 Subject: [PATCH 1/2] Updated rtorrent-python library (HTTP-RPC support) - Added URI transforming to cleanly support HTTP-RPC --- libs/rtorrent/__init__.py | 27 ++++++++++++---- libs/rtorrent/common.py | 67 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/libs/rtorrent/__init__.py b/libs/rtorrent/__init__.py index 2c0f3fa9..4aec991e 100755 --- a/libs/rtorrent/__init__.py +++ b/libs/rtorrent/__init__.py @@ -22,8 +22,8 @@ import os.path import time import xmlrpclib -from rtorrent.common import find_torrent, \ - is_valid_port, convert_version_tuple_to_str +from rtorrent.common import find_torrent, join_uri, \ + update_uri, 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 @@ -48,18 +48,18 @@ class RTorrent: def __init__(self, uri, username=None, password=None, verify=False, sp=None, sp_kwargs=None): - self.uri = uri # : From X{__init__(self, url)} + self.uri = self._transform_uri(uri) # : From X{__init__(self, url)} self.username = username self.password = password - self.schema = urllib.splittype(uri)[0] + self.scheme = urllib.splittype(self.uri)[0] if sp: self.sp = sp - elif self.schema in ['http', 'https']: + elif self.scheme in ['http', 'https']: self.sp = HTTPServerProxy - elif self.schema == 'scgi': + elif self.scheme == 'scgi': self.sp = SCGIServerProxy else: raise NotImplementedError() @@ -74,10 +74,23 @@ class RTorrent: if verify is True: self._verify_conn() + def _transform_uri(self, uri): + scheme = urllib.splittype(uri)[0] + + if scheme == 'httprpc' or scheme.startswith('httprpc+'): + # Try find HTTPRPC transport (token after '+' in 'httprpc+https'), otherwise assume HTTP + transport = scheme[scheme.index('+') + 1:] if '+' in scheme else 'http' + + # Transform URI with new path and scheme + uri = join_uri(uri, 'plugins/httprpc/action.php', construct=False) + return update_uri(uri, scheme=transport) + + return uri + def _get_conn(self): """Get ServerProxy instance""" if self.username is not None and self.password is not None: - if self.schema == 'scgi': + if self.scheme == 'scgi': raise NotImplementedError() return self.sp( diff --git a/libs/rtorrent/common.py b/libs/rtorrent/common.py index 371c71c3..668865b0 100755 --- a/libs/rtorrent/common.py +++ b/libs/rtorrent/common.py @@ -17,7 +17,8 @@ # 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 urlparse +import os from rtorrent.compat import is_py3 @@ -84,3 +85,67 @@ def safe_repr(fmt, *args, **kwargs): return out.encode("utf-8") else: return fmt.format(*args, **kwargs) + + +def split_path(path): + fragments = path.split('/') + + if len(fragments) == 1: + return fragments + + if not fragments[-1]: + return fragments[:-1] + + return fragments + + +def join_path(base, path): + # Return if we have a new absolute path + if os.path.isabs(path): + return path + + # non-absolute base encountered + if base and not os.path.isabs(base): + raise NotImplementedError() + + return '/'.join(split_path(base) + split_path(path)) + + +def join_uri(base, uri, construct=True): + p_uri = urlparse.urlparse(uri) + + # Return if there is nothing to join + if not p_uri.path: + return base + + scheme, netloc, path, params, query, fragment = urlparse.urlparse(base) + + # Switch to 'uri' parts + _, _, _, params, query, fragment = p_uri + + path = join_path(path, p_uri.path) + + result = urlparse.ParseResult(scheme, netloc, path, params, query, fragment) + + if not construct: + return result + + # Construct from parts + return urlparse.urlunparse(result) + + +def update_uri(uri, construct=True, **kwargs): + if isinstance(uri, urlparse.ParseResult): + uri = dict(uri._asdict()) + + if type(uri) is not dict: + raise ValueError("Unknown URI type") + + uri.update(kwargs) + + result = urlparse.ParseResult(**uri) + + if not construct: + return result + + return urlparse.urlunparse(result) From d448b8cd9940fe76d42ca2c5a2d90e3a2273f612 Mon Sep 17 00:00:00 2001 From: Dean Gardiner Date: Fri, 21 Feb 2014 15:48:17 +1300 Subject: [PATCH 2/2] Adjusted rtorrent connect method to work with httprpc URIs, adjusted option descriptions --- couchpotato/core/downloaders/rtorrent/__init__.py | 6 +++--- couchpotato/core/downloaders/rtorrent/main.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/couchpotato/core/downloaders/rtorrent/__init__.py b/couchpotato/core/downloaders/rtorrent/__init__.py index 4a593fd2..f793cad1 100755 --- a/couchpotato/core/downloaders/rtorrent/__init__.py +++ b/couchpotato/core/downloaders/rtorrent/__init__.py @@ -31,8 +31,8 @@ config = [{ { 'name': 'host', 'default': 'localhost:80', - 'description': 'Hostname with port or XML-RPC Endpoint URI. Usually scgi://localhost:5000 ' - 'or localhost:80' + 'description': 'RPC Communication URI. Usually scgi://localhost:5000, ' + 'httprpc://localhost/rutorrent or localhost:80' }, { 'name': 'ssl', @@ -46,7 +46,7 @@ config = [{ 'type': 'string', 'default': 'RPC2', 'advanced': True, - 'description': 'Change if you don\'t run rTorrent RPC at the default url.', + 'description': 'Change if your RPC mount is at a different path.', }, { 'name': 'username', diff --git a/couchpotato/core/downloaders/rtorrent/main.py b/couchpotato/core/downloaders/rtorrent/main.py index 1fbae354..b7b18b86 100755 --- a/couchpotato/core/downloaders/rtorrent/main.py +++ b/couchpotato/core/downloaders/rtorrent/main.py @@ -1,14 +1,15 @@ -from base64 import b16encode, b32decode -from bencode import bencode, bdecode from couchpotato.core.downloaders.base import Downloader, ReleaseDownloadList from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.helpers.encoding import sp from couchpotato.core.helpers.variable import cleanHost, splitString from couchpotato.core.logger import CPLog +from base64 import b16encode, b32decode +from bencode import bencode, bdecode from datetime import timedelta from hashlib import sha1 from rtorrent import RTorrent from rtorrent.err import MethodError +from urlparse import urlparse import os log = CPLog(__name__) @@ -51,7 +52,12 @@ class rTorrent(Downloader): if self.rt is not None: return self.rt - url = cleanHost(self.conf('host'), protocol = True, ssl = self.conf('ssl')) + self.conf('rpc_url') + url = cleanHost(self.conf('host'), protocol = True, ssl = self.conf('ssl')) + parsed = urlparse(url) + + # rpc_url is only used on http/https scgi pass-through + if parsed.scheme in ['http', 'https']: + url += self.conf('rpc_url') if self.conf('username') and self.conf('password'): self.rt = RTorrent(