diff options
Diffstat (limited to 'module')
-rw-r--r-- | module/HookManager.py | 4 | ||||
-rw-r--r-- | module/plugins/AccountManager.py | 2 | ||||
-rw-r--r-- | module/plugins/Plugin.py | 2 | ||||
-rw-r--r-- | module/plugins/PluginManager.py | 224 | ||||
-rw-r--r-- | module/plugins/accounts/WuploadCom.py | 2 | ||||
-rw-r--r-- | module/plugins/hooks/ExtractArchive.py | 113 | ||||
-rw-r--r-- | module/plugins/hooks/RealdebridCom.py | 3 | ||||
-rw-r--r-- | module/plugins/hooks/RehostTo.py | 4 | ||||
-rw-r--r-- | module/plugins/hoster/MediafireCom.py | 17 | ||||
-rw-r--r-- | module/plugins/internal/AbstractExtractor.py | 93 | ||||
-rw-r--r-- | module/plugins/internal/MultiHoster.py (renamed from module/common/MultiHoster.py) | 0 | ||||
-rw-r--r-- | module/plugins/internal/SimpleHoster.py | 12 | ||||
-rw-r--r-- | module/plugins/internal/UnRar.py | 12 | ||||
-rw-r--r-- | module/plugins/internal/UnZip.py | 2 |
14 files changed, 224 insertions, 266 deletions
diff --git a/module/HookManager.py b/module/HookManager.py index 25f84831e..1ecaae1ac 100644 --- a/module/HookManager.py +++ b/module/HookManager.py @@ -124,7 +124,7 @@ class HookManager: #hookClass = getattr(plugin, plugin.__name__) if self.core.config.getPlugin(pluginname, "activated"): - pluginClass = self.core.pluginManager.getHookPlugin(pluginname) + pluginClass = self.core.pluginManager.loadClass("hooks", pluginname) if not pluginClass: continue plugin = pluginClass(self.core, self) @@ -159,7 +159,7 @@ class HookManager: if inst.__name__ == plugin: return - pluginClass = self.core.pluginManager.getHookPlugin(plugin) + pluginClass = self.core.pluginManager.loadClass("hooks", plugin) if not pluginClass: return diff --git a/module/plugins/AccountManager.py b/module/plugins/AccountManager.py index 96090ef38..7e30f4817 100644 --- a/module/plugins/AccountManager.py +++ b/module/plugins/AccountManager.py @@ -51,7 +51,7 @@ class AccountManager(): """get account instance for plugin or None if anonymous""" if plugin in self.accounts: if plugin not in self.plugins: - self.plugins[plugin] = self.core.pluginManager.getAccountPlugin(plugin)(self, self.accounts[plugin]) + self.plugins[plugin] = self.core.pluginManager.loadClass("accounts", plugin)(self, self.accounts[plugin]) return self.plugins[plugin] else: diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py index e503cd6ed..7a7ecd974 100644 --- a/module/plugins/Plugin.py +++ b/module/plugins/Plugin.py @@ -359,7 +359,7 @@ class Plugin(Base): has_plugin = self.__name__ in self.core.pluginManager.captchaPlugins if self.core.captcha: - Ocr = self.core.pluginManager.getCaptchaPlugin(self.__name__) + Ocr = self.core.pluginManager.loadClass("captcha", self.__name__) else: Ocr = None diff --git a/module/plugins/PluginManager.py b/module/plugins/PluginManager.py index e5a1c06cc..e6ad14b66 100644 --- a/module/plugins/PluginManager.py +++ b/module/plugins/PluginManager.py @@ -29,34 +29,36 @@ from traceback import print_exc from module.lib.SafeEval import const_eval as literal_eval from module.ConfigParser import IGNORE -class PluginManager(): +class PluginManager: + ROOT = "module.plugins." + USERROOT = "userplugins." + TYPES = ("crypter", "container", "hoster", "captcha", "accounts", "hooks", "internal") + + PATTERN = re.compile(r'__pattern__.*=.*r("|\')([^"\']+)') + VERSION = re.compile(r'__version__.*=.*("|\')([0-9.]+)') + CONFIG = re.compile(r'__config__.*=.*\[([^\]]+)', re.MULTILINE) + DESC = re.compile(r'__description__.?=.?("|"""|\')([^"\']+)') + + def __init__(self, core): self.core = core #self.config = self.core.config self.log = core.log - self.crypterPlugins = {} - self.containerPlugins = {} - self.hosterPlugins = {} - self.captchaPlugins = {} - self.accountPlugins = {} - self.hookPlugins = {} + self.createIndex() self.plugins = {"crypter": self.crypterPlugins, "container": self.containerPlugins, "hoster": self.hosterPlugins, "captcha": self.captchaPlugins, "accounts": self.accountPlugins, - "hooks": self.hookPlugins} - - self.createIndex() + "hooks": self.hookPlugins, + "internal": self.internalPlugins} + #register for import hook + sys.meta_path.append(self) - rePattern = re.compile(r'__pattern__.*=.*r("|\')([^"\']+)') - reVersion = re.compile(r'__version__.*=.*("|\')([0-9.]+)') - reConfig = re.compile(r'__config__.*=.*\[([^\]]+)', re.MULTILINE) - reDesc = re.compile(r'__description__.?=.?("|"""|\')([^"\']+)') def createIndex(self): """create information for all plugins available""" @@ -69,11 +71,6 @@ class PluginManager(): f = open(join("userplugins", "__init__.py"), "wb") f.close() - self.rePattern = re.compile(r'__pattern__.*=.*r("|\')([^"\']+)') - self.reVersion = re.compile(r'__version__.*=.*("|\')([0-9.]+)') - self.reConfig = re.compile(r'__config__.*=.*\[([^\]]+)', re.MULTILINE) - self.reDesc = re.compile(r'__description__.?=.?("|"""|\')([^"\']+)') - self.crypterPlugins = self.parse("crypter", pattern=True) self.containerPlugins = self.parse("container", pattern=True) self.hosterPlugins = self.parse("hoster", pattern=True) @@ -124,7 +121,7 @@ class PluginManager(): name = f[:-3] if name[-1] == ".": name = name[:-4] - version = self.reVersion.findall(content) + version = self.VERSION.findall(content) if version: version = float(version[0][1]) else: @@ -143,15 +140,17 @@ class PluginManager(): if name in IGNORE: del plugins[name] continue - path = "userplugins.%s.%s" % (folder, module) + + user = True else: - path = "module.plugins.%s.%s" % (folder, module) + user = False + # the plugin is loaded from user directory + plugins[name]["user"] = user plugins[name]["name"] = module - plugins[name]["path"] = path if pattern: - pattern = self.rePattern.findall(content) + pattern = self.PATTERN.findall(content) if pattern: pattern = pattern[0][1] @@ -171,10 +170,10 @@ class PluginManager(): self.core.config.deleteConfig(name) continue - config = self.reConfig.findall(content) + config = self.CONFIG.findall(content) if config: config = literal_eval(config[0].strip().replace("\n", "").replace("\r", "")) - desc = self.reDesc.findall(content) + desc = self.DESC.findall(content) desc = desc[0][1] if desc else "" if type(config[0]) == tuple: @@ -182,8 +181,6 @@ class PluginManager(): else: config = [list(config)] - - if folder == "hooks": append = True for item in config: @@ -194,17 +191,17 @@ class PluginManager(): try: self.core.config.addPluginConfig(name, config, desc) - except : + except: self.log.error("Invalid config in %s: %s" % (name, config)) elif folder == "hooks": #force config creation - desc = self.reDesc.findall(content) + desc = self.DESC.findall(content) desc = desc[0][1] if desc else "" config = (["activated", "bool", "Activated", False],) try: self.core.config.addPluginConfig(name, config, desc) - except : + except: self.log.error("Invalid config in %s: %s" % (name, config)) if not home: @@ -229,7 +226,7 @@ class PluginManager(): continue for name, value in chain(self.crypterPlugins.iteritems(), self.hosterPlugins.iteritems(), - self.containerPlugins.iteritems()): + self.containerPlugins.iteritems()): if value["re"].match(url): res.append((url, name)) last = (name, value) @@ -241,17 +238,15 @@ class PluginManager(): return res + def findPlugin(self, name, pluginlist=("hoster", "crypter", "container")): + for ptype in pluginlist: + if name in self.plugins[ptype]: + return self.plugins[ptype][name], ptype + return None, None def getPlugin(self, name, original=False): """return plugin module from hoster|decrypter|container""" - plugin = None - - if name in self.containerPlugins: - plugin = self.containerPlugins[name] - if name in self.crypterPlugins: - plugin = self.crypterPlugins[name] - if name in self.hosterPlugins: - plugin = self.hosterPlugins[name] + plugin, type = self.findPlugin(name) if not plugin: self.log.warning("Plugin %s not found." % name) @@ -260,112 +255,85 @@ class PluginManager(): if "new_module" in plugin and not original: return plugin["new_module"] - if "module" in plugin: - return plugin["module"] - - plugin["module"] = __import__(plugin["path"], globals(), locals(), [plugin["name"]], -1) - - return plugin["module"] + return self.loadModule(type, name) def getPluginName(self, name): """ used to obtain new name if other plugin was injected""" - plugin = None - if name in self.containerPlugins: - plugin = self.containerPlugins[name] - if name in self.crypterPlugins: - plugin = self.crypterPlugins[name] - if name in self.hosterPlugins: - plugin = self.hosterPlugins[name] + plugin, type = self.findPlugin(name) if "new_name" in plugin: return plugin["new_name"] - - return name - - - - def getCaptchaPlugin(self, name): - """return captcha modul if existent""" - if name in self.captchaPlugins: - plugin = self.captchaPlugins[name] - if "class" in plugin: - return plugin["class"] - module = __import__(plugin["path"], globals(), locals(), [plugin["name"]], -1) - plugin["class"] = getattr(module, name) - - return plugin["class"] - - return None - - - def getAccountPlugin(self, name): - """return account class if existent""" - if name in self.accountPlugins: - plugin = self.accountPlugins[name] - if "class" in plugin: - return plugin["class"] - - module = __import__(plugin["path"], globals(), locals(), [plugin["name"]], -1) - plugin["class"] = getattr(module, plugin["name"]) - - return plugin["class"] + return name - return None + def loadModule(self, type, name): + """ Returns loaded module for plugin + :param type: plugin type, subfolder of module.plugins + :param name: + """ + plugins = self.plugins[type] + if name in plugins: + if "module" in plugins[name]: return plugins[name]["module"] + try: + module = __import__(self.ROOT + "%s.%s" % (type, plugins[name]["name"]), globals(), locals(), + plugins[name]["name"]) + plugins[name]["module"] = module #cache import, maybe unneeded + return module + except Exception, e: + self.log.error(_("Error importing %(name)s: %(msg)s") % {"name": name, "msg": str(e)}) + if self.core.debug: + print_exc() + + def loadClass(self, type, name): + """Returns the class of a plugin with the same name""" + module = self.loadModule(type, name) + if module: return getattr(module, name) def getAccountPlugins(self): """return list of account plugin names""" - res = [] - - for name in self.accountPlugins.keys(): - res.append(name) - - return res - - def getHookPlugin(self, name): - - if name not in self.hookPlugins: return None - - value = self.hookPlugins[name] - - if "class" in value: - return value["class"] - - try: - module = __import__(value["path"], globals(), locals(), [value["name"]], -1) - pluginClass = getattr(module, name) - except Exception, e: - self.log.error(_("Error importing %(name)s: %(msg)s") % {"name": name, "msg": str(e)}) - self.log.error(_("You should fix dependicies or deactivate it.")) - if self.core.debug: - print_exc() - return None - - value["class"] = pluginClass - - return pluginClass + return self.accountPlugins.keys() + + def find_module(self, fullname, path=None): + #redirecting imports if necesarry + if fullname.startswith(self.ROOT) or fullname.startswith(self.USERROOT): #seperate pyload plugins + if fullname.startswith(self.USERROOT): user = 1 + else: user = 0 #used as bool and int + + split = fullname.split(".") + if len(split) != 4 - user: return + type, name = split[2 - user:4 - user] + + if type in self.plugins and name in self.plugins[type]: + #userplugin is a newer version + if not user and self.plugins[type][name]["user"]: + return self + #imported from userdir, but pyloads is newer + if user and not self.plugins[type][name]["user"]: + return self + + + def load_module(self, name, replace=True): + if name not in sys.modules: #could be already in modules + if replace: + if self.ROOT in name: + newname = name.replace(self.ROOT, self.USERROOT) + else: + newname = name.replace(self.USERROOT, self.ROOT) + else: newname = name - def getInternalModule(self, name): + base, plugin = newname.rsplit(".", 1) - if name not in self.internalPlugins: return None + self.log.debug("Redirected import %s -> %s" % (name, newname)) - value = self.internalPlugins[name] + module = __import__(newname, globals(), locals(), [plugin]) + #inject under new an old name + sys.modules[name] = module + sys.modules[newname] = module - if "module" in value: - return value["module"] + return sys.modules[name] - try: - module = __import__(value["path"], globals(), locals(), [value["name"]], -1) - except Exception, e: - self.log.error(_("Error importing %(name)s: %(msg)s") % {"name": name, "msg": str(e)}) - if self.core.debug: - print_exc() - return None - value["module"] = module - return module - def reloadPlugins(self): """ reloads and reindexes plugins """ pass diff --git a/module/plugins/accounts/WuploadCom.py b/module/plugins/accounts/WuploadCom.py index b3d73df27..3d9ddfffa 100644 --- a/module/plugins/accounts/WuploadCom.py +++ b/module/plugins/accounts/WuploadCom.py @@ -33,7 +33,7 @@ class WuploadCom(Account): API_URL = "http://api.wupload.com" def init(self): - fs = self.core.pluginManager.getAccountPlugin("FilesonicCom") + fs = self.core.pluginManager.loadClass("accounts", "FilesonicCom") methods = ["loadAccountInfo", "login"] #methods to bind from fs diff --git a/module/plugins/hooks/ExtractArchive.py b/module/plugins/hooks/ExtractArchive.py index 0a70da417..359bfca76 100644 --- a/module/plugins/hooks/ExtractArchive.py +++ b/module/plugins/hooks/ExtractArchive.py @@ -8,7 +8,6 @@ from os.path import exists, basename, isfile, isdir, join from traceback import print_exc from copy import copy - # monkey patch bug in python 2.6 and lower # see http://bugs.python.org/issue6122 # http://bugs.python.org/issue1236 @@ -49,21 +48,9 @@ if os.name != "nt": from pwd import getpwnam from grp import getgrnam -from module.plugins.Hook import Hook, threaded, Expose from module.utils import save_join, fs_encode - - -class ArchiveError(Exception): - pass - - -class CRCError(Exception): - pass - - -class WrongPassword(Exception): - pass - +from module.plugins.Hook import Hook, threaded, Expose +from module.plugins.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword class ExtractArchive(Hook): """ @@ -93,7 +80,7 @@ class ExtractArchive(Hook): for p in ("UnRar", "UnZip"): try: - module = self.core.pluginManager.getInternalModule(p) + module = self.core.pluginManager.loadModule("internal", p) klass = getattr(module, p) if klass.checkDeps(): names.append(p) @@ -317,96 +304,4 @@ class ExtractArchive(Hook): gid = getgrnam(self.config["permission"]["group"])[2] chown(f, uid, gid) except Exception, e: - self.log.warning(_("Setting User and Group failed"), e) - - def archiveError(self, msg): - raise ArchiveError(msg) - - def wrongPassword(self): - raise WrongPassword() - - def crcError(self): - raise CRCError() - - -class AbtractExtractor: - @staticmethod - def checkDeps(): - """ Check if system statisfy dependencies - :return: boolean - """ - return True - - @staticmethod - def getTargets(files_ids): - """ Filter suited targets from list of filename id tuple list - :param files_ids: List of filepathes - :return: List of targets, id tuple list - """ - raise NotImplementedError - - - def __init__(self, m, file, out, fullpath, overwrite, renice): - """Initialize extractor for specific file - - :param m: ExtractArchive Hook plugin - :param file: Absolute filepath - :param out: Absolute path to destination directory - :param fullpath: extract to fullpath - :param overwrite: Overwrite existing archives - :param renice: Renice value - """ - self.m = m - self.file = file - self.out = out - self.fullpath = fullpath - self.overwrite = overwrite - self.renice = renice - self.files = [] # Store extracted files here - - - def init(self): - """ Initialize additional data structures """ - pass - - - def checkArchive(self): - """Check if password if needed. Raise ArchiveError if integrity is - questionable. - - :return: boolean - :raises ArchiveError - """ - return False - - def checkPassword(self, password): - """ Check if the given password is/might be correct. - If it can not be decided at this point return true. - - :param password: - :return: boolean - """ - return True - - def extract(self, progress, password=None): - """Extract the archive. Raise specific errors in case of failure. - - :param progress: Progress function, call this to update status - :param password password to use - :raises WrongPassword - :raises CRCError - :raises ArchiveError - :return: - """ - raise NotImplementedError - - def getDeleteFiles(self): - """Return list of files to delete, do *not* delete them here. - - :return: List with paths of files to delete - """ - raise NotImplementedError - - def getExtractedFiles(self): - """Populate self.files at some point while extracting""" - return self.files
\ No newline at end of file + self.log.warning(_("Setting User and Group failed"), e)
\ No newline at end of file diff --git a/module/plugins/hooks/RealdebridCom.py b/module/plugins/hooks/RealdebridCom.py index 4ec109de4..c57e3de52 100644 --- a/module/plugins/hooks/RealdebridCom.py +++ b/module/plugins/hooks/RealdebridCom.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- - from module.network.RequestFactory import getURL -from module.common.MultiHoster import MultiHoster +from module.plugins.internal.MultiHoster import MultiHoster class RealdebridCom(MultiHoster): __name__ = "RealdebridCom" diff --git a/module/plugins/hooks/RehostTo.py b/module/plugins/hooks/RehostTo.py index f0a95d925..34bcbf4c7 100644 --- a/module/plugins/hooks/RehostTo.py +++ b/module/plugins/hooks/RehostTo.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from module.network.RequestFactory import getURL -from module.common.MultiHoster import MultiHoster +from module.plugins.internal.MultiHoster import MultiHoster class RehostTo(MultiHoster): __name__ = "RehostTo" @@ -24,7 +24,7 @@ class RehostTo(MultiHoster): def coreReady(self): - self.account = self.core.accountManager.getAccountPlugin("RehostTo") + self.account = self.core.accountManager.loadClass("accounts", "RehostTo") user = self.account.selectAccount()[0] diff --git a/module/plugins/hoster/MediafireCom.py b/module/plugins/hoster/MediafireCom.py index 15fc9f3c6..e499a406f 100644 --- a/module/plugins/hoster/MediafireCom.py +++ b/module/plugins/hoster/MediafireCom.py @@ -17,19 +17,9 @@ """ import re -from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo -from module.network.RequestFactory import getURL +from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo from module.plugins.ReCaptcha import ReCaptcha -def getInfo(urls): - result = [] - - for url in urls: - file_info = parseFileInfo(MediafireCom, url, getURL(url, decode=True)) - result.append(file_info) - - yield result - def replace_eval(js_expr): return js_expr.replace(r'eval("', '').replace(r"\'", r"'").replace(r'\"', r'"') @@ -125,4 +115,7 @@ class MediafireCom(SimpleHoster): self.fail("Final link not found") self.logDebug("FINAL LINK: %s" % final_link) - self.download(final_link)
\ No newline at end of file + self.download(final_link) + + +getInfo = create_getInfo(MediafireCom)
\ No newline at end of file diff --git a/module/plugins/internal/AbstractExtractor.py b/module/plugins/internal/AbstractExtractor.py new file mode 100644 index 000000000..2130f910e --- /dev/null +++ b/module/plugins/internal/AbstractExtractor.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +class ArchiveError(Exception): + pass + +class CRCError(Exception): + pass + +class WrongPassword(Exception): + pass + +class AbtractExtractor: + @staticmethod + def checkDeps(): + """ Check if system statisfy dependencies + :return: boolean + """ + return True + + @staticmethod + def getTargets(files_ids): + """ Filter suited targets from list of filename id tuple list + :param files_ids: List of filepathes + :return: List of targets, id tuple list + """ + raise NotImplementedError + + + def __init__(self, m, file, out, fullpath, overwrite, renice): + """Initialize extractor for specific file + + :param m: ExtractArchive Hook plugin + :param file: Absolute filepath + :param out: Absolute path to destination directory + :param fullpath: extract to fullpath + :param overwrite: Overwrite existing archives + :param renice: Renice value + """ + self.m = m + self.file = file + self.out = out + self.fullpath = fullpath + self.overwrite = overwrite + self.renice = renice + self.files = [] # Store extracted files here + + + def init(self): + """ Initialize additional data structures """ + pass + + + def checkArchive(self): + """Check if password if needed. Raise ArchiveError if integrity is + questionable. + + :return: boolean + :raises ArchiveError + """ + return False + + def checkPassword(self, password): + """ Check if the given password is/might be correct. + If it can not be decided at this point return true. + + :param password: + :return: boolean + """ + return True + + def extract(self, progress, password=None): + """Extract the archive. Raise specific errors in case of failure. + + :param progress: Progress function, call this to update status + :param password password to use + :raises WrongPassword + :raises CRCError + :raises ArchiveError + :return: + """ + raise NotImplementedError + + def getDeleteFiles(self): + """Return list of files to delete, do *not* delete them here. + + :return: List with paths of files to delete + """ + raise NotImplementedError + + def getExtractedFiles(self): + """Populate self.files at some point while extracting""" + return self.files
\ No newline at end of file diff --git a/module/common/MultiHoster.py b/module/plugins/internal/MultiHoster.py index 6f0b4b636..6f0b4b636 100644 --- a/module/common/MultiHoster.py +++ b/module/plugins/internal/MultiHoster.py diff --git a/module/plugins/internal/SimpleHoster.py b/module/plugins/internal/SimpleHoster.py index a05b6e98c..e0963fd91 100644 --- a/module/plugins/internal/SimpleHoster.py +++ b/module/plugins/internal/SimpleHoster.py @@ -19,8 +19,10 @@ from module.plugins.Hoster import Hoster from module.utils import html_unescape +from module.network.RequestFactory import getURL from re import search + def parseFileInfo(self, url = '', html = ''): if not html and hasattr(self, "html"): html = self.html name, size, status, found = '', 0, 3, 0 @@ -52,7 +54,15 @@ def parseFileInfo(self, url = '', html = ''): if not name: name = url - return (name, size, status, url) + return name, size, status, url + + +def create_getInfo(plugin): + def getInfo(urls): + for url in urls: + file_info = parseFileInfo(plugin, url, getURL(url, decode=True)) + yield file_info + return getInfo class PluginParseError(Exception): def __init__(self, msg): diff --git a/module/plugins/internal/UnRar.py b/module/plugins/internal/UnRar.py index 1943f69e0..feac4c176 100644 --- a/module/plugins/internal/UnRar.py +++ b/module/plugins/internal/UnRar.py @@ -23,8 +23,8 @@ from os.path import join from glob import glob from subprocess import Popen, PIPE -from module.plugins.hooks.ExtractArchive import AbtractExtractor from module.utils import save_join, decode +from module.plugins.internal.AbstractExtractor import AbtractExtractor, WrongPassword, ArchiveError, CRCError class UnRar(AbtractExtractor): __name__ = "UnRar" @@ -95,7 +95,7 @@ class UnRar(AbtractExtractor): self.listContent() if not self.files: - self.m.archiveError("Empty Archive") + raise ArchiveError("Empty Archive") return False @@ -123,11 +123,11 @@ class UnRar(AbtractExtractor): progress(100) if "CRC failed" in err and not password and not self.passwordProtected: - self.m.crcError() + raise CRCError elif "CRC failed" in err: - self.m.wrongPassword() + raise WrongPassword if err.strip(): #raise error if anything is on stderr - self.m.archiveError(err.strip()) + raise ArchiveError(err.strip()) if not self.files: self.password = password @@ -145,7 +145,7 @@ class UnRar(AbtractExtractor): out, err = p.communicate() if "Cannot open" in err: - self.m.archiveError("Cannot open file") + raise ArchiveError("Cannot open file") if err.strip(): # only log error at this point self.m.logError(err.strip()) diff --git a/module/plugins/internal/UnZip.py b/module/plugins/internal/UnZip.py index dc60c1041..9aa9ac75c 100644 --- a/module/plugins/internal/UnZip.py +++ b/module/plugins/internal/UnZip.py @@ -20,7 +20,7 @@ import zipfile import sys -from module.plugins.hooks.ExtractArchive import AbtractExtractor +from module.plugins.internal.AbstractExtractor import AbtractExtractor class UnZip(AbtractExtractor): __name__ = "UnZip" |