diff options
Diffstat (limited to 'module')
| -rw-r--r-- | module/Api.py | 236 | ||||
| -rw-r--r-- | module/api/ApiComponent.py | 12 | ||||
| -rw-r--r-- | module/api/ConfigApi.py | 104 | ||||
| -rw-r--r-- | module/api/CoreApi.py | 121 | ||||
| -rw-r--r-- | module/api/__init__.py | 1 | ||||
| -rw-r--r-- | module/config/ConfigManager.py | 6 | ||||
| -rw-r--r-- | module/datatypes/User.py | 7 | 
7 files changed, 274 insertions, 213 deletions
| diff --git a/module/Api.py b/module/Api.py index dc500dabe..9a92da0ec 100644 --- a/module/Api.py +++ b/module/Api.py @@ -20,7 +20,7 @@ import re  from os.path import join, isabs  from itertools import chain  from functools import partial -from new import code +from types import MethodType, CodeType  from dis import opmap  from remote.ttypes import * @@ -70,7 +70,7 @@ class UserContext(object):          # load argument instead of global          new_code = new_code.replace(chr(opmap['LOAD_GLOBAL']) + chr(i), chr(opmap['LOAD_FAST']) + chr(fc.co_argcount)) -        new_fc = code(fc.co_argcount + 1, fc.co_nlocals + 1, fc.co_stacksize, fc.co_flags, new_code, fc.co_consts, +        new_fc = CodeType(fc.co_argcount + 1, fc.co_nlocals + 1, fc.co_stacksize, fc.co_flags, new_code, fc.co_consts,              new_names, new_varnames, fc.co_filename, fc.co_name, fc.co_firstlineno, fc.co_lnotab, fc.co_freevars,              fc.co_cellvars) @@ -138,11 +138,37 @@ class Api(Iface):      """      EXTERNAL = Iface  # let the json api know which methods are external +    EXTEND = False  # only extendable when set too true      def __init__(self, core):          self.core = core          self.user_apis = {} +    @classmethod +    def initComponents(cls): +        # Allow extending the api +        # This prevents unintentionally registering of the components, +        # but will only work once when they are imported +        cls.EXTEND = True +        # Import all Api modules, they register themselves. +        from module.api import * +        # they will vanish from the namespace afterwards + + +    @classmethod +    def extend(cls, api): +        """Takes all params from api and extends cls with it. +            api class can be removed afterwards + +        :param api: Class with methods to extend +        """ +        if cls.EXTEND: +            for name, func in api.__dict__.iteritems(): +                if name.startswith("_"): continue +                setattr(cls, name, MethodType(func, None, cls)) + +        return cls.EXTEND +      def withUserContext(self, uid):          """ Returns a proxy version of the api, to call method in user context @@ -162,210 +188,6 @@ class Api(Iface):          return self.user_apis[uid] -    ########################## -    #  Server Status -    ########################## - -    @RequirePerm(Permission.All) -    def getServerVersion(self): -        """pyLoad Core version """ -        return self.core.version - -    @RequirePerm(Permission.All) -    def getWSAddress(self): -        """Gets and address for the websocket based on configuration""" -        # TODO - -    @RequirePerm(Permission.All) -    def getServerStatus(self): -        """Some general information about the current status of pyLoad. - -        :return: `ServerStatus` -        """ -        serverStatus = ServerStatus(self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, -            not self.core.threadManager.pause and self.isTimeDownload(), self.core.threadManager.pause, -            self.core.config['reconnect']['activated'] and self.isTimeReconnect()) - -        for pyfile in self.core.threadManager.getActiveDownloads(): -            serverStatus.speed += pyfile.getSpeed() #bytes/s - -        return serverStatus - -    @RequirePerm(Permission.All) -    def getProgressInfo(self): -        """ Status of all currently running tasks - -        :return: list of `ProgressInfo` -        """ -        pass - -    def pauseServer(self): -        """Pause server: It won't start any new downloads, but nothing gets aborted.""" -        self.core.threadManager.pause = True - -    def unpauseServer(self): -        """Unpause server: New Downloads will be started.""" -        self.core.threadManager.pause = False - -    def togglePause(self): -        """Toggle pause state. - -        :return: new pause state -        """ -        self.core.threadManager.pause ^= True -        return self.core.threadManager.pause - -    def toggleReconnect(self): -        """Toggle reconnect activation. - -        :return: new reconnect state -        """ -        self.core.config["reconnect"]["activated"] ^= True -        return self.core.config["reconnect"]["activated"] - -    def freeSpace(self): -        """Available free space at download directory in bytes""" -        return free_space(self.core.config["general"]["download_folder"]) - - -    def quit(self): -        """Clean way to quit pyLoad""" -        self.core.do_kill = True - -    def restart(self): -        """Restart pyload core""" -        self.core.do_restart = True - -    def getLog(self, offset=0): -        """Returns most recent log entries. - -        :param offset: line offset -        :return: List of log entries -        """ -        filename = join(self.core.config['log']['log_folder'], 'log.txt') -        try: -            fh = open(filename, "r") -            lines = fh.readlines() -            fh.close() -            if offset >= len(lines): -                return [] -            return lines[offset:] -        except: -            return ['No log available'] - -    @RequirePerm(Permission.All) -    def isTimeDownload(self): -        """Checks if pyload will start new downloads according to time in config. - -        :return: bool -        """ -        start = self.core.config['downloadTime']['start'].split(":") -        end = self.core.config['downloadTime']['end'].split(":") -        return compare_time(start, end) - -    @RequirePerm(Permission.All) -    def isTimeReconnect(self): -        """Checks if pyload will try to make a reconnect - -        :return: bool -        """ -        start = self.core.config['reconnect']['startTime'].split(":") -        end = self.core.config['reconnect']['endTime'].split(":") -        return compare_time(start, end) and self.core.config["reconnect"]["activated"] - -    ########################## -    #  Configuration -    ########################## - -    def getConfigValue(self, section, option): -        """Retrieve config value. - -        :param section: name of category, or plugin -        :param option: config option -        :return: config value as string -        """ -        value = self.core.config.get(section, option) -        return to_string(value) - -    def setConfigValue(self, section, option, value): -        """Set new config value. - -        :param section: -        :param option: -        :param value: new config value -        """ -        if option in ("limit_speed", "max_speed"): #not so nice to update the limit -            self.core.requestFactory.updateBucket() - -        self.core.config.set(section, option, value) - -    def getConfig(self): -        """Retrieves complete config of core. - -        :rtype : ConfigHolder -        :return: dict with section mapped to config -        """ -        # TODO -        return dict([(section, ConfigHolder(section, data.name, data.description, data.long_desc, [ -        ConfigItem(option, d.name, d.description, d.type, to_string(d.default), -            to_string(self.core.config.get(section, option))) for -        option, d in data.config.iteritems()])) for -                                                section, data in self.core.config.getBaseSections()]) - - -    def getConfigRef(self): -        """Config instance, not for RPC""" -        return self.core.config - -    def getGlobalPlugins(self): -        """All global plugins/addons, only admin can use this - -        :return: list of `ConfigInfo` -        """ -        pass - -    @UserContext -    @RequirePerm(Permission.Plugins) -    def getUserPlugins(self): -        """List of plugins every user can configure for himself - -        :return: list of `ConfigInfo` -        """ -        pass - -    @UserContext -    @RequirePerm(Permission.Plugins) -    def configurePlugin(self, plugin): -        """Get complete config options for an plugin - -        :param plugin: Name of the plugin to configure -        :return: :class:`ConfigHolder` -        """ - -        pass - -    @UserContext -    @RequirePerm(Permission.Plugins) -    def saveConfig(self, config): -        """Used to save a configuration, core config can only be saved by admins - -        :param config: :class:`ConfigHolder -        """ -        pass - -    @UserContext -    @RequirePerm(Permission.Plugins) -    def deleteConfig(self, plugin): -        """Deletes modified config - -        :param plugin: plugin name -        :return: -        """ -        pass - -    @RequirePerm(Permission.Plugins) -    def setConfigHandler(self, plugin, iid, value): -        pass      ##########################      #  Download Preparing @@ -1054,4 +876,4 @@ class Api(Iface):          :param plugin: pluginname          :return: dict of attr names mapped to value {"name": value}          """ -        return self.core.addonManager.getInfo(plugin) +        return self.core.addonManager.getInfo(plugin)
\ No newline at end of file diff --git a/module/api/ApiComponent.py b/module/api/ApiComponent.py new file mode 100644 index 000000000..2b09d05a3 --- /dev/null +++ b/module/api/ApiComponent.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +class ApiComponent: + +    def __init__(self, core): +        # Only for auto completion, this class can not be instantiated +        from pyload import Core +        assert isinstance(core, Core) +        self.core = core +        # No instantiating! +        raise Exception()
\ No newline at end of file diff --git a/module/api/ConfigApi.py b/module/api/ConfigApi.py new file mode 100644 index 000000000..f3f2e6950 --- /dev/null +++ b/module/api/ConfigApi.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.Api import Api, UserContext, RequirePerm, Permission, ConfigHolder, ConfigItem +from module.utils import to_string + +from ApiComponent import ApiComponent + +class ConfigApi(ApiComponent): +    """ Everything related to configuration """ + +    def getConfigValue(self, section, option): +        """Retrieve config value. + +        :param section: name of category, or plugin +        :param option: config option +        :rtype: str +        :return: config value as string +        """ +        value = self.core.config.get(section, option) +        return to_string(value) + +    def setConfigValue(self, section, option, value): +        """Set new config value. + +        :param section: +        :param option: +        :param value: new config value +        """ +        if option in ("limit_speed", "max_speed"): #not so nice to update the limit +            self.core.requestFactory.updateBucket() + +        self.core.config.set(section, option, value) + +    def getConfig(self): +        """Retrieves complete config of core. + +        :rtype: ConfigHolder +        :return: dict with section mapped to config +        """ +        # TODO +        return dict([(section, ConfigHolder(section, data.name, data.description, data.long_desc, [ +        ConfigItem(option, d.name, d.description, d.type, to_string(d.default), +            to_string(self.core.config.get(section, option))) for +        option, d in data.config.iteritems()])) for +                     section, data in self.core.config.getBaseSections()]) + + +    def getConfigRef(self): +        """Config instance, not for RPC""" +        return self.core.config + +    def getGlobalPlugins(self): +        """All global plugins/addons, only admin can use this + +        :return: list of `ConfigInfo` +        """ +        pass + +    @UserContext +    @RequirePerm(Permission.Plugins) +    def getUserPlugins(self): +        """List of plugins every user can configure for himself + +        :return: list of `ConfigInfo` +        """ +        pass + +    @UserContext +    @RequirePerm(Permission.Plugins) +    def configurePlugin(self, plugin): +        """Get complete config options for an plugin + +        :param plugin: Name of the plugin to configure +        :return: :class:`ConfigHolder` +        """ + +        pass + +    @UserContext +    @RequirePerm(Permission.Plugins) +    def saveConfig(self, config): +        """Used to save a configuration, core config can only be saved by admins + +        :param config: :class:`ConfigHolder +        """ +        pass + +    @UserContext +    @RequirePerm(Permission.Plugins) +    def deleteConfig(self, plugin): +        """Deletes modified config + +        :param plugin: plugin name +        :return: +        """ +        pass + +    @RequirePerm(Permission.Plugins) +    def setConfigHandler(self, plugin, iid, value): +        pass + +if Api.extend(ConfigApi): +    del ConfigApi
\ No newline at end of file diff --git a/module/api/CoreApi.py b/module/api/CoreApi.py new file mode 100644 index 000000000..4de8c1f96 --- /dev/null +++ b/module/api/CoreApi.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from module.Api import Api, RequirePerm, Permission, ServerStatus +from module.utils.fs import join, free_space +from module.utils import compare_time + +from ApiComponent import ApiComponent + +class CoreApi(ApiComponent): +    """ This module provides methods for general interaction with the core, like status or progress retrieval  """ + +    @RequirePerm(Permission.All) +    def getServerVersion(self): +        """pyLoad Core version """ +        return self.core.version + +    @RequirePerm(Permission.All) +    def getWSAddress(self): +        """Gets and address for the websocket based on configuration""" +        # TODO + +    @RequirePerm(Permission.All) +    def getServerStatus(self): +        """Some general information about the current status of pyLoad. + +        :return: `ServerStatus` +        """ +        serverStatus = ServerStatus(self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, +            not self.core.threadManager.pause and self.isTimeDownload(), self.core.threadManager.pause, +            self.core.config['reconnect']['activated'] and self.isTimeReconnect()) + +        for pyfile in self.core.threadManager.getActiveDownloads(): +            serverStatus.speed += pyfile.getSpeed() #bytes/s + +        return serverStatus + +    @RequirePerm(Permission.All) +    def getProgressInfo(self): +        """ Status of all currently running tasks + +        :rtype: list of :class:`ProgressInfo` +        """ +        pass + +    def pauseServer(self): +        """Pause server: It won't start any new downloads, but nothing gets aborted.""" +        self.core.threadManager.pause = True + +    def unpauseServer(self): +        """Unpause server: New Downloads will be started.""" +        self.core.threadManager.pause = False + +    def togglePause(self): +        """Toggle pause state. + +        :return: new pause state +        """ +        self.core.threadManager.pause ^= True +        return self.core.threadManager.pause + +    def toggleReconnect(self): +        """Toggle reconnect activation. + +        :return: new reconnect state +        """ +        self.core.config["reconnect"]["activated"] ^= True +        return self.core.config["reconnect"]["activated"] + +    def freeSpace(self): +        """Available free space at download directory in bytes""" +        return free_space(self.core.config["general"]["download_folder"]) + + +    def quit(self): +        """Clean way to quit pyLoad""" +        self.core.do_kill = True + +    def restart(self): +        """Restart pyload core""" +        self.core.do_restart = True + +    def getLog(self, offset=0): +        """Returns most recent log entries. + +        :param offset: line offset +        :return: List of log entries +        """ +        filename = join(self.core.config['log']['log_folder'], 'log.txt') +        try: +            fh = open(filename, "r") +            lines = fh.readlines() +            fh.close() +            if offset >= len(lines): +                return [] +            return lines[offset:] +        except: +            return ['No log available'] + +    @RequirePerm(Permission.All) +    def isTimeDownload(self): +        """Checks if pyload will start new downloads according to time in config. + +        :return: bool +        """ +        start = self.core.config['downloadTime']['start'].split(":") +        end = self.core.config['downloadTime']['end'].split(":") +        return compare_time(start, end) + +    @RequirePerm(Permission.All) +    def isTimeReconnect(self): +        """Checks if pyload will try to make a reconnect + +        :return: bool +        """ +        start = self.core.config['reconnect']['startTime'].split(":") +        end = self.core.config['reconnect']['endTime'].split(":") +        return compare_time(start, end) and self.core.config["reconnect"]["activated"] + +if Api.extend(CoreApi): +    del CoreApi
\ No newline at end of file diff --git a/module/api/__init__.py b/module/api/__init__.py new file mode 100644 index 000000000..f7ceb6183 --- /dev/null +++ b/module/api/__init__.py @@ -0,0 +1 @@ +__all__ = ["CoreApi", "ConfigApi"]
\ No newline at end of file diff --git a/module/config/ConfigManager.py b/module/config/ConfigManager.py index 872ce2e00..4898b0c66 100644 --- a/module/config/ConfigManager.py +++ b/module/config/ConfigManager.py @@ -57,7 +57,7 @@ class ConfigManager(ConfigParser):          else:              # We need the id and not the instance              # Will be None for admin user and so the same as internal access -            user = user.handle if user else None +            user = user.primary if user else None              try:                  # Check if this config exists                  # Configs without meta data can not be loaded! @@ -87,7 +87,7 @@ class ConfigManager(ConfigParser):              changed = self.parser.set(section, option, value, sync)          else:              # associated id -            user = user.handle if user else None +            user = user.primary if user else None              data = self.config[section].config[option]              value = from_string(value, data.type)              old_value = self.get(section, option) @@ -107,7 +107,7 @@ class ConfigManager(ConfigParser):      def delete(self, section, user=False):          """ Deletes values saved in db and cached values for given user, NOT meta data              Does not trigger an error when nothing was deleted. """ -        user = user.handle if user else None +        user = user.primary if user else None          if (user, section) in self.values:              del self.values[user, section] diff --git a/module/datatypes/User.py b/module/datatypes/User.py index 3832653c7..bdf52d860 100644 --- a/module/datatypes/User.py +++ b/module/datatypes/User.py @@ -55,8 +55,9 @@ class User(UserData):          return self.hasRole(Role.Admin)      @property -    def handle(self): -        """ Internal user handle used for most operations (secondary share handle with primary user)  """ +    def primary(self): +        """ Primary user id, Internal user handle used for most operations +        Secondary user account share id with primary user. Only Admins have no primary id. """          if self.hasRole(Role.Admin):              return None -        return self.user if self.user else self.uid +        return self.user if self.user else self.uid
\ No newline at end of file | 
