diff options
| author | 2015-02-16 10:46:28 +0100 | |
|---|---|---|
| committer | 2015-02-16 10:46:28 +0100 | |
| commit | ce1c2b6b05c08b669357947e61ae40efce7fc50f (patch) | |
| tree | 0b5f7996960cf35c4eface53a89eba18a37519b7 /module/plugins/addon/ExtractArchive.py | |
| parent | Fix filename case (diff) | |
| download | pyload-ce1c2b6b05c08b669357947e61ae40efce7fc50f.tar.xz | |
module temp
Diffstat (limited to 'module/plugins/addon/ExtractArchive.py')
| -rw-r--r-- | module/plugins/addon/ExtractArchive.py | 363 | 
1 files changed, 363 insertions, 0 deletions
diff --git a/module/plugins/addon/ExtractArchive.py b/module/plugins/addon/ExtractArchive.py new file mode 100644 index 000000000..b24bb37a2 --- /dev/null +++ b/module/plugins/addon/ExtractArchive.py @@ -0,0 +1,363 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import os +import sys + +from copy import copy +from os import remove, chmod, makedirs +from os.path import exists, basename, isfile, isdir +from traceback import print_exc + +# monkey patch bug in python 2.6 and lower +# http://bugs.python.org/issue6122 , http://bugs.python.org/issue1236 , http://bugs.python.org/issue1731717 +if sys.version_info < (2, 7) and os.name != "nt": +    import errno +    from subprocess import Popen + + +    def _eintr_retry_call(func, *args): +        while True: +            try: +                return func(*args) +            except OSError, e: +                if e.errno == errno.EINTR: +                    continue +                raise + + +    # unsued timeout option for older python version +    def wait(self, timeout=0): +        """Wait for child process to terminate.  Returns returncode +        attribute.""" +        if self.returncode is None: +            try: +                pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0) +            except OSError, e: +                if e.errno != errno.ECHILD: +                    raise +                    # This happens if SIGCLD is set to be ignored or waiting +                # for child processes has otherwise been disabled for our +                # process.  This child is dead, we can't get the status. +                sts = 0 +            self._handle_exitstatus(sts) +        return self.returncode + +    Popen.wait = wait + +if os.name != "nt": +    from grp import getgrnam +    from os import chown +    from pwd import getpwnam + +from pyload.plugin.Addon import Addon, threaded, Expose +from pyload.plugin.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword +from pyload.utils import safe_join, fs_encode + + +class ExtractArchive(Addon): +    __name__    = "ExtractArchive" +    __type__    = "addon" +    __version__ = "0.19" + +    __config__ = [("activated"    , "bool"  , "Activated"                                    , True                  ), +                ("fullpath"     , "bool"  , "Extract full path"                            , True                  ), +                ("overwrite"    , "bool"  , "Overwrite files"                              , True                  ), +                ("passwordfile" , "file"  , "password file"                                , "archive_password.txt"), +                ("deletearchive", "bool"  , "Delete archives when done"                    , False                 ), +                ("subfolder"    , "bool"  , "Create subfolder for each package"            , False                 ), +                ("destination"  , "folder", "Extract files to"                             , ""                    ), +                ("excludefiles" , "str"   , "Exclude files from unpacking (seperated by ;)", ""                    ), +                ("recursive"    , "bool"  , "Extract archives in archvies"                 , True                  ), +                ("queue"        , "bool"  , "Wait for all downloads to be finished"        , True                  ), +                ("renice"       , "int"   , "CPU Priority"                                 , 0                     )] + +    __description__ = """Extract different kind of archives""" +    __license__     = "GPLv3" +    __authors__     = [("RaNaN", "ranan@pyload.org"), +                     ("AndroKev", ""), +                     ("Walter Purcaro", "vuolter@gmail.com")] + + +    event_map = {'all_downloads-processed': "allDownloadsProcessed"} + + +    def setup(self): +        self.plugins = [] +        self.passwords = [] +        names = [] + +        for p in ("UnRar", "UnZip"): +            try: +                module = self.core.pluginManager.loadModule("internal", p) +                klass = getattr(module, p) +                if klass.checkDeps(): +                    names.append(p) +                    self.plugins.append(klass) + +            except OSError, e: +                if e.errno == 2: +                    self.logInfo(_("No %s installed") % p) +                else: +                    self.logWarning(_("Could not activate %s") % p, e) +                    if self.core.debug: +                        print_exc() + +            except Exception, e: +                self.logWarning(_("Could not activate %s") % p, e) +                if self.core.debug: +                    print_exc() + +        if names: +            self.logInfo(_("Activated") + " " + " ".join(names)) +        else: +            self.logInfo(_("No Extract plugins activated")) + +        # queue with package ids +        self.queue = [] + + +    @Expose +    def extractPackage(self, id): +        """ Extract package with given id""" +        self.manager.startThread(self.extract, [id]) + + +    def packageFinished(self, pypack): +        pid = pypack.id +        if self.getConfig("queue"): +            self.logInfo(_("Package %s queued for later extracting") % pypack.name) +            self.queue.append(pid) +        else: +            self.manager.startThread(self.extract, [pid]) + + +    @threaded +    def allDownloadsProcessed(self, thread): +        local = copy(self.queue) +        del self.queue[:] +        if self.extract(local, thread):  #: check only if all gone fine, no failed reporting for now +            self.manager.dispatchEvent("all_archives-extracted") +        self.manager.dispatchEvent("all_archives-processed") + + +    def extract(self, ids, thread=None): +        processed = [] +        extracted = [] +        failed = [] + +        destination = self.getConfig("destination") +        subfolder = self.getConfig("subfolder") +        fullpath = self.getConfig("fullpath") +        overwrite = self.getConfig("overwrite") +        excludefiles = self.getConfig("excludefiles") +        renice = self.getConfig("renice") +        recursive = self.getConfig("recursive") + +        # reload from txt file +        self.reloadPasswords() + +        # dl folder +        dl = self.config['general']['download_folder'] + +        #iterate packages -> plugins -> targets +        for pid in ids: +            p = self.core.files.getPackage(pid) +            self.logInfo(_("Check package %s") % p.name) +            if not p: +                continue + +            # determine output folder +            out = safe_join(dl, p.folder, "") + +                out = safe_join(dl, p.folder, self.getConfig("destination"), "") +            if subfolder: +                    out = safe_join(out, fs_encode(p.folder)) + +            if not exists(out): +                makedirs(out) + +            files_ids = [(safe_join(dl, p.folder, x['name']), x['id']) for x in p.getChildren().itervalues()] +            matched = False +            success = True + +            # check as long there are unseen files +            while files_ids: +                new_files_ids = [] + +                for plugin in self.plugins: +                    targets = plugin.getTargets(files_ids) +                    if targets: +                        self.logDebug("Targets for %s: %s" % (plugin.__name__, targets)) +                        matched = True +                    for target, fid in targets: +                        if target in processed: +                            self.logDebug(basename(target), "skipped") +                            continue + +                        processed.append(target)  # prevent extracting same file twice + +                        self.logInfo(basename(target), _("Extract to %s") % out) +                        try: +                            klass = plugin(self, target, out, fullpath, overwrite, excludefiles, renice) +                            klass.init() +                            password = p.password.strip().splitlines() +                            new_files = self._extract(klass, fid, password, thread) +                        except Exception, e: +                            self.logError(basename(target), e) +                            success = False +                            continue + +                        self.logDebug("Extracted", new_files) +                        self.setPermissions(new_files) + +                        for file in new_files: +                            if not exists(file): +                                self.logDebug("New file %s does not exists" % file) +                                continue +                            if recursive and isfile(file): +                                new_files_ids.append((file, fid))  # append as new target + +                files_ids = new_files_ids  # also check extracted files + +            if matched: +                if success: +                    extracted.append(pid) +                    self.manager.dispatchEvent("package-extracted", p) +                else: +                    failed.append(pid) +                    self.manager.dispatchEvent("package-extract_failed", p) +            else: +                self.logInfo(_("No files found to extract")) + +        return True if not failed else False + + +    def _extract(self, plugin, fid, passwords, thread): +        pyfile = self.core.files.getFile(fid) +        deletearchive = self.getConfig("deletearchive") + +        pyfile.setCustomStatus(_("extracting")) +        thread.addActive(pyfile)  # keep this file until everything is done + +        try: +            progress = lambda x: pyfile.setProgress(x) +            success = False + +            if not plugin.checkArchive(): +                plugin.extract(progress) +                success = True +            else: +                self.logInfo(basename(plugin.file), _("Password protected")) +                self.logDebug("Passwords", passwords) + +                pwlist = copy(self.getPasswords()) +                # remove already supplied pws from list (only local) +                for pw in passwords: +                    if pw in pwlist: +                        pwlist.remove(pw) + +                for pw in passwords + pwlist: +                    try: +                        self.logDebug("Try password", pw) +                        if plugin.checkPassword(pw): +                            plugin.extract(progress, pw) +                            self.addPassword(pw) +                            success = True +                            break +                    except WrongPassword: +                        self.logDebug("Password was wrong") + +            if not success: +                raise Exception(_("Wrong password")) + +            if self.core.debug: +                self.logDebug("Would delete", ", ".join(plugin.getDeleteFiles())) + +            if deletearchive: +                files = plugin.getDeleteFiles() +                self.logInfo(_("Deleting %s files") % len(files)) +                for f in files: +                    if exists(f): +                        remove(f) +                    else: +                        self.logDebug("%s does not exists" % f) + +            self.logInfo(basename(plugin.file), _("Extracting finished")) + +            extracted_files = plugin.getExtractedFiles() +            self.manager.dispatchEvent("archive-extracted", pyfile, plugin.out, plugin.file, extracted_files) + +            return extracted_files + +        except ArchiveError, e: +            self.logError(basename(plugin.file), _("Archive Error"), e) +        except CRCError: +            self.logError(basename(plugin.file), _("CRC Mismatch")) +        except Exception, e: +            if self.core.debug: +                print_exc() +            self.logError(basename(plugin.file), _("Unknown Error"), e) + +        self.manager.dispatchEvent("archive-extract_failed", pyfile) +        raise Exception(_("Extract failed")) + + +    @Expose +    def getPasswords(self): +        """ List of saved passwords """ +        return self.passwords + + +    def reloadPasswords(self): +        passwordfile = self.getConfig("passwordfile") + +        try: +            passwords = [] +            with open(passwordfile, "a+") as f: +                for pw in f.read().splitlines(): +                    passwords.append(pw) + +        except IOError, e: +            self.logError(e) + +        else: +            self.passwords = passwords + + +    @Expose +    def addPassword(self, pw): +        """  Adds a password to saved list""" +        passwordfile = self.getConfig("passwordfile") + +        if pw in self.passwords: +            self.passwords.remove(pw) + +        self.passwords.insert(0, pw) + +        try: +            with open(passwordfile, "wb") as f: +                for pw in self.passwords: +                    f.write(pw + "\n") +        except IOError, e: +            self.logError(e) + + +    def setPermissions(self, files): +        for f in files: +            if not exists(f): +                continue +            try: +                if self.config['permission']['change_file']: +                    if isfile(f): +                        chmod(f, int(self.config['permission']['file'], 8)) +                    elif isdir(f): +                        chmod(f, int(self.config['permission']['folder'], 8)) + +                if self.config['permission']['change_dl'] and os.name != "nt": +                    uid = getpwnam(self.config['permission']['user'])[2] +                    gid = getgrnam(self.config['permission']['group'])[2] +                    chown(f, uid, gid) +            except Exception, e: +                self.logWarning(_("Setting User and Group failed"), e)  | 
