You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.0 KiB
136 lines
4.0 KiB
12 years ago
|
import os
|
||
|
|
||
|
from collections import defaultdict
|
||
|
from itertools import imap
|
||
|
|
||
12 years ago
|
from .synchronousdeluge.exceptions import DelugeRPCError
|
||
|
from .synchronousdeluge.protocol import DelugeRPCRequest, DelugeRPCResponse
|
||
|
from .synchronousdeluge.transfer import DelugeTransfer
|
||
12 years ago
|
|
||
|
__all__ = ["DelugeClient"]
|
||
|
|
||
|
|
||
|
RPC_RESPONSE = 1
|
||
|
RPC_ERROR = 2
|
||
|
RPC_EVENT = 3
|
||
|
|
||
|
|
||
|
class DelugeClient(object):
|
||
|
def __init__(self):
|
||
|
"""A deluge client session."""
|
||
|
self.transfer = DelugeTransfer()
|
||
|
self.modules = []
|
||
|
self._request_counter = 0
|
||
|
|
||
|
def _get_local_auth(self):
|
||
|
xdg_config = os.path.expanduser(os.environ.get("XDG_CONFIG_HOME", "~/.config"))
|
||
|
config_home = os.path.join(xdg_config, "deluge")
|
||
|
auth_file = os.path.join(config_home, "auth")
|
||
|
|
||
|
username = password = ""
|
||
|
with open(auth_file) as fd:
|
||
|
for line in fd:
|
||
|
if line.startswith("#"):
|
||
|
continue
|
||
|
|
||
|
auth = line.split(":")
|
||
|
if len(auth) >= 2 and auth[0] == "localclient":
|
||
|
username, password = auth[0], auth[1]
|
||
|
break
|
||
|
|
||
|
return username, password
|
||
|
|
||
|
def _create_module_method(self, module, method):
|
||
|
fullname = "{0}.{1}".format(module, method)
|
||
|
|
||
|
def func(obj, *args, **kwargs):
|
||
|
return self.remote_call(fullname, *args, **kwargs)
|
||
|
|
||
|
func.__name__ = method
|
||
|
|
||
|
return func
|
||
|
|
||
|
def _introspect(self):
|
||
|
self.modules = []
|
||
|
|
||
|
methods = self.remote_call("daemon.get_method_list").get()
|
||
|
methodmap = defaultdict(dict)
|
||
|
splitter = lambda v: v.split(".")
|
||
|
|
||
|
for module, method in imap(splitter, methods):
|
||
|
methodmap[module][method] = self._create_module_method(module, method)
|
||
|
|
||
|
for module, methods in methodmap.items():
|
||
|
clsname = "DelugeModule{0}".format(module.capitalize())
|
||
|
cls = type(clsname, (), methods)
|
||
|
setattr(self, module, cls())
|
||
|
self.modules.append(module)
|
||
|
|
||
|
def remote_call(self, method, *args, **kwargs):
|
||
|
req = DelugeRPCRequest(self._request_counter, method, *args, **kwargs)
|
||
|
message = next(self.transfer.send_request(req))
|
||
|
|
||
|
response = DelugeRPCResponse()
|
||
|
|
||
|
if not isinstance(message, tuple):
|
||
|
return
|
||
|
|
||
|
if len(message) < 3:
|
||
|
return
|
||
|
|
||
|
message_type = message[0]
|
||
|
|
||
|
# if message_type == RPC_EVENT:
|
||
|
# event = message[1]
|
||
|
# values = message[2]
|
||
|
#
|
||
|
# if event in self._event_handlers:
|
||
|
# for handler in self._event_handlers[event]:
|
||
|
# gevent.spawn(handler, *values)
|
||
|
#
|
||
|
# elif message_type in (RPC_RESPONSE, RPC_ERROR):
|
||
|
if message_type in (RPC_RESPONSE, RPC_ERROR):
|
||
|
request_id = message[1]
|
||
|
value = message[2]
|
||
|
|
||
|
if request_id == self._request_counter :
|
||
|
if message_type == RPC_RESPONSE:
|
||
|
response.set(value)
|
||
|
elif message_type == RPC_ERROR:
|
||
|
err = DelugeRPCError(*value)
|
||
|
response.set_exception(err)
|
||
|
|
||
|
self._request_counter += 1
|
||
|
return response
|
||
|
|
||
|
def connect(self, host="127.0.0.1", port=58846, username="", password=""):
|
||
|
"""Connects to a daemon process.
|
||
|
|
||
|
:param host: str, the hostname of the daemon
|
||
|
:param port: int, the port of the daemon
|
||
|
:param username: str, the username to login with
|
||
|
:param password: str, the password to login with
|
||
|
"""
|
||
|
|
||
|
# Connect transport
|
||
|
self.transfer.connect((host, port))
|
||
|
|
||
|
# Attempt to fetch local auth info if needed
|
||
|
if not username and host in ("127.0.0.1", "localhost"):
|
||
|
username, password = self._get_local_auth()
|
||
|
|
||
|
# Authenticate
|
||
|
self.remote_call("daemon.login", username, password).get()
|
||
|
|
||
|
# Introspect available methods
|
||
|
self._introspect()
|
||
|
|
||
|
@property
|
||
|
def connected(self):
|
||
|
return self.transfer.connected
|
||
|
|
||
|
def disconnect(self):
|
||
|
"""Disconnects from the daemon."""
|
||
|
self.transfer.disconnect()
|
||
|
|