summaryrefslogtreecommitdiffstats
path: root/pyload/PluginManager.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/PluginManager.py')
-rw-r--r--pyload/PluginManager.py258
1 files changed, 258 insertions, 0 deletions
diff --git a/pyload/PluginManager.py b/pyload/PluginManager.py
new file mode 100644
index 000000000..2e3c66e03
--- /dev/null
+++ b/pyload/PluginManager.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+
+###############################################################################
+# Copyright(c) 2008-2013 pyLoad Team
+# http://www.pyload.org
+#
+# This file is part of pyLoad.
+# pyLoad is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# Subjected to the terms and conditions in LICENSE
+#
+# @author: RaNaN, mkaay
+###############################################################################
+
+import sys
+
+from os.path import abspath, join
+from pyload.utils.PluginLoader import LoaderFactory, PluginLoader
+
+
+class PluginMatcher(object):
+ """ Abstract class that allows modify which plugins to match and to load """
+
+ def matchURL(self, url):
+ return None
+
+ def getPlugin(self, plugin, name):
+ return False
+
+
+class PluginManager:
+ ROOT = "pyload.plugins"
+ LOCALROOT = "localplugins"
+
+ MATCH_HISTORY = 10
+ DEFAULT_PLUGIN = "BasePlugin"
+
+ def __init__(self, core):
+ self.core = core
+ self.log = core.log
+
+ # cached modules (type, name)
+ self.modules = {}
+ # match history to speedup parsing (type, name)
+ self.history = []
+
+ #register for import addon
+ sys.meta_path.append(self)
+
+ # add to path, so we can import from userplugins
+ sys.path.append(abspath(""))
+ self.loader = LoaderFactory(PluginLoader(abspath(self.LOCALROOT), self.LOCALROOT, self.core.config),
+ PluginLoader(abspath(join(pypath, "pyload", "plugins")), self.ROOT,
+ self.core.config))
+
+ self.loader.checkVersions()
+
+ # plugin matcher to overwrite some behaviour
+ self.matcher = []
+
+ def addMatcher(self, matcher, index=0):
+ """ Inserts matcher at given index, first position by default """
+ if not isinstance(matcher, PluginMatcher):
+ raise TypeError("Expected type of PluginMatcher, got %s instead" % type(matcher))
+
+ if matcher in self.matcher:
+ self.matcher.remove(matcher)
+
+ self.matcher.insert(index, matcher)
+
+ def removeMatcher(self, matcher):
+ """ Removes a matcher if it exists """
+ if matcher in self.matcher:
+ self.matcher.remove(matcher)
+
+ def parseUrls(self, urls):
+ """parse plugins for given list of urls, separate to crypter and hoster"""
+
+ res = {"hoster": [], "crypter": []} # tupels of (url, plugin)
+
+ for url in urls:
+ if type(url) not in (str, unicode, buffer):
+ self.log.debug("Parsing invalid type %s" % type(url))
+ continue
+
+ found = False
+
+ for ptype, name in self.history:
+ if self.loader.getPlugin(ptype, name).re.match(url):
+ res[ptype].append((url, name))
+ found = (ptype, name)
+ break # need to exit this loop first
+
+ if found: # found match
+ if self.history[0] != found: #update history
+ self.history.remove(found)
+ self.history.insert(0, found)
+ continue
+
+ for ptype in ("crypter", "hoster"):
+ for loader in self.loader:
+ for name, plugin in loader.getPlugins(ptype).iteritems():
+ if plugin.re.match(url):
+ res[ptype].append((url, name))
+ self.history.insert(0, (ptype, name))
+ del self.history[10:] # cut down to size of 10
+ found = True
+ break
+
+ if not found:
+ res["hoster"].append((url, self.DEFAULT_PLUGIN))
+
+ return res["hoster"], res["crypter"]
+
+ def getPlugins(self, plugin):
+ """ Get all plugins of a certain type in a dict """
+ plugins = {}
+ for loader in self.loader:
+ plugins.update(loader.getPlugins(plugin))
+ return plugins
+
+ def findPlugin(self, name, pluginlist=("hoster", "crypter")):
+ # TODO: use matcher
+ for loader in self.loader:
+ for plugin in pluginlist:
+ if loader.hasPlugin(plugin, name):
+ return plugin, loader.getPlugin(plugin, name)
+
+ return None, None
+
+ def getPluginClass(self, name, overwrite=True):
+ """Gives the plugin class of a hoster or crypter plugin
+
+ :param overwrite: allow the use of overwritten plugins
+ """
+ # TODO: use matcher
+ type, plugin = self.findPlugin(name)
+ return self.loadClass(type, name)
+
+ def loadAttributes(self, plugin, name):
+ for loader in self.loader:
+ if loader.hasPlugin(plugin, name):
+ return loader.loadAttributes(plugin, name)
+
+ return {}
+
+ def loadModule(self, plugin, name):
+ """ Returns loaded module for plugin
+
+ :param plugin: plugin type, subfolder of module.plugins
+ """
+ if (plugin, name) in self.modules: return self.modules[(plugin, name)]
+ for loader in self.loader:
+ if loader.hasPlugin(plugin, name):
+ try:
+ module = loader.loadModule(plugin, name)
+ # cache import
+ self.modules[(plugin, name)] = module
+ return module
+ except Exception, e:
+ self.log.error(_("Error importing %(name)s: %(msg)s") % {"name": name, "msg": str(e)})
+ self.core.print_exc()
+
+ def loadClass(self, plugin, name):
+ """Returns the class of a plugin with the same name"""
+ module = self.loadModule(plugin, name)
+ if module: return getattr(module, name)
+
+ def find_module(self, fullname, path=None):
+ #redirecting imports if necessary
+ for loader in self.loader:
+ if not fullname.startswith(loader.package):
+ continue
+
+ # TODO not well tested
+ offset = 1 - loader.package.count(".")
+
+ split = fullname.split(".")
+ if len(split) != 4 - offset: return
+ plugin, name = split[2 - offset:4 - offset]
+
+ # check if a different loader than the current one has the plugin
+ # in this case import needs redirect
+ for l2 in self.loader:
+ if l2 is not loader and l2.hasPlugin(plugin, name):
+ return self
+
+ # TODO: Remove when all plugin imports are adapted
+ if "module" in fullname:
+ return self
+
+ def load_module(self, name, replace=True):
+ if name not in sys.modules: #could be already in modules
+
+ # TODO: only temporary
+ if name.endswith("module"):
+ # name = "pyload."
+ name = name.replace(".module", "")
+ self.log.debug("Old import reference detected, use %s" % name)
+ replace = False
+ return __import__("pyload")
+ if name.startswith("module"):
+ name = name.replace("module", "pyload")
+ self.log.debug("Old import reference detected, use %s" % name)
+ replace = False
+
+ # TODO: this still works but does not respect other loaders
+ if replace:
+ if self.ROOT in name:
+ newname = name.replace(self.ROOT, self.LOCALROOT)
+ else:
+ newname = name.replace(self.LOCALROOT, self.ROOT)
+ else:
+ newname = name
+
+ base, plugin = newname.rsplit(".", 1)
+
+ self.log.debug("Redirected import %s -> %s" % (name, newname))
+
+ module = __import__(newname, globals(), locals(), [plugin])
+ #inject under new an old name
+ sys.modules[name] = module
+ sys.modules[newname] = module
+
+ return sys.modules[name]
+
+ def reloadPlugins(self, type_plugins):
+ """ reloads and reindexes plugins """
+ # TODO
+ # check if reloadable
+ # reload
+ # save new plugins
+ # update index
+ # reload accounts
+
+ def isUserPlugin(self, plugin):
+ """ A plugin suitable for multiple user """
+ return any(l.isUserPlugin(plugin) for l in self.loader)
+
+ def getCategory(self, plugin):
+ plugin = self.loader.getPlugin("addons", plugin)
+ if plugin:
+ return plugin.category or "addon"
+
+ def loadIcon(self, name):
+ """ load icon for single plugin, base64 encoded"""
+ pass
+
+ def checkDependencies(self, type, name):
+ """ Check deps for given plugin
+
+ :return: List of unfullfilled dependencies
+ """
+ pass
+