diff options
Diffstat (limited to 'pyload/manager/Addon.py')
| -rw-r--r-- | pyload/manager/Addon.py | 303 | 
1 files changed, 303 insertions, 0 deletions
| diff --git a/pyload/manager/Addon.py b/pyload/manager/Addon.py new file mode 100644 index 000000000..2a3bc4318 --- /dev/null +++ b/pyload/manager/Addon.py @@ -0,0 +1,303 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN, mkaay +# @interface-version: 0.1 + +import __builtin__ + +import traceback +from threading import RLock, Thread + +from types import MethodType + +from pyload.manager.thread.Addon import AddonThread +from pyload.manager.Plugin import literal_eval +from pyload.utils import lock + + +class AddonManager(object): +    """Manages addons, delegates and handles Events. + +        Every plugin can define events, \ +        but some very usefull events are called by the Core. +        Contrary to overwriting addon methods you can use event listener, +        which provides additional entry point in the control flow. +        Only do very short tasks or use threads. + +        **Known Events:** +        Most addon methods exists as events. These are the additional known events. + +        ======================= ============== ================================== +        Name                     Arguments      Description +        ======================= ============== ================================== +        download-preparing      fid            A download was just queued and will be prepared now. +        download-start          fid            A plugin will immediately starts the download afterwards. +        links-added             links, pid     Someone just added links, you are able to modify the links. +        all_downloads-processed                Every link was handled, pyload would idle afterwards. +        all_downloads-finished                 Every download in queue is finished. +        config-changed                          The config was changed via the api. +        pluginConfigChanged                    The plugin config changed, due to api or internal process. +        ======================= ============== ================================== + +        | Notes: +        |    all_downloads-processed is *always* called before all_downloads-finished. +        |    config-changed is *always* called before pluginConfigChanged. +    """ + +    def __init__(self, core): +        self.core = core + +        __builtin__.addonManager = self  #: needed to let addons register themself + +        self.plugins = [] +        self.pluginMap = {} +        self.methods = {}  #: dict of names and list of methods usable by rpc + +        self.events = {}  #: contains events + +        # registering callback for config event +        self.core.config.pluginCB = MethodType(self.dispatchEvent, "pluginConfigChanged", basestring)  #@TODO: Rename event pluginConfigChanged + +        self.addEvent("pluginConfigChanged", self.manageAddon) + +        self.lock = RLock() +        self.createIndex() + + +    def try_catch(func): + + +        def new(*args): +            try: +                return func(*args) +            except Exception, e: +                args[0].core.log.error(_("Error executing addon: %s") % e) +                if args[0].core.debug: +                    traceback.print_exc() + +        return new + + +    def addRPC(self, plugin, func, doc): +        plugin = plugin.rpartition(".")[2] +        doc = doc.strip() if doc else "" + +        if plugin in self.methods: +            self.methods[plugin][func] = doc +        else: +            self.methods[plugin] = {func: doc} + + +    def callRPC(self, plugin, func, args, parse): +        if not args: +            args = () +        if parse: +            args = tuple([literal_eval(x) for x in args]) +        plugin = self.pluginMap[plugin] +        f = getattr(plugin, func) +        return f(*args) + + +    def createIndex(self): +        plugins  = [] + +        for type in ("addon", "hook"): +            active   = [] +            deactive = [] +            for pluginname in getattr(self.core.pluginManager, "%sPlugins" % type): +                try: +                    if self.core.config.getPlugin("%s_%s" % (pluginname, type), "activated"): +                        pluginClass = self.core.pluginManager.loadClass(type, pluginname) +                        if not pluginClass: +                            continue + +                        plugin = pluginClass(self.core, self) +                        plugins.append(plugin) +                        self.pluginMap[pluginClass.__name__] = plugin +                        if plugin.isActivated(): +                            active.append(pluginClass.__name__) +                    else: +                        deactive.append(pluginname) + +                except Exception: +                    self.core.log.warning(_("Failed activating %(name)s") % {"name": pluginname}) +                    if self.core.debug: +                        traceback.print_exc() + +            self.core.log.info(_("Activated %ss: %s") % (type, ", ".join(sorted(active)))) +            self.core.log.info(_("Deactivated %ss: %s") % (type, ", ".join(sorted(deactive)))) + +        self.plugins = plugins + + +    def manageAddon(self, plugin, name, value): +        if name == "activated" and value: +            self.activateAddon(plugin) + +        elif name == "activated" and not value: +            self.deactivateAddon(plugin) + + +    def activateAddon(self, pluginname): +        # check if already loaded +        for inst in self.plugins: +            if inst.__class__.__name__ == pluginname: +                return + +        pluginClass = self.core.pluginManager.loadClass("addon", pluginname) + +        if not pluginClass: +            return + +        self.core.log.debug("Activate addon: %s" % pluginname) + +        addon = pluginClass(self.core, self) +        self.plugins.append(addon) +        self.pluginMap[pluginClass.__name__] = addon + +        addon.activate() + + +    def deactivateAddon(self, pluginname): +        for plugin in self.plugins: +            if plugin.__class__.__name__ == pluginname: +                addon = plugin +                break +        else: +            return + +        self.core.log.debug("Deactivate addon: %s" % pluginname) + +        addon.deactivate() + +        # remove periodic call +        self.core.log.debug("Removed callback: %s" % self.core.scheduler.removeJob(addon.cb)) + +        self.plugins.remove(addon) +        del self.pluginMap[addon.__class__.__name__] + + +    @try_catch +    def coreReady(self): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.activate() + +        self.dispatchEvent("addon-start") + + +    @try_catch +    def coreExiting(self): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.exit() + +        self.dispatchEvent("addon-exit") + + +    @lock +    def downloadPreparing(self, pyfile): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.downloadPreparing(pyfile) + +        self.dispatchEvent("download-preparing", pyfile) + + +    @lock +    def downloadFinished(self, pyfile): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.downloadFinished(pyfile) + +        self.dispatchEvent("download-finished", pyfile) + + +    @lock +    @try_catch +    def downloadFailed(self, pyfile): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.downloadFailed(pyfile) + +        self.dispatchEvent("download-failed", pyfile) + + +    @lock +    def packageFinished(self, package): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.packageFinished(package) + +        self.dispatchEvent("package-finished", package) + + +    @lock +    def beforeReconnecting(self, ip): +        for plugin in self.plugins: +            plugin.beforeReconnecting(ip) + +        self.dispatchEvent("beforeReconnecting", ip) + + +    @lock +    def afterReconnecting(self, ip): +        for plugin in self.plugins: +            if plugin.isActivated(): +                plugin.afterReconnecting(ip) + +        self.dispatchEvent("afterReconnecting", ip) + + +    def startThread(self, function, *args, **kwargs): +        return AddonThread(self.core.threadManager, function, args, kwargs) + + +    def activePlugins(self): +        """ returns all active plugins """ +        return [x for x in self.plugins if x.isActivated()] + + +    def getAllInfo(self): +        """returns info stored by addon plugins""" +        info = {} +        for name, plugin in self.pluginMap.iteritems(): +            if plugin.info: +                # copy and convert so str +                info[name] = dict( +                    [(x, str(y) if not isinstance(y, basestring) else y) for x, y in plugin.info.iteritems()]) +        return info + + +    def getInfo(self, plugin): +        info = {} +        if plugin in self.pluginMap and self.pluginMap[plugin].info: +            info = dict((x, str(y) if not isinstance(y, basestring) else y) +                         for x, y in self.pluginMap[plugin].info.iteritems()) +        return info + + +    def addEvent(self, event, func): +        """Adds an event listener for event name""" +        if event in self.events: +            self.events[event].append(func) +        else: +            self.events[event] = [func] + + +    def removeEvent(self, event, func): +        """removes previously added event listener""" +        if event in self.events: +            self.events[event].remove(func) + + +    def dispatchEvent(self, event, *args): +        """dispatches event with args""" +        if event in self.events: +            for f in self.events[event]: +                try: +                    f(*args) +                except Exception, e: +                    self.core.log.warning("Error calling event handler %s: %s, %s, %s" +                                          % (event, f, args, str(e))) +                    if self.core.debug: +                        traceback.print_exc() | 
