/libs/synchronousdeluge/client.py
https://gitlab.com/132nd-etcher/CouchPotatoServer · Python · 162 lines · 101 code · 38 blank · 23 comment · 24 complexity · 0d7e49e170618916404c85952a575561 MD5 · raw file
- import os
- import platform
- from collections import defaultdict
- from itertools import imap
- from synchronousdeluge.exceptions import DelugeRPCError
- from synchronousdeluge.protocol import DelugeRPCRequest, DelugeRPCResponse
- from synchronousdeluge.transfer import DelugeTransfer
- __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):
- auth_file = ""
- username = password = ""
- if platform.system() in ('Windows', 'Microsoft'):
- appDataPath = os.environ.get("APPDATA")
- if not appDataPath:
- import _winreg
- hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")
- appDataReg = _winreg.QueryValueEx(hkey, "AppData")
- appDataPath = appDataReg[0]
- _winreg.CloseKey(hkey)
- auth_file = os.path.join(appDataPath, "deluge", "auth")
- else:
- from xdg.BaseDirectory import save_config_path
- try:
- auth_file = os.path.join(save_config_path("deluge"), "auth")
- except OSError, e:
- return username, password
- if os.path.exists(auth_file):
- for line in open(auth_file):
- if line.startswith("#"):
- # This is a comment line
- continue
- line = line.strip()
- try:
- lsplit = line.split(":")
- except Exception, e:
- continue
- if len(lsplit) == 2:
- username, password = lsplit
- elif len(lsplit) == 3:
- username, password, level = lsplit
- else:
- continue
- if username == "localclient":
- return (username, password)
- return ("", "")
- 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()