diff options
Diffstat (limited to 'module/plugins/internal')
-rw-r--r-- | module/plugins/internal/UnRar.py | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/module/plugins/internal/UnRar.py b/module/plugins/internal/UnRar.py new file mode 100644 index 000000000..8ff99b5e8 --- /dev/null +++ b/module/plugins/internal/UnRar.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- + +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + @author: mkaay +""" +from __future__ import with_statement + +import sys +import os +from os.path import exists, join, isabs, isdir +from os import remove, makedirs, rmdir, listdir, chmod +from traceback import print_exc + +from module.plugins.Hook import Hook +from module.lib.pyunrar import Unrar, WrongPasswordError, CommandError, UnknownError, LowRamError + +from module.utils import save_join + +if os.name != "nt": + from pwd import getpwnam + from os import chown + +import re + +class UnRar(Hook): + __name__ = "UnRar" + __version__ = "0.1" + __description__ = """Unrar plugin for archive extractor""" + __config__ = [("activated", "bool", "Activated", False), + ("fullpath", "bool", "extract full path", True), + ("overwrite", "bool", "overwrite files", True), + ("passwordfile", "str", "unrar password file", "unrar_passwords.txt"), + ("deletearchive", "bool", "delete archives when done", False), + ("ramwarning", "bool", "warn about low ram", True), + ("renice", "int", "Cpu Priority", 10), + ("unrar_destination", "str", "Unpack files to", "")] + __threaded__ = ["packageFinished"] + __author_name__ = ("mkaay") + __author_mail__ = ("mkaay@mkaay.de") + + def setup(self): + self.comments = ["# one password each line"] + self.passwords = [] + if exists(self.getConfig("passwordfile")): + with open(self.getConfig("passwordfile"), "r") as f: + for l in f.readlines(): + l = l.strip("\n\r") + if l and not l.startswith("#"): + self.passwords.append(l) + else: + with open(self.getConfig("passwordfile"), "w") as f: + f.writelines(self.comments) + self.re_splitfile = re.compile("(.*)\.part(\d+)\.rar$") + + self.ram = 0 #ram in kb for unix osses + try: + f = open("/proc/meminfo") + line = True + while line: + line = f.readline() + if line.startswith("MemTotal:"): + self.ram = int(re.search(r"([0-9]+)", line).group(1)) + except: + self.ram = 0 + + self.ram /= 1024 + + def setOwner(self, d, uid, gid, mode): + if not exists(d): + self.core.log.debug(_("Directory %s does not exist!") % d) + return + + for fileEntry in listdir(d): + fullEntryName = join(d, fileEntry) + if isdir(fullEntryName): + self.setOwner(fullEntryName, uid, gid, mode) + try: + chown(fullEntryName, uid, gid) + chmod(fullEntryName, mode) + except: + self.core.log.debug(_("Chown/Chmod for %s failed") % fullEntryName) + self.core.log.debug(_("Exception: %s") % sys.exc_info()[0]) + continue + try: + chown(d, uid, gid) + chmod(d, mode) + except: + self.core.log.debug(_("Chown/Chmod for %s failed") % d) + self.core.log.debug(_("Exception: %s") % sys.exc_info()[0]) + return + + def addPassword(self, pws): + if not type(pws) == list: pws = [pws] + pws.reverse() + for pw in pws: + pw = pw.strip() + if not pw or pw == "None" or pw in self.passwords: continue + self.passwords.insert(0, pw) + + with open(self.getConfig("passwordfile"), "w") as f: + f.writelines([c + "\n" for c in self.comments]) + f.writelines([p + "\n" for p in self.passwords]) + + def removeFiles(self, pack, fname): + if not self.getConfig("deletearchive"): + return + m = self.re_splitfile.search(fname) + + download_folder = self.core.config['general']['download_folder'] + if self.core.config['general']['folder_per_package']: + folder = join(download_folder, pack.folder.decode(sys.getfilesystemencoding())) + else: + folder = download_folder + if m: + nre = re.compile("%s\.part\d+\.rar" % m.group(1)) + for fid, data in pack.getChildren().iteritems(): + if nre.match(data["name"]): + remove(join(folder, data["name"])) + elif not m and fname.endswith(".rar"): + nre = re.compile("^%s\.r..$" % fname.replace(".rar", "")) + for fid, data in pack.getChildren().iteritems(): + if nre.match(data["name"]): + remove(join(folder, data["name"])) + + def packageFinished(self, pack): + if pack.password and pack.password.strip() and pack.password.strip() != "None": + self.addPassword(pack.password.splitlines()) + files = [] + + for fid, data in pack.getChildren().iteritems(): + m = self.re_splitfile.search(data["name"]) + if m and int(m.group(2)) == 1: + files.append((fid, m.group(0))) + elif not m and data["name"].endswith(".rar"): + files.append((fid, data["name"])) + + for fid, fname in files: + self.core.log.info(_("starting Unrar of %s") % fname) + pyfile = self.core.files.getFile(fid) + pyfile.setStatus("processing") + + def s(p): + pyfile.setProgress(p) + + download_folder = self.core.config['general']['download_folder'] + self.core.log.debug(_("download folder %s") % download_folder) + + folder = save_join(download_folder, pack.folder, "") + + + destination = folder + if self.getConfig("unrar_destination") and not self.getConfig("unrar_destination").lower() == "none": + destination = self.getConfig("unrar_destination") + sub = "." + if self.core.config['general']['folder_per_package']: + sub = pack.folder.decode(sys.getfilesystemencoding()) + if isabs(destination): + destination = join(destination, sub, "") + else: + destination = join(folder, destination, sub, "") + + self.core.log.debug(_("Destination folder %s") % destination) + if not exists(destination): + self.core.log.info(_("Creating destination folder %s") % destination) + makedirs(destination) + + u = Unrar(join(folder, fname), tmpdir=join(folder, "tmp"), + ramSize=(self.ram if self.getConfig("ramwarning") else 0), cpu=self.getConfig("renice")) + try: + success = u.crackPassword(passwords=self.passwords, statusFunction=s, overwrite=True, + destination=destination, fullPath=self.getConfig("fullpath")) + except WrongPasswordError: + self.core.log.info(_("Unrar of %s failed (wrong password)") % fname) + continue + except CommandError, e: + if self.core.debug: + print_exc() + if re.search("Cannot find volume", e.stderr): + self.core.log.info(_("Unrar of %s failed (missing volume)") % fname) + continue + try: + if e.getExitCode() == 1 and len(u.listContent(u.getPassword())) == 1: + self.core.log.info(_("Unrar of %s ok") % fname) + self.removeFiles(pack, fname) + except: + if self.core.debug: + print_exc() + self.core.log.info(_("Unrar of %s failed") % fname) + continue + except LowRamError: + self.log.warning(_( + "Your ram amount of %s MB seems not sufficient to unrar this file. You can deactivate this warning and risk instability") % self.ram) + continue + except UnknownError: + if self.core.debug: + print_exc() + self.core.log.info(_("Unrar of %s failed") % fname) + continue + else: + if success: + self.core.log.info(_("Unrar of %s ok") % fname) + self.removeFiles(pack, fname) + + if os.name != "nt" and self.core.config['permission']['change_dl'] and\ + self.core.config['permission']['change_file']: + ownerUser = self.core.config['permission']['user'] + fileMode = int(self.core.config['permission']['file'], 8) + + self.core.log.debug("Setting destination file/directory owner / mode to %s / %s" + % (ownerUser, fileMode)) + + uinfo = getpwnam(ownerUser) + self.core.log.debug("Uid/Gid is %s/%s." % (uinfo.pw_uid, uinfo.pw_gid)) + self.setOwner(destination, uinfo.pw_uid, uinfo.pw_gid, fileMode) + self.core.log.debug("The owner/rights have been successfully changed.") + + self.core.hookManager.unrarFinished(folder, fname) + else: + self.core.log.info(_("Unrar of %s failed (wrong password or bad parts)") % fname) + finally: + pyfile.setProgress(100) + pyfile.setStatus("finished") + pyfile.release() + |