diff --git a/couchpotato/core/loader.py b/couchpotato/core/loader.py index 9362bb8c..6ceee4ed 100644 --- a/couchpotato/core/loader.py +++ b/couchpotato/core/loader.py @@ -1,6 +1,8 @@ from couchpotato.core.event import fireEvent from couchpotato.core.logger import CPLog +from importlib import import_module import os +import sys import traceback log = CPLog(__name__) @@ -11,17 +13,6 @@ class Loader(object): providers = {} modules = {} - def addPath(self, root, base_path, priority, recursive = False): - for filename in os.listdir(os.path.join(root, *base_path)): - path = os.path.join(os.path.join(root, *base_path), filename) - if os.path.isdir(path) and filename[:2] != '__': - if u'__init__.py' in os.listdir(path): - new_base_path = ''.join(s + '.' for s in base_path) + filename - self.paths[new_base_path.replace('.', '_')] = (priority, new_base_path, path) - - if recursive: - self.addPath(root, base_path + [filename], priority, recursive = True) - def preload(self, root = ''): core = os.path.join(root, 'couchpotato', 'core') @@ -38,6 +29,13 @@ class Loader(object): # Add media to loader self.addPath(root, ['couchpotato', 'core', 'media'], 25, recursive = True) + # Add custom plugin folder + from couchpotato.environment import Env + custom_plugin_dir = os.path.join(Env.get('data_dir'), 'custom_plugins') + sys.path.insert(0, custom_plugin_dir) + self.paths['custom_plugins'] = (30, '', custom_plugin_dir) + + # Loop over all paths and add to module list for plugin_type, plugin_tuple in self.paths.iteritems(): priority, module, dir_name = plugin_tuple self.addFromDir(plugin_type, priority, module, dir_name) @@ -45,8 +43,9 @@ class Loader(object): def run(self): did_save = 0 - for priority in self.modules: + for priority in sorted(self.modules): for module_name, plugin in sorted(self.modules[priority].iteritems()): + # Load module try: if plugin.get('name')[:2] == '__': @@ -55,7 +54,6 @@ class Loader(object): m = self.loadModule(module_name) if m is None: continue - m = getattr(m, plugin.get('name')) log.info('Loading %s: %s', (plugin['type'], plugin['name'])) @@ -77,10 +75,23 @@ class Loader(object): if did_save: fireEvent('settings.save') + def addPath(self, root, base_path, priority, recursive = False): + root_path = os.path.join(root, *base_path) + for filename in os.listdir(root_path): + path = os.path.join(root_path, filename) + if os.path.isdir(path) and filename[:2] != '__': + if u'__init__.py' in os.listdir(path): + new_base_path = ''.join(s + '.' for s in base_path) + filename + self.paths[new_base_path.replace('.', '_')] = (priority, new_base_path, path) + + if recursive: + self.addPath(root, base_path + [filename], priority, recursive = True) + def addFromDir(self, plugin_type, priority, module, dir_name): # Load dir module - self.addModule(priority, plugin_type, module, os.path.basename(dir_name)) + if module and len(module) > 0: + self.addModule(priority, plugin_type, module, os.path.basename(dir_name)) for name in os.listdir(dir_name): if os.path.isdir(os.path.join(dir_name, name)) and name != 'static' and os.path.isfile(os.path.join(dir_name, name, '__init__.py')): @@ -123,6 +134,7 @@ class Loader(object): if not self.modules.get(priority): self.modules[priority] = {} + module = module.lstrip('.') self.modules[priority][module] = { 'priority': priority, 'module': module, @@ -132,11 +144,7 @@ class Loader(object): def loadModule(self, name): try: - m = __import__(name) - splitted = name.split('.') - for sub in splitted[1:-1]: - m = getattr(m, sub) - return m + return import_module(name) except ImportError: log.debug('Skip loading module plugin %s: %s', (name, traceback.format_exc())) return None diff --git a/couchpotato/core/plugins/custom/__init__.py b/couchpotato/core/plugins/custom/__init__.py new file mode 100644 index 00000000..573cd99f --- /dev/null +++ b/couchpotato/core/plugins/custom/__init__.py @@ -0,0 +1,6 @@ +from .main import Custom + +def start(): + return Custom() + +config = [] diff --git a/couchpotato/core/plugins/custom/main.py b/couchpotato/core/plugins/custom/main.py new file mode 100644 index 00000000..a15c915c --- /dev/null +++ b/couchpotato/core/plugins/custom/main.py @@ -0,0 +1,21 @@ +from couchpotato.core.event import addEvent +from couchpotato.core.logger import CPLog +from couchpotato.core.plugins.base import Plugin +from couchpotato.environment import Env +import os + +log = CPLog(__name__) + + +class Custom(Plugin): + + def __init__(self): + addEvent('app.load', self.createStructure) + + def createStructure(self): + + custom_dir = os.path.join(Env.get('data_dir'), 'custom_plugins') + + if not os.path.isdir(custom_dir): + self.makeDir(custom_dir) + self.createFile(os.path.join(custom_dir, '__init__.py'), '# Don\'t remove this file') diff --git a/libs/importlib/__init__.py b/libs/importlib/__init__.py new file mode 100644 index 00000000..ad31a1ac --- /dev/null +++ b/libs/importlib/__init__.py @@ -0,0 +1,38 @@ +"""Backport of importlib.import_module from 3.x.""" +# While not critical (and in no way guaranteed!), it would be nice to keep this +# code compatible with Python 2.3. +import sys + +def _resolve_name(name, package, level): + """Return the absolute name of the module to be imported.""" + if not hasattr(package, 'rindex'): + raise ValueError("'package' not set to a string") + dot = len(package) + for x in xrange(level, 1, -1): + try: + dot = package.rindex('.', 0, dot) + except ValueError: + raise ValueError("attempted relative import beyond top-level " + "package") + return "%s.%s" % (package[:dot], name) + + +def import_module(name, package=None): + """Import a module. + + The 'package' argument is required when performing a relative import. It + specifies the package to use as the anchor point from which to resolve the + relative import to an absolute import. + + """ + if name.startswith('.'): + if not package: + raise TypeError("relative imports require the 'package' argument") + level = 0 + for character in name: + if character != '.': + break + level += 1 + name = _resolve_name(name[level:], package, level) + __import__(name) + return sys.modules[name]