Merge pull request #2326 from fuzeman/feature/dev_rtorrent
rTorrent Downloader - scgi support
This commit is contained in:
commit
32a4075979
6 files changed
+328
-72
No files matched your search
@@ -23,6 +23,8 @@ config = [{
|
|||||||
{
|
{
|
||||||
'name': 'url',
|
'name': 'url',
|
||||||
'default': 'http://localhost:80/RPC2',
|
'default': 'http://localhost:80/RPC2',
|
||||||
|
'description': 'XML-RPC Endpoint URI. Usually <strong>scgi://localhost:5000</strong> '
|
||||||
|
'or <strong>http://localhost:80/RPC2</strong>'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'username',
|
'name': 'username',
|
||||||
@@ -38,7 +40,7 @@ config = [{
|
|||||||
{
|
{
|
||||||
'name': 'directory',
|
'name': 'directory',
|
||||||
'type': 'directory',
|
'type': 'directory',
|
||||||
'description': 'Directory where rtorrent should download the files too.',
|
'description': 'Download to this directory. Keep empty for default rTorrent download directory.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'remove_complete',
|
'name': 'remove_complete',
|
||||||
@@ -48,14 +50,6 @@ config = [{
|
|||||||
'type': 'bool',
|
'type': 'bool',
|
||||||
'description': 'Remove the torrent after it finishes seeding.',
|
'description': 'Remove the torrent after it finishes seeding.',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'name': 'append_label',
|
|
||||||
'label': 'Append Label',
|
|
||||||
'default': False,
|
|
||||||
'advanced': True,
|
|
||||||
'type': 'bool',
|
|
||||||
'description': 'Append label to download location. Requires you to set the download location above.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'name': 'delete_files',
|
'name': 'delete_files',
|
||||||
'label': 'Remove files',
|
'label': 'Remove files',
|
||||||
@@ -64,6 +58,14 @@ config = [{
|
|||||||
'advanced': True,
|
'advanced': True,
|
||||||
'description': 'Also remove the leftover files.',
|
'description': 'Also remove the leftover files.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'name': 'append_label',
|
||||||
|
'label': 'Append Label',
|
||||||
|
'default': False,
|
||||||
|
'advanced': True,
|
||||||
|
'type': 'bool',
|
||||||
|
'description': 'Append label to download location. Requires you to set the download location above.',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'name': 'paused',
|
'name': 'paused',
|
||||||
'type': 'bool',
|
'type': 'bool',
|
||||||
|
|||||||
+30
-11
@@ -17,18 +17,21 @@
|
|||||||
# 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
|
||||||
|
import os.path
|
||||||
|
import time
|
||||||
|
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
|
||||||
from rtorrent.lib.torrentparser import TorrentParser
|
from rtorrent.lib.torrentparser import TorrentParser
|
||||||
from rtorrent.lib.xmlrpc.http import HTTPServerProxy
|
from rtorrent.lib.xmlrpc.http import HTTPServerProxy
|
||||||
from rtorrent.rpc import Method, BasicAuthTransport
|
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.torrent import Torrent
|
||||||
from rtorrent.group import Group
|
from rtorrent.group import Group
|
||||||
import os.path
|
|
||||||
import rtorrent.rpc # @UnresolvedImport
|
import rtorrent.rpc # @UnresolvedImport
|
||||||
import time
|
|
||||||
import xmlrpclib
|
|
||||||
|
|
||||||
__version__ = "0.2.9"
|
__version__ = "0.2.9"
|
||||||
__author__ = "Chris Lucas"
|
__author__ = "Chris Lucas"
|
||||||
@@ -43,13 +46,25 @@ class RTorrent:
|
|||||||
""" Create a new rTorrent connection """
|
""" Create a new rTorrent connection """
|
||||||
rpc_prefix = None
|
rpc_prefix = None
|
||||||
|
|
||||||
def __init__(self, url, username=None, password=None,
|
def __init__(self, uri, username=None, password=None,
|
||||||
verify=False, sp=HTTPServerProxy, sp_kwargs={}):
|
verify=False, sp=None, sp_kwargs=None):
|
||||||
self.url = url # : From X{__init__(self, url)}
|
self.uri = uri # : From X{__init__(self, url)}
|
||||||
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.sp = sp
|
|
||||||
self.sp_kwargs = sp_kwargs
|
self.schema = urllib.splittype(uri)[0]
|
||||||
|
|
||||||
|
if sp:
|
||||||
|
self.sp = sp
|
||||||
|
elif self.schema in ['http', 'https']:
|
||||||
|
self.sp = HTTPServerProxy
|
||||||
|
elif self.schema == 'scgi':
|
||||||
|
self.sp = SCGIServerProxy
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
self.sp_kwargs = sp_kwargs or {}
|
||||||
|
|
||||||
self.torrents = [] # : List of L{Torrent} instances
|
self.torrents = [] # : List of L{Torrent} instances
|
||||||
self._rpc_methods = [] # : List of rTorrent RPC methods
|
self._rpc_methods = [] # : List of rTorrent RPC methods
|
||||||
@@ -62,12 +77,16 @@ class RTorrent:
|
|||||||
def _get_conn(self):
|
def _get_conn(self):
|
||||||
"""Get ServerProxy instance"""
|
"""Get ServerProxy instance"""
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
|
if self.schema == 'scgi':
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
return self.sp(
|
return self.sp(
|
||||||
self.url,
|
self.uri,
|
||||||
transport=BasicAuthTransport(self.username, self.password),
|
transport=BasicAuthTransport(self.username, self.password),
|
||||||
**self.sp_kwargs
|
**self.sp_kwargs
|
||||||
)
|
)
|
||||||
return self.sp(self.url, **self.sp_kwargs)
|
|
||||||
|
return self.sp(self.uri, **self.sp_kwargs)
|
||||||
|
|
||||||
def _verify_conn(self):
|
def _verify_conn(self):
|
||||||
# check for rpc methods that should be available
|
# check for rpc methods that should be available
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013 Dean Gardiner, <gardiner91@gmail.com>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
# a copy of this software and associated documentation files (the
|
||||||
|
# "Software"), to deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
# permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
# the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be
|
||||||
|
# included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from base64 import encodestring
|
||||||
|
import string
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
|
|
||||||
|
class BasicAuthTransport(xmlrpclib.Transport):
|
||||||
|
def __init__(self, username=None, password=None):
|
||||||
|
xmlrpclib.Transport.__init__(self)
|
||||||
|
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
|
||||||
|
def send_auth(self, h):
|
||||||
|
if self.username is not None and self.password is not None:
|
||||||
|
h.putheader('AUTHORIZATION', "Basic %s" % string.replace(
|
||||||
|
encodestring("%s:%s" % (self.username, self.password)),
|
||||||
|
"\012", ""
|
||||||
|
))
|
||||||
|
|
||||||
|
def single_request(self, host, handler, request_body, verbose=0):
|
||||||
|
# issue XML-RPC request
|
||||||
|
|
||||||
|
h = self.make_connection(host)
|
||||||
|
if verbose:
|
||||||
|
h.set_debuglevel(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.send_request(h, handler, request_body)
|
||||||
|
self.send_host(h, host)
|
||||||
|
self.send_user_agent(h)
|
||||||
|
self.send_auth(h)
|
||||||
|
self.send_content(h, request_body)
|
||||||
|
|
||||||
|
response = h.getresponse(buffering=True)
|
||||||
|
if response.status == 200:
|
||||||
|
self.verbose = verbose
|
||||||
|
return self.parse_response(response)
|
||||||
|
except xmlrpclib.Fault:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
#discard any response data and raise exception
|
||||||
|
if response.getheader("content-length", 0):
|
||||||
|
response.read()
|
||||||
|
raise xmlrpclib.ProtocolError(
|
||||||
|
host + handler,
|
||||||
|
response.status, response.reason,
|
||||||
|
response.msg,
|
||||||
|
)
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# rtorrent_xmlrpc
|
||||||
|
# (c) 2011 Roger Que <alerante@bellsouth.net>
|
||||||
|
#
|
||||||
|
# Python module for interacting with rtorrent's XML-RPC interface
|
||||||
|
# directly over SCGI, instead of through an HTTP server intermediary.
|
||||||
|
# Inspired by Glenn Washburn's xmlrpc2scgi.py [1], but subclasses the
|
||||||
|
# built-in xmlrpclib classes so that it is compatible with features
|
||||||
|
# such as MultiCall objects.
|
||||||
|
#
|
||||||
|
# [1] <http://libtorrent.rakshasa.no/wiki/UtilsXmlrpc2scgi>
|
||||||
|
#
|
||||||
|
# Usage: server = SCGIServerProxy('scgi://localhost:7000/')
|
||||||
|
# server = SCGIServerProxy('scgi:///path/to/scgi.sock')
|
||||||
|
# print server.system.listMethods()
|
||||||
|
# mc = xmlrpclib.MultiCall(server)
|
||||||
|
# mc.get_up_rate()
|
||||||
|
# mc.get_down_rate()
|
||||||
|
# print mc()
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# 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 2 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, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# In addition, as a special exception, the copyright holders give
|
||||||
|
# permission to link the code of portions of this program with the
|
||||||
|
# OpenSSL library under certain conditions as described in each
|
||||||
|
# individual source file, and distribute linked combinations
|
||||||
|
# including the two.
|
||||||
|
#
|
||||||
|
# You must obey the GNU General Public License in all respects for
|
||||||
|
# all of the code used other than OpenSSL. If you modify file(s)
|
||||||
|
# with this exception, you may extend this exception to your version
|
||||||
|
# of the file(s), but you are not obligated to do so. If you do not
|
||||||
|
# wish to do so, delete this exception statement from your version.
|
||||||
|
# If you delete this exception statement from all source files in the
|
||||||
|
# program, then also delete it here.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Portions based on Python's xmlrpclib:
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999-2002 by Secret Labs AB
|
||||||
|
# Copyright (c) 1999-2002 by Fredrik Lundh
|
||||||
|
#
|
||||||
|
# By obtaining, using, and/or copying this software and/or its
|
||||||
|
# associated documentation, you agree that you have read, understood,
|
||||||
|
# and will comply with the following terms and conditions:
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and distribute this software and
|
||||||
|
# its associated documentation for any purpose and without fee is
|
||||||
|
# hereby granted, provided that the above copyright notice appears in
|
||||||
|
# all copies, and that both that copyright notice and this permission
|
||||||
|
# notice appear in supporting documentation, and that the name of
|
||||||
|
# Secret Labs AB or the author not be used in advertising or publicity
|
||||||
|
# pertaining to distribution of the software without specific, written
|
||||||
|
# prior permission.
|
||||||
|
#
|
||||||
|
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||||
|
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
||||||
|
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
|
||||||
|
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
||||||
|
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import urllib
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
|
|
||||||
|
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'
|
||||||
|
header = '%d:%s' % (len(header), header)
|
||||||
|
request_body = '%s,%s' % (header, request_body)
|
||||||
|
|
||||||
|
sock = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if host:
|
||||||
|
host, port = urllib.splitport(host)
|
||||||
|
addrinfo = socket.getaddrinfo(host, port, socket.AF_INET,
|
||||||
|
socket.SOCK_STREAM)
|
||||||
|
sock = socket.socket(*addrinfo[0][:3])
|
||||||
|
sock.connect(addrinfo[0][4])
|
||||||
|
else:
|
||||||
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
sock.connect(handler)
|
||||||
|
|
||||||
|
self.verbose = verbose
|
||||||
|
|
||||||
|
sock.send(request_body)
|
||||||
|
return self.parse_response(sock.makefile())
|
||||||
|
finally:
|
||||||
|
if sock:
|
||||||
|
sock.close()
|
||||||
|
|
||||||
|
def parse_response(self, response):
|
||||||
|
p, u = self.getparser()
|
||||||
|
|
||||||
|
response_body = ''
|
||||||
|
while True:
|
||||||
|
data = response.read(1024)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
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)
|
||||||
|
|
||||||
|
p.feed(response_body)
|
||||||
|
p.close()
|
||||||
|
|
||||||
|
return u.close()
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
if type not in ('scgi'):
|
||||||
|
raise IOError('unsupported XML-RPC protocol')
|
||||||
|
self.__host, self.__handler = urllib.splithost(uri)
|
||||||
|
if not self.__handler:
|
||||||
|
self.__handler = '/'
|
||||||
|
|
||||||
|
if transport is None:
|
||||||
|
transport = SCGITransport(use_datetime=use_datetime)
|
||||||
|
self.__transport = transport
|
||||||
|
|
||||||
|
self.__encoding = encoding
|
||||||
|
self.__verbose = verbose
|
||||||
|
self.__allow_none = allow_none
|
||||||
|
|
||||||
|
def __close(self):
|
||||||
|
self.__transport.close()
|
||||||
|
|
||||||
|
def __request(self, methodname, params):
|
||||||
|
# call a method on the remote server
|
||||||
|
|
||||||
|
request = xmlrpclib.dumps(params, methodname, encoding=self.__encoding,
|
||||||
|
allow_none=self.__allow_none)
|
||||||
|
|
||||||
|
response = self.__transport.request(
|
||||||
|
self.__host,
|
||||||
|
self.__handler,
|
||||||
|
request,
|
||||||
|
verbose=self.__verbose
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(response) == 1:
|
||||||
|
response = response[0]
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
"<SCGIServerProxy for %s%s>" %
|
||||||
|
(self.__host, self.__handler)
|
||||||
|
)
|
||||||
|
|
||||||
|
__str__ = __repr__
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
# magic method dispatcher
|
||||||
|
return xmlrpclib._Method(self.__request, name)
|
||||||
|
|
||||||
|
# note: to call a remote object with an non-standard name, use
|
||||||
|
# result getattr(server, "strange-python-name")(args)
|
||||||
|
|
||||||
|
def __call__(self, attr):
|
||||||
|
"""A workaround to get special attributes on the ServerProxy
|
||||||
|
without interfering with the magic __getattr__
|
||||||
|
"""
|
||||||
|
if attr == "close":
|
||||||
|
return self.__close
|
||||||
|
elif attr == "transport":
|
||||||
|
return self.__transport
|
||||||
|
raise AttributeError("Attribute %r not found" % (attr,))
|
||||||
@@ -17,66 +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.
|
||||||
from base64 import encodestring
|
|
||||||
import httplib
|
|
||||||
import inspect
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
import inspect
|
||||||
import rtorrent
|
import rtorrent
|
||||||
import re
|
import re
|
||||||
from rtorrent.common import bool_to_int, convert_version_tuple_to_str,\
|
from rtorrent.common import bool_to_int, convert_version_tuple_to_str,\
|
||||||
safe_repr
|
safe_repr
|
||||||
from rtorrent.err import RTorrentVersionError, MethodError
|
from rtorrent.err import MethodError
|
||||||
from rtorrent.compat import xmlrpclib
|
from rtorrent.compat import xmlrpclib
|
||||||
|
|
||||||
|
|
||||||
class BasicAuthTransport(xmlrpclib.Transport):
|
|
||||||
def __init__(self, username=None, password=None):
|
|
||||||
xmlrpclib.Transport.__init__(self)
|
|
||||||
self.username = username
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
def send_auth(self, h):
|
|
||||||
if self.username is not None and self.password is not None:
|
|
||||||
h.putheader('AUTHORIZATION', "Basic %s" % string.replace(
|
|
||||||
encodestring("%s:%s" % (self.username, self.password)),
|
|
||||||
"\012", ""
|
|
||||||
))
|
|
||||||
|
|
||||||
def single_request(self, host, handler, request_body, verbose=0):
|
|
||||||
# issue XML-RPC request
|
|
||||||
|
|
||||||
h = self.make_connection(host)
|
|
||||||
if verbose:
|
|
||||||
h.set_debuglevel(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.send_request(h, handler, request_body)
|
|
||||||
self.send_host(h, host)
|
|
||||||
self.send_user_agent(h)
|
|
||||||
self.send_auth(h)
|
|
||||||
self.send_content(h, request_body)
|
|
||||||
|
|
||||||
response = h.getresponse(buffering=True)
|
|
||||||
if response.status == 200:
|
|
||||||
self.verbose = verbose
|
|
||||||
return self.parse_response(response)
|
|
||||||
except xmlrpclib.Fault:
|
|
||||||
raise
|
|
||||||
except Exception:
|
|
||||||
self.close()
|
|
||||||
raise
|
|
||||||
|
|
||||||
#discard any response data and raise exception
|
|
||||||
if (response.getheader("content-length", 0)):
|
|
||||||
response.read()
|
|
||||||
raise xmlrpclib.ProtocolError(
|
|
||||||
host + handler,
|
|
||||||
response.status, response.reason,
|
|
||||||
response.msg,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_varname(rpc_call):
|
def get_varname(rpc_call):
|
||||||
"""Transform rpc method into variable name.
|
"""Transform rpc method into variable name.
|
||||||
|
|
||||||
|
|||||||
@@ -172,6 +172,17 @@ class Torrent:
|
|||||||
|
|
||||||
self.directory = m.call()[-1]
|
self.directory = m.call()[-1]
|
||||||
|
|
||||||
|
def set_directory_base(self, d):
|
||||||
|
"""Modify base download directory
|
||||||
|
|
||||||
|
@note: Needs to stop torrent in order to change the directory.
|
||||||
|
Also doesn't restart after directory is set, that must be called
|
||||||
|
separately.
|
||||||
|
"""
|
||||||
|
m = rtorrent.rpc.Multicall(self)
|
||||||
|
self.multicall_add(m, "d.try_stop")
|
||||||
|
self.multicall_add(m, "d.set_directory_base", d)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the torrent"""
|
"""Start the torrent"""
|
||||||
m = rtorrent.rpc.Multicall(self)
|
m = rtorrent.rpc.Multicall(self)
|
||||||
|
|||||||
Reference in new issue
Block a user