diff options
Diffstat (limited to 'pyload/plugins/hooks')
44 files changed, 3906 insertions, 0 deletions
| diff --git a/pyload/plugins/hooks/AlldebridCom.py b/pyload/plugins/hooks/AlldebridCom.py new file mode 100644 index 000000000..8eade2941 --- /dev/null +++ b/pyload/plugins/hooks/AlldebridCom.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class AlldebridCom(MultiHoster): +    __name__ = "AlldebridCom" +    __type__ = "hook" +    __version__ = "0.13" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("https", "bool", "Enable HTTPS", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Alldebrid.com hook plugin""" +    __author_name__ = "Andy Voigt" +    __author_mail__ = "spamsales@online.de" + + +    def getHoster(self): +        https = "https" if self.getConfig("https") else "http" +        page = getURL(https + "://www.alldebrid.com/api.php?action=get_host").replace("\"", "").strip() + +        return [x.strip() for x in page.split(",") if x.strip()] diff --git a/pyload/plugins/hooks/BypassCaptcha.py b/pyload/plugins/hooks/BypassCaptcha.py new file mode 100644 index 000000000..0f16d0b06 --- /dev/null +++ b/pyload/plugins/hooks/BypassCaptcha.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- + +from pycurl import FORM_FILE, LOW_SPEED_TIME +from thread import start_new_thread + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugins.Hook import Hook + + +class BypassCaptchaException(Exception): + +    def __init__(self, err): +        self.err = err + +    def getCode(self): +        return self.err + +    def __str__(self): +        return "<BypassCaptchaException %s>" % self.err + +    def __repr__(self): +        return "<BypassCaptchaException %s>" % self.err + + +class BypassCaptcha(Hook): +    __name__ = "BypassCaptcha" +    __type__ = "hook" +    __version__ = "0.04" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("force", "bool", "Force BC even if client is connected", False), +                  ("passkey", "password", "Passkey", "")] + +    __description__ = """Send captchas to BypassCaptcha.com""" +    __author_name__ = ("RaNaN", "Godofdream", "zoidberg") +    __author_mail__ = ("RaNaN@pyload.org", "soilfcition@gmail.com", "zoidberg@mujmail.cz") + +    PYLOAD_KEY = "4f771155b640970d5607f919a615bdefc67e7d32" + +    SUBMIT_URL = "http://bypasscaptcha.com/upload.php" +    RESPOND_URL = "http://bypasscaptcha.com/check_value.php" +    GETCREDITS_URL = "http://bypasscaptcha.com/ex_left.php" + + +    def setup(self): +        self.info = {} + +    def getCredits(self): +        response = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")}) + +        data = dict([x.split(' ', 1) for x in response.splitlines()]) +        return int(data['Left']) + +    def submit(self, captcha, captchaType="file", match=None): +        req = getRequest() + +        #raise timeout threshold +        req.c.setopt(LOW_SPEED_TIME, 80) + +        try: +            response = req.load(self.SUBMIT_URL, +                                post={"vendor_key": self.PYLOAD_KEY, +                                      "key": self.getConfig("passkey"), +                                      "gen_task_id": "1", +                                      "file": (FORM_FILE, captcha)}, +                                multipart=True) +        finally: +            req.close() + +        data = dict([x.split(' ', 1) for x in response.splitlines()]) +        if not data or "Value" not in data: +            raise BypassCaptchaException(response) + +        result = data['Value'] +        ticket = data['TaskId'] +        self.logDebug("Result %s : %s" % (ticket, result)) + +        return ticket, result + +    def respond(self, ticket, success): +        try: +            response = getURL(self.RESPOND_URL, post={"task_id": ticket, "key": self.getConfig("passkey"), +                                                      "cv": 1 if success else 0}) +        except BadHeader, e: +            self.logError(_("Could not send response."), e + +    def newCaptchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig("passkey"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(100) +            start_new_thread(self.processCaptcha, (task,)) + +        else: +            self.logInfo(_("Your %s account has not enough credits") % self.__name__) + +    def captchaCorrect(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            self.respond(task.data['ticket'], True) + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            self.respond(task.data['ticket'], False) + +    def processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except BypassCaptchaException, e: +            task.error = e.getCode() +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugins/hooks/Captcha9kw.py b/pyload/plugins/hooks/Captcha9kw.py new file mode 100644 index 000000000..f8de28710 --- /dev/null +++ b/pyload/plugins/hooks/Captcha9kw.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import time + +from base64 import b64encode +from thread import start_new_thread + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL +from pyload.plugins.Hook import Hook + + +class Captcha9kw(Hook): +    __name__ = "Captcha9kw" +    __type__ = "hook" +    __version__ = "0.09" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("force", "bool", "Force CT even if client is connected", True), +                  ("https", "bool", "Enable HTTPS", False), +                  ("confirm", "bool", "Confirm Captcha (Cost +6)", False), +                  ("captchaperhour", "int", "Captcha per hour (max. 9999)", 9999), +                  ("prio", "int", "Prio 1-10 (Cost +1-10)", 0), +                  ("selfsolve", "bool", +                   "If enabled and you have a 9kw client active only you will get your captcha to solve it (Selfsolve)", +                   False), +                  ("timeout", "int", "Timeout (max. 300)", 300), +                  ("passkey", "password", "API key", "")] + +    __description__ = """Send captchas to 9kw.eu""" +    __author_name__ = "RaNaN" +    __author_mail__ = "RaNaN@pyload.org" + +    API_URL = "://www.9kw.eu/index.cgi" + + +    def setup(self): +        self.API_URL = "https" + self.API_URL if self.getConfig("https") else "http" + self.API_URL +        self.info = {} + +    def getCredits(self): +        response = getURL(self.API_URL, get={"apikey": self.getConfig("passkey"), "pyload": "1", "source": "pyload", +                                             "action": "usercaptchaguthaben"}) + +        if response.isdigit(): +            self.logInfo(_("%s credits left") % response) +            self.info['credits'] = credits = int(response) +            return credits +        else: +            self.logError(response) +            return 0 + +    def processCaptcha(self, task): +        result = None + +        with open(task.captchaFile, 'rb') as f: +            data = f.read() +        data = b64encode(data) +        self.logDebug(task.captchaFile, data) +        if task.isPositional(): +            mouse = 1 +        else: +            mouse = 0 + +        response = getURL(self.API_URL, post={ +            "apikey": self.getConfig("passkey"), +            "prio": self.getConfig("prio"), +            "confirm": self.getConfig("confirm"), +            "captchaperhour": self.getConfig("captchaperhour"), +            "maxtimeout": self.getConfig("timeout"), +            "selfsolve": self.getConfig("selfsolve"), +            "pyload": "1", +            "source": "pyload", +            "base64": "1", +            "mouse": mouse, +            "file-upload-01": data, +            "action": "usercaptchaupload"}) + +        if response.isdigit(): +            self.logInfo(_("New CaptchaID from upload: %s : %s") % (response, task.captchaFile)) + +            for _ in xrange(1, 100, 1): +                response2 = getURL(self.API_URL, get={"apikey": self.getConfig("passkey"), "id": response, +                                                      "pyload": "1", "source": "pyload", +                                                      "action": "usercaptchacorrectdata"}) + +                if response2 != "": +                    break + +                time.sleep(3) + +            result = response2 +            task.data['ticket'] = response +            self.logInfo(_("Result %s : %s") % (response, result)) +            task.setResult(result) +        else: +            self.logError(_("Bad upload"), response) +            return False + +    def newCaptchaTask(self, task): +        if not task.isTextual() and not task.isPositional(): +            return False + +        if not self.getConfig("passkey"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.setWaiting(self.getConfig("timeout")) +            start_new_thread(self.processCaptcha, (task,)) + +        else: +            self.logError(_("Your Captcha 9kw.eu Account has not enough credits")) + +    def captchaCorrect(self, task): +        if "ticket" in task.data: + +            try: +                response = getURL(self.API_URL, +                                  post={"action": "usercaptchacorrectback", +                                        "apikey": self.getConfig("passkey"), +                                        "api_key": self.getConfig("passkey"), +                                        "correct": "1", +                                        "pyload": "1", +                                        "source": "pyload", +                                        "id": task.data['ticket']}) +                self.logInfo(_("Request correct", response) + +            except BadHeader, e: +                self.logError(_("Could not send correct request."), e) +        else: +            self.logError(_("No CaptchaID for correct request (task %s) found.") % task) + +    def captchaInvalid(self, task): +        if "ticket" in task.data: + +            try: +                response = getURL(self.API_URL, +                                  post={"action": "usercaptchacorrectback", +                                        "apikey": self.getConfig("passkey"), +                                        "api_key": self.getConfig("passkey"), +                                        "correct": "2", +                                        "pyload": "1", +                                        "source": "pyload", +                                        "id": task.data['ticket']}) +                self.logInfo(_("Request refund", response) + +            except BadHeader, e: +                self.logError(_("Could not send refund request."), e) +        else: +            self.logError(_("No CaptchaID for not correct request (task %s) found.") % task) diff --git a/pyload/plugins/hooks/CaptchaBrotherhood.py b/pyload/plugins/hooks/CaptchaBrotherhood.py new file mode 100644 index 000000000..478a08cc5 --- /dev/null +++ b/pyload/plugins/hooks/CaptchaBrotherhood.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import StringIO +import pycurl + +from PIL import Image +from thread import start_new_thread +from time import sleep +from urllib import urlencode + +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugins.Hook import Hook + + +class CaptchaBrotherhoodException(Exception): + +    def __init__(self, err): +        self.err = err + +    def getCode(self): +        return self.err + +    def __str__(self): +        return "<CaptchaBrotherhoodException %s>" % self.err + +    def __repr__(self): +        return "<CaptchaBrotherhoodException %s>" % self.err + + +class CaptchaBrotherhood(Hook): +    __name__ = "CaptchaBrotherhood" +    __type__ = "hook" +    __version__ = "0.05" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("username", "str", "Username", ""), +                  ("force", "bool", "Force CT even if client is connected", False), +                  ("passkey", "password", "Password", "")] + +    __description__ = """Send captchas to CaptchaBrotherhood.com""" +    __author_name__ = ("RaNaN", "zoidberg") +    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    API_URL = "http://www.captchabrotherhood.com/" + + +    def setup(self): +        self.info = {} + +    def getCredits(self): +        response = getURL(self.API_URL + "askCredits.aspx", +                          get={"username": self.getConfig("username"), "password": self.getConfig("passkey")}) +        if not response.startswith("OK"): +            raise CaptchaBrotherhoodException(response) +        else: +            credits = int(response[3:]) +            self.logInfo(_("%d credits left") % credits) +            self.info['credits'] = credits +            return credits + +    def submit(self, captcha, captchaType="file", match=None): +        try: +            img = Image.open(captcha) +            output = StringIO.StringIO() +            self.logDebug("CAPTCHA IMAGE", img, img.format, img.mode) +            if img.format in ("GIF", "JPEG"): +                img.save(output, img.format) +            else: +                if img.mode != "RGB": +                    img = img.convert("RGB") +                img.save(output, "JPEG") +            data = output.getvalue() +            output.close() +        except Exception, e: +            raise CaptchaBrotherhoodException("Reading or converting captcha image failed: %s" % e) + +        req = getRequest() + +        url = "%ssendNewCaptcha.aspx?%s" % (self.API_URL, +                                            urlencode({"username": self.getConfig("username"), +                                                       "password": self.getConfig("passkey"), +                                                       "captchaSource": "pyLoad", +                                                       "timeout": "80"})) + +        req.c.setopt(pycurl.URL, url) +        req.c.setopt(pycurl.POST, 1) +        req.c.setopt(pycurl.POSTFIELDS, data) +        req.c.setopt(pycurl.HTTPHEADER, ["Content-Type: text/html"]) + +        try: +            req.c.perform() +            response = req.getResponse() +        except Exception, e: +            raise CaptchaBrotherhoodException("Submit captcha image failed") + +        req.close() + +        if not response.startswith("OK"): +            raise CaptchaBrotherhoodException(response[1]) + +        ticket = response[3:] + +        for _ in xrange(15): +            sleep(5) +            response = self.get_api("askCaptchaResult", ticket) +            if response.startswith("OK-answered"): +                return ticket, response[12:] + +        raise CaptchaBrotherhoodException("No solution received in time") + +    def get_api(self, api, ticket): +        response = getURL("%s%s.aspx" % (self.API_URL, api), +                          get={"username": self.getConfig("username"), +                               "password": self.getConfig("passkey"), +                               "captchaID": ticket}) +        if not response.startswith("OK"): +            raise CaptchaBrotherhoodException("Unknown response: %s" % response) + +        return response + +    def newCaptchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig("username") or not self.getConfig("passkey"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        if self.getCredits() > 10: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(100) +            start_new_thread(self.processCaptcha, (task,)) +        else: +            self.logInfo(_("Your CaptchaBrotherhood Account has not enough credits")) + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            response = self.get_api("complainCaptcha", task.data['ticket']) + +    def processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except CaptchaBrotherhoodException, e: +            task.error = e.getCode() +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugins/hooks/Checksum.py b/pyload/plugins/hooks/Checksum.py new file mode 100644 index 000000000..31d0cbf8c --- /dev/null +++ b/pyload/plugins/hooks/Checksum.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import hashlib +import re +import zlib + +from os import remove +from os.path import getsize, isfile, splitext + +from pyload.plugins.Hook import Hook +from pyload.utils import safe_join, fs_encode + + +def computeChecksum(local_file, algorithm): +    if algorithm in getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")): +        h = getattr(hashlib, algorithm)() + +        with open(local_file, 'rb') as f: +            for chunk in iter(lambda: f.read(128 * h.block_size), b''): +                h.update(chunk) + +        return h.hexdigest() + +    elif algorithm in ("adler32", "crc32"): +        hf = getattr(zlib, algorithm) +        last = 0 + +        with open(local_file, 'rb') as f: +            for chunk in iter(lambda: f.read(8192), b''): +                last = hf(chunk, last) + +        return "%x" % last + +    else: +        return None + + +class Checksum(Hook): +    __name__ = "Checksum" +    __type__ = "hook" +    __version__ = "0.13" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("check_checksum", "bool", "Check checksum? (If False only size will be verified)", True), +                  ("check_action", "fail;retry;nothing", "What to do if check fails?", "retry"), +                  ("max_tries", "int", "Number of retries", 2), +                  ("retry_action", "fail;nothing", "What to do if all retries fail?", "fail"), +                  ("wait_time", "int", "Time to wait before each retry (seconds)", 1)] + +    __description__ = """Verify downloaded file size and checksum""" +    __author_name__ = ("zoidberg", "Walter Purcaro", "stickell") +    __author_mail__ = ("zoidberg@mujmail.cz", "vuolter@gmail.com", "l.stickell@yahoo.it") + +    methods = {'sfv': 'crc32', 'crc': 'crc32', 'hash': 'md5'} +    regexps = {'sfv': r'^(?P<name>[^;].+)\s+(?P<hash>[0-9A-Fa-f]{8})$', +               'md5': r'^(?P<name>[0-9A-Fa-f]{32})  (?P<file>.+)$', +               'crc': r'filename=(?P<name>.+)\nsize=(?P<size>\d+)\ncrc32=(?P<hash>[0-9A-Fa-f]{8})$', +               'default': r'^(?P<hash>[0-9A-Fa-f]+)\s+\*?(?P<name>.+)$'} + + +    def coreReady(self): +        if not self.getConfig("check_checksum"): +            self.logInfo(_("Checksum validation is disabled in plugin configuration")) + +    def setup(self): +        self.algorithms = sorted( +            getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")), reverse=True) +        self.algorithms.extend(["crc32", "adler32"]) +        self.formats = self.algorithms + ["sfv", "crc", "hash"] + +    def downloadFinished(self, pyfile): +        """ +        Compute checksum for the downloaded file and compare it with the hash provided by the hoster. +        pyfile.plugin.check_data should be a dictionary which can contain: +        a) if known, the exact filesize in bytes (e.g. "size": 123456789) +        b) hexadecimal hash string with algorithm name as key (e.g. "md5": "d76505d0869f9f928a17d42d66326307") +        """ +        if hasattr(pyfile.plugin, "check_data") and (isinstance(pyfile.plugin.check_data, dict)): +            data = pyfile.plugin.check_data.copy() +        elif hasattr(pyfile.plugin, "api_data") and (isinstance(pyfile.plugin.api_data, dict)): +            data = pyfile.plugin.api_data.copy() +        else: +            return + +        self.logDebug(data) + +        if not pyfile.plugin.lastDownload: +            self.checkFailed(pyfile, None, "No file downloaded") + +        local_file = fs_encode(pyfile.plugin.lastDownload) +        #download_folder = self.config['general']['download_folder'] +        #local_file = fs_encode(safe_join(download_folder, pyfile.package().folder, pyfile.name)) + +        if not isfile(local_file): +            self.checkFailed(pyfile, None, "File does not exist") + +            # validate file size +        if "size" in data: +            api_size = int(data['size']) +            file_size = getsize(local_file) +            if api_size != file_size: +                self.logWarning(_("File %s has incorrect size: %d B (%d expected)") % (pyfile.name, file_size, api_size)) +                self.checkFailed(pyfile, local_file, "Incorrect file size") +            del data['size'] + +        # validate checksum +        if data and self.getConfig("check_checksum"): +            if "checksum" in data: +                data['md5'] = data['checksum'] + +            for key in self.algorithms: +                if key in data: +                    checksum = computeChecksum(local_file, key.replace("-", "").lower()) +                    if checksum: +                        if checksum == data[key].lower(): +                            self.logInfo(_('File integrity of "%s" verified by %s checksum (%s).') % +                                        (pyfile.name, key.upper(), checksum)) +                            break +                        else: +                            self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % +                                           (key.upper(), pyfile.name, checksum, data[key])) +                            self.checkFailed(pyfile, local_file, "Checksums do not match") +                    else: +                        self.logWarning(_("Unsupported hashing algorithm"), key.upper()) +            else: +                self.logWarning(_("Unable to validate checksum for file"), pyfile.name) + +    def checkFailed(self, pyfile, local_file, msg): +        check_action = self.getConfig("check_action") +        if check_action == "retry": +            max_tries = self.getConfig("max_tries") +            retry_action = self.getConfig("retry_action") +            if pyfile.plugin.retries < max_tries: +                if local_file: +                    remove(local_file) +                pyfile.plugin.retry(max_tries=max_tries, wait_time=self.getConfig("wait_time"), reason=msg) +            elif retry_action == "nothing": +                return +        elif check_action == "nothing": +            return +        pyfile.plugin.fail(reason=msg) + +    def packageFinished(self, pypack): +        download_folder = safe_join(self.config['general']['download_folder'], pypack.folder, "") + +        for link in pypack.getChildren().itervalues(): +            file_type = splitext(link['name'])[1][1:].lower() + +            if file_type not in self.formats: +                continue + +            hash_file = fs_encode(safe_join(download_folder, link['name'])) +            if not isfile(hash_file): +                self.logWarning(_("File not found"), link['name']) +                continue + +            with open(hash_file) as f: +                text = f.read() + +            for m in re.finditer(self.regexps.get(file_type, self.regexps['default']), text): +                data = m.groupdict() +                self.logDebug(link['name'], data) + +                local_file = fs_encode(safe_join(download_folder, data['name'])) +                algorithm = self.methods.get(file_type, file_type) +                checksum = computeChecksum(local_file, algorithm) +                if checksum == data['hash']: +                    self.logInfo(_('File integrity of "%s" verified by %s checksum (%s).') % +                                (data['name'], algorithm, checksum)) +                else: +                    self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % +                                   (algorithm, data['name'], checksum, data['hash'])) diff --git a/pyload/plugins/hooks/ClickAndLoad.py b/pyload/plugins/hooks/ClickAndLoad.py new file mode 100644 index 000000000..501845840 --- /dev/null +++ b/pyload/plugins/hooks/ClickAndLoad.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +import socket +import thread + +from pyload.plugins.Hook import Hook + + +class ClickAndLoad(Hook): +    __name__ = "ClickAndLoad" +    __type__ = "hook" +    __version__ = "0.22" + +    __config__ = [("activated", "bool", "Activated", True), +                  ("extern", "bool", "Allow external link adding", False)] + +    __description__ = """Gives abillity to use jd's click and load. depends on webinterface""" +    __author_name__ = ("RaNaN", "mkaay") +    __author_mail__ = ("RaNaN@pyload.de", "mkaay@mkaay.de") + + +    def coreReady(self): +        self.port = int(self.config['webinterface']['port']) +        if self.config['webinterface']['activated']: +            try: +                if self.getConfig("extern"): +                    ip = "0.0.0.0" +                else: +                    ip = "127.0.0.1" + +                thread.start_new_thread(proxy, (self, ip, self.port, 9666)) +            except: +                self.logError(_("ClickAndLoad port already in use")) + + +def proxy(self, *settings): +    thread.start_new_thread(server, (self,) + settings) +    lock = thread.allocate_lock() +    lock.acquire() +    lock.acquire() + + +def server(self, *settings): +    try: +        dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +        dock_socket.bind((settings[0], settings[2])) +        dock_socket.listen(5) +        while True: +            client_socket = dock_socket.accept()[0] +            server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +            server_socket.connect(("127.0.0.1", settings[1])) +            thread.start_new_thread(forward, (client_socket, server_socket)) +            thread.start_new_thread(forward, (server_socket, client_socket)) +    except socket.error, e: +        if hasattr(e, "errno"): +            errno = e.errno +        else: +            errno = e.args[0] + +        if errno == 98: +            self.logWarning(_("Click'N'Load: Port 9666 already in use")) +            return +        thread.start_new_thread(server, (self,) + settings) +    except: +        thread.start_new_thread(server, (self,) + settings) + + +def forward(source, destination): +    string = ' ' +    while string: +        string = source.recv(1024) +        if string: +            destination.sendall(string) +        else: +            #source.shutdown(socket.SHUT_RD) +            destination.shutdown(socket.SHUT_WR) diff --git a/pyload/plugins/hooks/DeathByCaptcha.py b/pyload/plugins/hooks/DeathByCaptcha.py new file mode 100644 index 000000000..f2bae4848 --- /dev/null +++ b/pyload/plugins/hooks/DeathByCaptcha.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from base64 import b64encode +from pycurl import FORM_FILE, HTTPHEADER +from thread import start_new_thread +from time import sleep + +from pyload.utils import json_loads +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getRequest +from pyload.plugins.Hook import Hook + + +class DeathByCaptchaException(Exception): +    DBC_ERRORS = {'not-logged-in': 'Access denied, check your credentials', +                  'invalid-credentials': 'Access denied, check your credentials', +                  'banned': 'Access denied, account is suspended', +                  'insufficient-funds': 'Insufficient account balance to decrypt CAPTCHA', +                  'invalid-captcha': 'CAPTCHA is not a valid image', +                  'service-overload': 'CAPTCHA was rejected due to service overload, try again later', +                  'invalid-request': 'Invalid request', +                  'timed-out': 'No CAPTCHA solution received in time'} + +    def __init__(self, err): +        self.err = err + +    def getCode(self): +        return self.err + +    def getDesc(self): +        if self.err in self.DBC_ERRORS.keys(): +            return self.DBC_ERRORS[self.err] +        else: +            return self.err + +    def __str__(self): +        return "<DeathByCaptchaException %s>" % self.err + +    def __repr__(self): +        return "<DeathByCaptchaException %s>" % self.err + + +class DeathByCaptcha(Hook): +    __name__ = "DeathByCaptcha" +    __type__ = "hook" +    __version__ = "0.03" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("username", "str", "Username", ""), +                  ("passkey", "password", "Password", ""), +                  ("force", "bool", "Force DBC even if client is connected", False)] + +    __description__ = """Send captchas to DeathByCaptcha.com""" +    __author_name__ = ("RaNaN", "zoidberg") +    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    API_URL = "http://api.dbcapi.me/api/" + + +    def setup(self): +        self.info = {} + +    def call_api(self, api="captcha", post=False, multipart=False): +        req = getRequest() +        req.c.setopt(HTTPHEADER, ["Accept: application/json", "User-Agent: pyLoad %s" % self.core.version]) + +        if post: +            if not isinstance(post, dict): +                post = {} +            post.update({"username": self.getConfig("username"), +                         "password": self.getConfig("passkey")}) + +        response = None +        try: +            json = req.load("%s%s" % (self.API_URL, api), +                            post=post, +                            multipart=multipart) +            self.logDebug(json) +            response = json_loads(json) + +            if "error" in response: +                raise DeathByCaptchaException(response['error']) +            elif "status" not in response: +                raise DeathByCaptchaException(str(response)) + +        except BadHeader, e: +            if 403 == e.code: +                raise DeathByCaptchaException('not-logged-in') +            elif 413 == e.code: +                raise DeathByCaptchaException('invalid-captcha') +            elif 503 == e.code: +                raise DeathByCaptchaException('service-overload') +            elif e.code in (400, 405): +                raise DeathByCaptchaException('invalid-request') +            else: +                raise + +        finally: +            req.close() + +        return response + +    def getCredits(self): +        response = self.call_api("user", True) + +        if 'is_banned' in response and response['is_banned']: +            raise DeathByCaptchaException('banned') +        elif 'balance' in response and 'rate' in response: +            self.info.update(response) +        else: +            raise DeathByCaptchaException(response) + +    def getStatus(self): +        response = self.call_api("status", False) + +        if 'is_service_overloaded' in response and response['is_service_overloaded']: +            raise DeathByCaptchaException('service-overload') + +    def submit(self, captcha, captchaType="file", match=None): +        #workaround multipart-post bug in HTTPRequest.py +        if re.match("^[A-Za-z0-9]*$", self.getConfig("passkey")): +            multipart = True +            data = (FORM_FILE, captcha) +        else: +            multipart = False +            with open(captcha, 'rb') as f: +                data = f.read() +            data = "base64:" + b64encode(data) + +        response = self.call_api("captcha", {"captchafile": data}, multipart) + +        if "captcha" not in response: +            raise DeathByCaptchaException(response) +        ticket = response['captcha'] + +        for _ in xrange(24): +            sleep(5) +            response = self.call_api("captcha/%d" % ticket, False) +            if response['text'] and response['is_correct']: +                break +        else: +            raise DeathByCaptchaException('timed-out') + +        result = response['text'] +        self.logDebug("Result %s : %s" % (ticket, result)) + +        return ticket, result + +    def newCaptchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig("username") or not self.getConfig("passkey"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        try: +            self.getStatus() +            self.getCredits() +        except DeathByCaptchaException, e: +            self.logError(e.getDesc()) +            return False + +        balance, rate = self.info['balance'], self.info['rate'] +        self.logInfo(_("Account balance"), +                     _("US$%.3f (%d captchas left at %.2f cents each)") % (balance / 100, +                                                                           balance // rate, rate)) + +        if balance > rate: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(180) +            start_new_thread(self.processCaptcha, (task,)) + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            try: +                response = self.call_api("captcha/%d/report" % task.data['ticket'], True) +            except DeathByCaptchaException, e: +                self.logError(e.getDesc()) +            except Exception, e: +                self.logError(e) + +    def processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except DeathByCaptchaException, e: +            task.error = e.getCode() +            self.logError(e.getDesc()) +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugins/hooks/DebridItaliaCom.py b/pyload/plugins/hooks/DebridItaliaCom.py new file mode 100644 index 000000000..4272b758f --- /dev/null +++ b/pyload/plugins/hooks/DebridItaliaCom.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class DebridItaliaCom(MultiHoster): +    __name__ = "DebridItaliaCom" +    __type__ = "hook" +    __version__ = "0.07" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Debriditalia.com hook plugin""" +    __author_name__ = "stickell" +    __author_mail__ = "l.stickell@yahoo.it" + + +    def getHoster(self): +        return ["netload.in", "hotfile.com", "rapidshare.com", "multiupload.com", +                "uploading.com", "megashares.com", "crocko.com", "filepost.com", +                "bitshare.com", "share-links.biz", "putlocker.com", "uploaded.to", +                "speedload.org", "rapidgator.net", "likeupload.net", "cyberlocker.ch", +                "depositfiles.com", "extabit.com", "filefactory.com", "sharefiles.co", +                "ryushare.com", "tusfiles.net", "nowvideo.co", "cloudzer.net", "letitbit.net", +                "easybytez.com", "uptobox.com", "ddlstorage.com"] diff --git a/pyload/plugins/hooks/DeleteFinished.py b/pyload/plugins/hooks/DeleteFinished.py new file mode 100644 index 000000000..4b22c7fed --- /dev/null +++ b/pyload/plugins/hooks/DeleteFinished.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +from pyload.database import style +from pyload.plugins.Hook import Hook + + +class DeleteFinished(Hook): +    __name__ = "DeleteFinished" +    __type__ = "hook" +    __version__ = "1.09" + +    __config__ = [('activated', 'bool', 'Activated', 'False'), +                  ('interval', 'int', 'Delete every (hours)', '72'), +                  ('deloffline', 'bool', 'Delete packages with offline links', 'False')] + +    __description__ = """Automatically delete all finished packages from queue""" +    __author_name__ = "Walter Purcaro" +    __author_mail__ = "vuolter@gmail.com" + + +    ## overwritten methods ## +    def periodical(self): +        if not self.info['sleep']: +            deloffline = self.getConfig('deloffline') +            mode = '0,1,4' if deloffline else '0,4' +            msg = _('delete all finished packages in queue list (%s packages with offline links)') +            self.logInfo(msg % (_('including') if deloffline else _('excluding'))) +            self.deleteFinished(mode) +            self.info['sleep'] = True +            self.addEvent('packageFinished', self.wakeup) + +    def pluginConfigChanged(self, plugin, name, value): +        if name == 'interval' and value != self.interval: +            self.interval = value * 3600 +            self.initPeriodical() + +    def unload(self): +        self.removeEvent('packageFinished', self.wakeup) + +    def coreReady(self): +        self.info = {'sleep': True} +        interval = self.getConfig('interval') +        self.pluginConfigChanged('DeleteFinished', 'interval', interval) +        self.addEvent('packageFinished', self.wakeup) + +    ## own methods ## +    @style.queue +    def deleteFinished(self, mode): +        self.c.execute('DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE package=packages.id AND status NOT IN (%s))' % mode) +        self.c.execute('DELETE FROM links WHERE NOT EXISTS(SELECT 1 FROM packages WHERE id=links.package)') + +    def wakeup(self, pypack): +        self.removeEvent('packageFinished', self.wakeup) +        self.info['sleep'] = False + +    ## event managing ## +    def addEvent(self, event, func): +        """Adds an event listener for event name""" +        if event in self.m.events: +            if func in self.m.events[event]: +                self.logDebug("Function already registered", func) +            else: +                self.m.events[event].append(func) +        else: +            self.m.events[event] = [func] + +    def setup(self): +        self.m = self.manager +        self.removeEvent = self.m.removeEvent diff --git a/pyload/plugins/hooks/DownloadScheduler.py b/pyload/plugins/hooks/DownloadScheduler.py new file mode 100644 index 000000000..c5caee35d --- /dev/null +++ b/pyload/plugins/hooks/DownloadScheduler.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +import re + +from time import localtime + +from pyload.plugins.Hook import Hook + + +class DownloadScheduler(Hook): +    __name__ = "DownloadScheduler" +    __type__ = "hook" +    __version__ = "0.21" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("timetable", "str", "List time periods as hh:mm full or number(kB/s)", +                   "0:00 full, 7:00 250, 10:00 0, 17:00 150"), +                  ("abort", "bool", "Abort active downloads when start period with speed 0", False)] + +    __description__ = """Download Scheduler""" +    __author_name__ = ("zoidberg", "stickell") +    __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") + + +    def setup(self): +        self.cb = None  # callback to scheduler job; will be by removed hookmanager when hook unloaded + +    def coreReady(self): +        self.updateSchedule() + +    def updateSchedule(self, schedule=None): +        if schedule is None: +            schedule = self.getConfig("timetable") + +        schedule = re.findall("(\d{1,2}):(\d{2})[\s]*(-?\d+)", +                              schedule.lower().replace("full", "-1").replace("none", "0")) +        if not schedule: +            self.logError(_("Invalid schedule")) +            return + +        t0 = localtime() +        now = (t0.tm_hour, t0.tm_min, t0.tm_sec, "X") +        schedule = sorted([(int(x[0]), int(x[1]), 0, int(x[2])) for x in schedule] + [now]) + +        self.logDebug("Schedule", schedule) + +        for i, v in enumerate(schedule): +            if v[3] == "X": +                last, next = schedule[i - 1], schedule[(i + 1) % len(schedule)] +                self.logDebug("Now/Last/Next", now, last, next) + +                self.setDownloadSpeed(last[3]) + +                next_time = (((24 + next[0] - now[0]) * 60 + next[1] - now[1]) * 60 + next[2] - now[2]) % 86400 +                self.core.scheduler.removeJob(self.cb) +                self.cb = self.core.scheduler.addJob(next_time, self.updateSchedule, threaded=False) + +    def setDownloadSpeed(self, speed): +        if speed == 0: +            abort = self.getConfig("abort") +            self.logInfo(_("Stopping download server. (Running downloads will %sbe aborted.)") % '' if abort else _('not ')) +            self.core.api.pauseServer() +            if abort: +                self.core.api.stopAllDownloads() +        else: +            self.core.api.unpauseServer() + +            if speed > 0: +                self.logInfo(_("Setting download speed to %d kB/s") % speed) +                self.core.api.setConfigValue("download", "limit_speed", 1) +                self.core.api.setConfigValue("download", "max_speed", speed) +            else: +                self.logInfo(_("Setting download speed to FULL")) +                self.core.api.setConfigValue("download", "limit_speed", 0) +                self.core.api.setConfigValue("download", "max_speed", -1) diff --git a/pyload/plugins/hooks/EasybytezCom.py b/pyload/plugins/hooks/EasybytezCom.py new file mode 100644 index 000000000..9d1cdc0db --- /dev/null +++ b/pyload/plugins/hooks/EasybytezCom.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class EasybytezCom(MultiHoster): +    __name__ = "EasybytezCom" +    __type__ = "hook" +    __version__ = "0.03" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", "")] + +    __description__ = """EasyBytez.com hook plugin""" +    __author_name__ = "zoidberg" +    __author_mail__ = "zoidberg@mujmail.cz" + + +    def getHoster(self): +        self.account = self.core.accountManager.getAccountPlugin(self.__name__) +        user = self.account.selectAccount()[0] + +        try: +            req = self.account.getAccountRequest(user) +            page = req.load("http://www.easybytez.com") + +            m = re.search(r'</textarea>\s*Supported sites:(.*)', page) +            return m.group(1).split(',') +        except Exception, e: +            self.logDebug(e) +            self.logWarning(_("Unable to load supported hoster list, using last known")) +            return ["bitshare.com", "crocko.com", "ddlstorage.com", "depositfiles.com", "extabit.com", "hotfile.com", +                    "mediafire.com", "netload.in", "rapidgator.net", "rapidshare.com", "uploading.com", "uload.to", +                    "uploaded.to"] diff --git a/pyload/plugins/hooks/Ev0InFetcher.py b/pyload/plugins/hooks/Ev0InFetcher.py new file mode 100644 index 000000000..cd7314fc9 --- /dev/null +++ b/pyload/plugins/hooks/Ev0InFetcher.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +import feedparser + +from time import mktime, time + +from pyload.plugins.Hook import Hook + + +class Ev0InFetcher(Hook): +    __name__ = "Ev0InFetcher" +    __type__ = "hook" +    __version__ = "0.21" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("interval", "int", "Check interval in minutes", 10), +                  ("queue", "bool", "Move new shows directly to Queue", False), +                  ("shows", "str", "Shows to check for (comma seperated)", ""), +                  ("quality", "xvid;x264;rmvb", "Video Format", "xvid"), +                  ("hoster", "str", "Hoster to use (comma seperated)", +                   "NetloadIn,RapidshareCom,MegauploadCom,HotfileCom")] + +    __description__ = """Checks rss feeds for Ev0.in""" +    __author_name__ = "mkaay" +    __author_mail__ = "mkaay@mkaay.de" + + +    def setup(self): +        self.interval = self.getConfig("interval") * 60 + +    def filterLinks(self, links): +        results = self.core.pluginManager.parseUrls(links) +        sortedLinks = {} + +        for url, hoster in results: +            if hoster not in sortedLinks: +                sortedLinks[hoster] = [] +            sortedLinks[hoster].append(url) + +        for h in self.getConfig("hoster").split(","): +            try: +                return sortedLinks[h.strip()] +            except: +                continue +        return [] + + +    def periodical(self): + +        def normalizefiletitle(filename): +            filename = filename.replace('.', ' ') +            filename = filename.replace('_', ' ') +            filename = filename.lower() +            return filename + +        shows = [s.strip() for s in self.getConfig("shows").split(",")] + +        feed = feedparser.parse("http://feeds.feedburner.com/ev0in/%s?format=xml" % self.getConfig("quality")) + +        showStorage = {} +        for show in shows: +            showStorage[show] = int(self.getStorage("show_%s_lastfound" % show, 0)) + +        found = False +        for item in feed['items']: +            for show, lastfound in showStorage.iteritems(): +                if show.lower() in normalizefiletitle(item['title']) and lastfound < int(mktime(item.date_parsed)): +                    links = self.filterLinks(item['description'].split("<br />")) +                    packagename = item['title'].encode("utf-8") +                    self.logInfo(_("New episode '%s' (matched '%s')") % (packagename, show)) +                    self.core.api.addPackage(packagename, links, 1 if self.getConfig("queue") else 0) +                    self.setStorage("show_%s_lastfound" % show, int(mktime(item.date_parsed))) +                    found = True +        if not found: +            pass + +        for show, lastfound in self.getStorage().iteritems(): +            if int(lastfound) > 0 and int(lastfound) + (3600 * 24 * 30) < int(time()): +                self.delStorage("show_%s_lastfound" % show) +                self.logDebug("Cleaned '%s' record" % show) diff --git a/pyload/plugins/hooks/ExpertDecoders.py b/pyload/plugins/hooks/ExpertDecoders.py new file mode 100644 index 000000000..292c84b7c --- /dev/null +++ b/pyload/plugins/hooks/ExpertDecoders.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +from base64 import b64encode +from pycurl import LOW_SPEED_TIME +from thread import start_new_thread +from uuid import uuid4 + +from pyload.network.HTTPRequest import BadHeader +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugins.Hook import Hook + + +class ExpertDecoders(Hook): +    __name__ = "ExpertDecoders" +    __type__ = "hook" +    __version__ = "0.01" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("force", "bool", "Force CT even if client is connected", False), +                  ("passkey", "password", "Access key", "")] + +    __description__ = """Send captchas to expertdecoders.com""" +    __author_name__ = ("RaNaN", "zoidberg") +    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    API_URL = "http://www.fasttypers.org/imagepost.ashx" + + +    def setup(self): +        self.info = {} + +    def getCredits(self): +        response = getURL(self.API_URL, post={"key": self.getConfig("passkey"), "action": "balance"}) + +        if response.isdigit(): +            self.logInfo(_("%s credits left") % response) +            self.info['credits'] = credits = int(response) +            return credits +        else: +            self.logError(response) +            return 0 + +    def processCaptcha(self, task): +        task.data['ticket'] = ticket = uuid4() +        result = None + +        with open(task.captchaFile, 'rb') as f: +            data = f.read() +        data = b64encode(data) + +        req = getRequest() +        #raise timeout threshold +        req.c.setopt(LOW_SPEED_TIME, 80) + +        try: +            result = req.load(self.API_URL,  post={"action": "upload", "key": self.getConfig("passkey"), +                                                   "file": data, "gen_task_id": ticket}) +        finally: +            req.close() + +        self.logDebug("Result %s : %s" % (ticket, result)) +        task.setResult(result) + +    def newCaptchaTask(self, task): +        if not task.isTextual(): +            return False + +        if not self.getConfig("passkey"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.setWaiting(100) +            start_new_thread(self.processCaptcha, (task,)) + +        else: +            self.logInfo(_("Your ExpertDecoders Account has not enough credits")) + +    def captchaInvalid(self, task): +        if "ticket" in task.data: + +            try: +                response = getURL(self.API_URL, post={"action": "refund", "key": self.getConfig("passkey"), +                                                      "gen_task_id": task.data['ticket']}) +                self.logInfo(_("Request refund"), response) + +            except BadHeader, e: +                self.logError(_("Could not send refund request."), e) diff --git a/pyload/plugins/hooks/ExternalScripts.py b/pyload/plugins/hooks/ExternalScripts.py new file mode 100644 index 000000000..2e8dace14 --- /dev/null +++ b/pyload/plugins/hooks/ExternalScripts.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- + +import subprocess + +from itertools import chain +from os import listdir, access, X_OK, makedirs +from os.path import join, exists, basename, abspath + +from pyload.plugins.Hook import Hook +from pyload.utils import safe_join + + +class ExternalScripts(Hook): +    __name__ = "ExternalScripts" +    __type__ = "hook" +    __version__ = "0.24" + +    __config__ = [("activated", "bool", "Activated", True)] + +    __description__ = """Run external scripts""" +    __author_name__ = ("mkaay", "RaNaN", "spoob", "Walter Purcaro") +    __author_mail__ = ("mkaay@mkaay.de", "ranan@pyload.org", "spoob@pyload.org", "vuolter@gmail.com") + +    event_list = ["archive_extracted", "package_extracted", "all_archives_extracted", "all_archives_processed", +                  "allDownloadsFinished", "allDownloadsProcessed"] + + +    def setup(self): +        self.scripts = {} + +        folders = ["download_preparing", "download_finished", "all_downloads_finished", "all_downloads_processed", +                   "before_reconnect", "after_reconnect", +                   "package_finished", "package_extracted", +                   "archive_extracted", "all_archives_extracted", "all_archives_processed", +                   # deprecated folders +                   "unrar_finished", "all_dls_finished", "all_dls_processed"] + +        for folder in folders: +            self.scripts[folder] = [] + +            self.initPluginType(folder, join(pypath, 'scripts', folder)) +            self.initPluginType(folder, join('scripts', folder)) + +        for script_type, names in self.scripts.iteritems(): +            if names: +                self.logInfo(_("Installed scripts for"), script_type, ", ".join([basename(x) for x in names])) + + +    def initPluginType(self, folder, path): +        if not exists(path): +            try: +                makedirs(path) +            except: +                self.logDebug("Script folder %s not created" % folder) +                return + +        for f in listdir(path): +            if f.startswith("#") or f.startswith(".") or f.startswith("_") or f.endswith("~") or f.endswith(".swp"): +                continue + +            if not access(join(path, f), X_OK): +                self.logWarning(_("Script not executable:") + " %s/%s" % (folder, f)) + +            self.scripts[folder].append(join(path, f)) + + +    def callScript(self, script, *args): +        try: +            cmd = [script] + [str(x) if not isinstance(x, basestring) else x for x in args] +            self.logDebug("Executing", abspath(script), " ".join(cmd)) +            #output goes to pyload +            subprocess.Popen(cmd, bufsize=-1) +        except Exception, e: +            self.logError(_("Error in %(script)s: %(error)s") % {"script": basename(script), "error": e}) + + +    def downloadPreparing(self, pyfile): +        for script in self.scripts['download_preparing']: +            self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.id) + + +    def downloadFinished(self, pyfile): +        download_folder = self.config['general']['download_folder'] +        for script in self.scripts['download_finished']: +            filename = safe_join(download_folder, pyfile.package().folder, pyfile.name) +            self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, filename, pyfile.id) + + +    def packageFinished(self, pypack): +        download_folder = self.config['general']['download_folder'] +        for script in self.scripts['package_finished']: +            folder = safe_join(download_folder, pypack.folder) +            self.callScript(script, pypack.name, folder, pypack.password, pypack.id) + + +    def beforeReconnecting(self, ip): +        for script in self.scripts['before_reconnect']: +            self.callScript(script, ip) + + +    def afterReconnecting(self, ip): +        for script in self.scripts['after_reconnect']: +            self.callScript(script, ip) + + +    def archive_extracted(self, pyfile, folder, filename, files): +        for script in self.scripts['archive_extracted']: +            self.callScript(script, folder, filename, files) +        for script in self.scripts['unrar_finished']:  #: deprecated +            self.callScript(script, folder, filename) + + +    def package_extracted(self, pypack): +        download_folder = self.config['general']['download_folder'] +        for script in self.scripts['package_extracted']: +            folder = save_join(download_folder, pypack.folder) +            self.callScript(script, pypack.name, folder, pypack.password, pypack.id) + + +    def all_archives_extracted(self): +        for script in self.scripts['all_archives_extracted']: +            self.callScript(script) + + +    def all_archives_processed(self): +        for script in self.scripts['all_archives_processed']: +            self.callScript(script) + + +    def allDownloadsFinished(self): +        for script in chain(self.scripts['all_downloads_finished'], self.scripts['all_dls_finished']): +            self.callScript(script) + + +    def allDownloadsProcessed(self): +        for script in chain(self.scripts['all_downloads_processed'], self.scripts['all_dls_processed']): +            self.callScript(script) diff --git a/pyload/plugins/hooks/ExtractArchive.py b/pyload/plugins/hooks/ExtractArchive.py new file mode 100644 index 000000000..8d6f09172 --- /dev/null +++ b/pyload/plugins/hooks/ExtractArchive.py @@ -0,0 +1,351 @@ +# -*- coding: utf-8 -*- + +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.plugins.Hook import Hook, threaded, Expose +from pyload.plugins.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword +from pyload.utils import safe_join, fs_encode + + +class ExtractArchive(Hook): +    __name__ = "ExtractArchive" +    __type__ = "hook" +    __version__ = "0.17" + +    __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""" +    __author_name__ = ("RaNaN", "AndroKev", "Walter Purcaro") +    __author_mail__ = ("ranan@pyload.org", "@pyloadforum", "vuolter@gmail.com") + +    event_list = ["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") +        if not exists(passwordfile): +            open(passwordfile, "wb").close() + +        passwords = [] +        f = open(passwordfile, "rb") +        for pw in f.read().splitlines(): +            passwords.append(pw) +        f.close() + +        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) + +        f = open(passwordfile, "wb") +        for pw in self.passwords: +            f.write(pw + "\n") +        f.close() + + +    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) diff --git a/pyload/plugins/hooks/FastixRu.py b/pyload/plugins/hooks/FastixRu.py new file mode 100644 index 000000000..9e2abd92a --- /dev/null +++ b/pyload/plugins/hooks/FastixRu.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class FastixRu(MultiHoster): +    __name__ = "FastixRu" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Fastix.ru hook plugin""" +    __author_name__ = "Massimo Rosamilia" +    __author_mail__ = "max@spiritix.eu" + + +    def getHoster(self): +        page = getURL( +            "http://fastix.ru/api_v2/?apikey=5182964c3f8f9a7f0b00000a_kelmFB4n1IrnCDYuIFn2y&sub=allowed_sources") +        host_list = json_loads(page) +        host_list = host_list['allow'] +        return host_list diff --git a/pyload/plugins/hooks/FreeWayMe.py b/pyload/plugins/hooks/FreeWayMe.py new file mode 100644 index 000000000..635bc3415 --- /dev/null +++ b/pyload/plugins/hooks/FreeWayMe.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class FreeWayMe(MultiHoster): +    __name__ = "FreeWayMe" +    __type__ = "hook" +    __version__ = "0.11" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """FreeWay.me hook plugin""" +    __author_name__ = "Nicolas Giese" +    __author_mail__ = "james@free-way.me" + + +    def getHoster(self): +        hostis = getURL("https://www.free-way.me/ajax/jd.php", get={"id": 3}).replace("\"", "").strip() +        self.logDebug("Hosters", hostis) +        return [x.strip() for x in hostis.split(",") if x.strip()] diff --git a/pyload/plugins/hooks/HotFolder.py b/pyload/plugins/hooks/HotFolder.py new file mode 100644 index 000000000..f76e95af4 --- /dev/null +++ b/pyload/plugins/hooks/HotFolder.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- + +import time + +from os import listdir, makedirs +from os.path import exists, isfile, join +from shutil import move + +from pyload.plugins.Hook import Hook + + +class HotFolder(Hook): +    __name__ = "HotFolder" +    __type__ = "hook" +    __version__ = "0.11" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("folder", "str", "Folder to observe", "container"), +                  ("watch_file", "bool", "Observe link file", False), +                  ("keep", "bool", "Keep added containers", True), +                  ("file", "str", "Link file", "links.txt")] + +    __description__ = """Observe folder and file for changes and add container and links""" +    __author_name__ = "RaNaN" +    __author_mail__ = "RaNaN@pyload.de" + + +    def setup(self): +        self.interval = 10 + +    def periodical(self): +        if not exists(join(self.getConfig("folder"), "finished")): +            makedirs(join(self.getConfig("folder"), "finished")) + +        if self.getConfig("watch_file"): + +            if not exists(self.getConfig("file")): +                f = open(self.getConfig("file"), "wb") +                f.close() + +            f = open(self.getConfig("file"), "rb") +            content = f.read().strip() +            f.close() +            f = open(self.getConfig("file"), "wb") +            f.close() +            if content: +                name = "%s_%s.txt" % (self.getConfig("file"), time.strftime("%H-%M-%S_%d%b%Y")) + +                f = open(join(self.getConfig("folder"), "finished", name), "wb") +                f.write(content) +                f.close() + +                self.core.api.addPackage(f.name, [f.name], 1) + +        for f in listdir(self.getConfig("folder")): +            path = join(self.getConfig("folder"), f) + +            if not isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."): +                continue + +            newpath = join(self.getConfig("folder"), "finished", f if self.getConfig("keep") else "tmp_" + f) +            move(path, newpath) + +            self.logInfo(_("Added %s from HotFolder") % f) +            self.core.api.addPackage(f, [newpath], 1) diff --git a/pyload/plugins/hooks/IRCInterface.py b/pyload/plugins/hooks/IRCInterface.py new file mode 100644 index 000000000..99ac20acb --- /dev/null +++ b/pyload/plugins/hooks/IRCInterface.py @@ -0,0 +1,404 @@ +# -*- coding: utf-8 -*- + +import re +import socket +import time + +from pycurl import FORM_FILE +from select import select +from threading import Thread +from time import sleep +from traceback import print_exc + +from pyload.api import PackageDoesNotExists, FileDoesNotExists +from pyload.network.RequestFactory import getURL +from pyload.plugins.Hook import Hook +from pyload.utils import formatSize + + +class IRCInterface(Thread, Hook): +    __name__ = "IRCInterface" +    __type__ = "hook" +    __version__ = "0.11" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("host", "str", "IRC-Server Address", "Enter your server here!"), +                  ("port", "int", "IRC-Server Port", 6667), +                  ("ident", "str", "Clients ident", "pyload-irc"), +                  ("realname", "str", "Realname", "pyload-irc"), +                  ("nick", "str", "Nickname the Client will take", "pyLoad-IRC"), +                  ("owner", "str", "Nickname the Client will accept commands from", "Enter your nick here!"), +                  ("info_file", "bool", "Inform about every file finished", False), +                  ("info_pack", "bool", "Inform about every package finished", True), +                  ("captcha", "bool", "Send captcha requests", True)] + +    __description__ = """Connect to irc and let owner perform different tasks""" +    __author_name__ = "Jeix" +    __author_mail__ = "Jeix@hasnomail.com" + + +    def __init__(self, core, manager): +        Thread.__init__(self) +        Hook.__init__(self, core, manager) +        self.setDaemon(True) +        #   self.sm = core.server_methods +        self.api = core.api  # todo, only use api + +    def coreReady(self): +        self.abort = False +        self.more = [] +        self.new_package = {} + +        self.start() + +    def packageFinished(self, pypack): +        try: +            if self.getConfig("info_pack"): +                self.response(_("Package finished: %s") % pypack.name) +        except: +            pass + +    def downloadFinished(self, pyfile): +        try: +            if self.getConfig("info_file"): +                self.response( +                    _("Download finished: %(name)s @ %(plugin)s ") % {"name": pyfile.name, "plugin": pyfile.pluginname}) +        except: +            pass + +    def newCaptchaTask(self, task): +        if self.getConfig("captcha") and task.isTextual(): +            task.handler.append(self) +            task.setWaiting(60) + +            page = getURL("http://www.freeimagehosting.net/upload.php", +                          post={"attached": (FORM_FILE, task.captchaFile)}, multipart=True) + +            url = re.search(r"\[img\]([^\[]+)\[/img\]\[/url\]", page).group(1) +            self.response(_("New Captcha Request: %s") % url) +            self.response(_("Answer with 'c %s text on the captcha'") % task.id) + +    def run(self): +        # connect to IRC etc. +        self.sock = socket.socket() +        host = self.getConfig("host") +        self.sock.connect((host, self.getConfig("port"))) +        nick = self.getConfig("nick") +        self.sock.send("NICK %s\r\n" % nick) +        self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick)) +        for t in self.getConfig("owner").split(): +            if t.strip().startswith("#"): +                self.sock.send("JOIN %s\r\n" % t.strip()) +        self.logInfo(_("Connected to"), host) +        self.logInfo(_("Switching to listening mode!")) +        try: +            self.main_loop() + +        except IRCError, ex: +            self.sock.send("QUIT :byebye\r\n") +            print_exc() +            self.sock.close() + +    def main_loop(self): +        readbuffer = "" +        while True: +            sleep(1) +            fdset = select([self.sock], [], [], 0) +            if self.sock not in fdset[0]: +                continue + +            if self.abort: +                raise IRCError("quit") + +            readbuffer += self.sock.recv(1024) +            temp = readbuffer.split("\n") +            readbuffer = temp.pop() + +            for line in temp: +                line = line.rstrip() +                first = line.split() + +                if first[0] == "PING": +                    self.sock.send("PONG %s\r\n" % first[1]) + +                if first[0] == "ERROR": +                    raise IRCError(line) + +                msg = line.split(None, 3) +                if len(msg) < 4: +                    continue + +                msg = { +                    "origin": msg[0][1:], +                    "action": msg[1], +                    "target": msg[2], +                    "text": msg[3][1:] +                } + +                self.handle_events(msg) + +    def handle_events(self, msg): +        if not msg['origin'].split("!", 1)[0] in self.getConfig("owner").split(): +            return + +        if msg['target'].split("!", 1)[0] != self.getConfig("nick"): +            return + +        if msg['action'] != "PRIVMSG": +            return + +        # HANDLE CTCP ANTI FLOOD/BOT PROTECTION +        if msg['text'] == "\x01VERSION\x01": +            self.logDebug("Sending CTCP VERSION.") +            self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) +            return +        elif msg['text'] == "\x01TIME\x01": +            self.logDebug("Sending CTCP TIME.") +            self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) +            return +        elif msg['text'] == "\x01LAG\x01": +            self.logDebug("Received CTCP LAG.")  # don't know how to answer +            return + +        trigger = "pass" +        args = None + +        try: +            temp = msg['text'].split() +            trigger = temp[0] +            if len(temp) > 1: +                args = temp[1:] +        except: +            pass + +        handler = getattr(self, "event_%s" % trigger, self.event_pass) +        try: +            res = handler(args) +            for line in res: +                self.response(line, msg['origin']) +        except Exception, e: +            self.logError(repr(e)) + +    def response(self, msg, origin=""): +        if origin == "": +            for t in self.getConfig("owner").split(): +                self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg)) +        else: +            self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg)) + +        #### Events + +    def event_pass(self, args): +        return [] + +    def event_status(self, args): +        downloads = self.api.statusDownloads() +        if not downloads: +            return ["INFO: There are no active downloads currently."] + +        temp_progress = "" +        lines = ["ID - Name - Status - Speed - ETA - Progress"] +        for data in downloads: + +            if data.status == 5: +                temp_progress = data.format_wait +            else: +                temp_progress = "%d%% (%s)" % (data.percent, data.format_size) + +            lines.append("#%d - %s - %s - %s - %s - %s" % +                         ( +                             data.fid, +                             data.name, +                             data.statusmsg, +                             "%s/s" % formatSize(data.speed), +                             "%s" % data.format_eta, +                             temp_progress +                         )) +        return lines + +    def event_queue(self, args): +        ps = self.api.getQueueData() + +        if not ps: +            return ["INFO: There are no packages in queue."] + +        lines = [] +        for pack in ps: +            lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + +        return lines + +    def event_collector(self, args): +        ps = self.api.getCollectorData() +        if not ps: +            return ["INFO: No packages in collector!"] + +        lines = [] +        for pack in ps: +            lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links))) + +        return lines + +    def event_info(self, args): +        if not args: +            return ["ERROR: Use info like this: info <id>"] + +        info = None +        try: +            info = self.api.getFileData(int(args[0])) + +        except FileDoesNotExists: +            return ["ERROR: Link doesn't exists."] + +        return ['LINK #%s: %s (%s) [%s][%s]' % (info.fid, info.name, info.format_size, info.statusmsg, info.plugin)] + +    def event_packinfo(self, args): +        if not args: +            return ["ERROR: Use packinfo like this: packinfo <id>"] + +        lines = [] +        pack = None +        try: +            pack = self.api.getPackageData(int(args[0])) + +        except PackageDoesNotExists: +            return ["ERROR: Package doesn't exists."] + +        id = args[0] + +        self.more = [] + +        lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack.name, len(pack.links))) +        for pyfile in pack.links: +            self.more.append('LINK #%s: %s (%s) [%s][%s]' % (pyfile.fid, pyfile.name, pyfile.format_size, +                                                             pyfile.statusmsg, pyfile.plugin)) + +        if len(self.more) < 6: +            lines.extend(self.more) +            self.more = [] +        else: +            lines.extend(self.more[:6]) +            self.more = self.more[6:] +            lines.append("%d more links do display." % len(self.more)) + +        return lines + +    def event_more(self, args): +        if not self.more: +            return ["No more information to display."] + +        lines = self.more[:6] +        self.more = self.more[6:] +        lines.append("%d more links do display." % len(self.more)) + +        return lines + +    def event_start(self, args): +        self.api.unpauseServer() +        return ["INFO: Starting downloads."] + +    def event_stop(self, args): +        self.api.pauseServer() +        return ["INFO: No new downloads will be started."] + +    def event_add(self, args): +        if len(args) < 2: +            return ['ERROR: Add links like this: "add <packagename|id> links". ', +                    "This will add the link <link> to to the package <package> / the package with id <id>!"] + +        pack = args[0].strip() +        links = [x.strip() for x in args[1:]] + +        count_added = 0 +        count_failed = 0 +        try: +            id = int(pack) +            pack = self.api.getPackageData(id) +            if not pack: +                return ["ERROR: Package doesn't exists."] + +            #TODO add links + +            return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)] + +        except: +            # create new package +            id = self.api.addPackage(pack, links, 1) +            return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))] + +    def event_del(self, args): +        if len(args) < 2: +            return ["ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + +        if args[0] == "-p": +            ret = self.api.deletePackages(map(int, args[1:])) +            return ["INFO: Deleted %d packages!" % len(args[1:])] + +        elif args[0] == "-l": +            ret = self.api.delLinks(map(int, args[1:])) +            return ["INFO: Deleted %d links!" % len(args[1:])] + +        else: +            return ["ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + +    def event_push(self, args): +        if not args: +            return ["ERROR: Push package to queue like this: push <package id>"] + +        id = int(args[0]) +        try: +            info = self.api.getPackageInfo(id) +        except PackageDoesNotExists: +            return ["ERROR: Package #%d does not exist." % id] + +        self.api.pushToQueue(id) +        return ["INFO: Pushed package #%d to queue." % id] + +    def event_pull(self, args): +        if not args: +            return ["ERROR: Pull package from queue like this: pull <package id>."] + +        id = int(args[0]) +        if not self.api.getPackageData(id): +            return ["ERROR: Package #%d does not exist." % id] + +        self.api.pullFromQueue(id) +        return ["INFO: Pulled package #%d from queue to collector." % id] + +    def event_c(self, args): +        """ captcha answer """ +        if not args: +            return ["ERROR: Captcha ID missing."] + +        task = self.core.captchaManager.getTaskByID(args[0]) +        if not task: +            return ["ERROR: Captcha Task with ID %s does not exists." % args[0]] + +        task.setResult(" ".join(args[1:])) +        return ["INFO: Result %s saved." % " ".join(args[1:])] + +    def event_help(self, args): +        lines = ["The following commands are available:", +                 "add <package|packid> <links> [...] Adds link to package. (creates new package if it does not exist)", +                 "queue                       Shows all packages in the queue", +                 "collector                   Shows all packages in collector", +                 "del -p|-l <id> [...]        Deletes all packages|links with the ids specified", +                 "info <id>                   Shows info of the link with id <id>", +                 "packinfo <id>               Shows info of the package with id <id>", +                 "more                        Shows more info when the result was truncated", +                 "start                       Starts all downloads", +                 "stop                        Stops the download (but not abort active downloads)", +                 "push <id>                   Push package to queue", +                 "pull <id>                   Pull package from queue", +                 "status                      Show general download status", +                 "help                        Shows this help message"] +        return lines + + +class IRCError(Exception): + +    def __init__(self, value): +        self.value = value + +    def __str__(self): +        return repr(self.value) diff --git a/pyload/plugins/hooks/ImageTyperz.py b/pyload/plugins/hooks/ImageTyperz.py new file mode 100644 index 000000000..8d2fb2006 --- /dev/null +++ b/pyload/plugins/hooks/ImageTyperz.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement + +import re + +from base64 import b64encode +from pycurl import FORM_FILE, LOW_SPEED_TIME +from thread import start_new_thread + +from pyload.network.RequestFactory import getURL, getRequest +from pyload.plugins.Hook import Hook + + +class ImageTyperzException(Exception): + +    def __init__(self, err): +        self.err = err + +    def getCode(self): +        return self.err + +    def __str__(self): +        return "<ImageTyperzException %s>" % self.err + +    def __repr__(self): +        return "<ImageTyperzException %s>" % self.err + + +class ImageTyperz(Hook): +    __name__ = "ImageTyperz" +    __type__ = "hook" +    __version__ = "0.04" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("username", "str", "Username", ""), +                  ("passkey", "password", "Password", ""), +                  ("force", "bool", "Force IT even if client is connected", False)] + +    __description__ = """Send captchas to ImageTyperz.com""" +    __author_name__ = ("RaNaN", "zoidberg") +    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + +    SUBMIT_URL = "http://captchatypers.com/Forms/UploadFileAndGetTextNEW.ashx" +    RESPOND_URL = "http://captchatypers.com/Forms/SetBadImage.ashx" +    GETCREDITS_URL = "http://captchatypers.com/Forms/RequestBalance.ashx" + + +    def setup(self): +        self.info = {} + +    def getCredits(self): +        response = getURL(self.GETCREDITS_URL, post={"action": "REQUESTBALANCE", "username": self.getConfig("username"), +                                                     "password": self.getConfig("passkey")}) + +        if response.startswith('ERROR'): +            raise ImageTyperzException(response) + +        try: +            balance = float(response) +        except: +            raise ImageTyperzException("invalid response") + +        self.logInfo(_("Account balance: $%s left") % response) +        return balance + +    def submit(self, captcha, captchaType="file", match=None): +        req = getRequest() +        #raise timeout threshold +        req.c.setopt(LOW_SPEED_TIME, 80) + +        try: +            #workaround multipart-post bug in HTTPRequest.py +            if re.match("^[A-Za-z0-9]*$", self.getConfig("passkey")): +                multipart = True +                data = (FORM_FILE, captcha) +            else: +                multipart = False +                with open(captcha, 'rb') as f: +                    data = f.read() +                data = b64encode(data) + +            response = req.load(self.SUBMIT_URL, post={"action": "UPLOADCAPTCHA", +                                                       "username": self.getConfig("username"), +                                                       "password": self.getConfig("passkey"), "file": data}, +                                                       multipart=multipart) +        finally: +            req.close() + +        if response.startswith("ERROR"): +            raise ImageTyperzException(response) +        else: +            data = response.split('|') +            if len(data) == 2: +                ticket, result = data +            else: +                raise ImageTyperzException("Unknown response %s" % response) + +        return ticket, result + +    def newCaptchaTask(self, task): +        if "service" in task.data: +            return False + +        if not task.isTextual(): +            return False + +        if not self.getConfig("username") or not self.getConfig("passkey"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        if self.getCredits() > 0: +            task.handler.append(self) +            task.data['service'] = self.__name__ +            task.setWaiting(100) +            start_new_thread(self.processCaptcha, (task,)) + +        else: +            self.logInfo(_("Your %s account has not enough credits") % self.__name__) + +    def captchaInvalid(self, task): +        if task.data['service'] == self.__name__ and "ticket" in task.data: +            response = getURL(self.RESPOND_URL, post={"action": "SETBADIMAGE", "username": self.getConfig("username"), +                                                      "password": self.getConfig("passkey"), +                                                      "imageid": task.data['ticket']}) + +            if response == "SUCCESS": +                self.logInfo(_("Bad captcha solution received, requested refund")) +            else: +                self.logError(_("Bad captcha solution received, refund request failed"), response) + +    def processCaptcha(self, task): +        c = task.captchaFile +        try: +            ticket, result = self.submit(c) +        except ImageTyperzException, e: +            task.error = e.getCode() +            return + +        task.data['ticket'] = ticket +        task.setResult(result) diff --git a/pyload/plugins/hooks/LinkdecrypterCom.py b/pyload/plugins/hooks/LinkdecrypterCom.py new file mode 100644 index 000000000..cb7ab9da5 --- /dev/null +++ b/pyload/plugins/hooks/LinkdecrypterCom.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.network.RequestFactory import getURL +from pyload.plugins.Hook import Hook +from pyload.utils import remove_chars + + +class LinkdecrypterCom(Hook): +    __name__ = "LinkdecrypterCom" +    __type__ = "hook" +    __version__ = "0.19" + +    __config__ = [("activated", "bool", "Activated", False)] + +    __description__ = """Linkdecrypter.com hook plugin""" +    __author_name__ = "zoidberg" +    __author_mail__ = "zoidberg@mujmail.cz" + + +    def coreReady(self): +        try: +            self.loadPatterns() +        except Exception, e: +            self.logError(e) + +    def loadPatterns(self): +        page = getURL("http://linkdecrypter.com/") +        m = re.search(r'<b>Supported\(\d+\)</b>: <i>([^+<]*)', page) +        if m is None: +            self.logError(_("Crypter list not found")) +            return + +        builtin = [name.lower() for name in self.core.pluginManager.crypterPlugins.keys()] +        builtin.append("downloadserienjunkiesorg") + +        crypter_pattern = re.compile("(\w[\w.-]+)") +        online = [] +        for crypter in m.group(1).split(', '): +            m = re.match(crypter_pattern, crypter) +            if m and remove_chars(m.group(1), "-.") not in builtin: +                online.append(m.group(1).replace(".", "\\.")) + +        if not online: +            self.logError(_("Crypter list is empty")) +            return + +        regexp = r"https?://([^.]+\.)*?(%s)/.*" % "|".join(online) + +        dict = self.core.pluginManager.crypterPlugins[self.__name__] +        dict['pattern'] = regexp +        dict['re'] = re.compile(regexp) + +        self.logDebug("REGEXP", regexp) diff --git a/pyload/plugins/hooks/LinksnappyCom.py b/pyload/plugins/hooks/LinksnappyCom.py new file mode 100644 index 000000000..9086e3c2a --- /dev/null +++ b/pyload/plugins/hooks/LinksnappyCom.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class LinksnappyCom(MultiHoster): +    __name__ = "LinksnappyCom" +    __type__ = "hook" +    __version__ = "0.01" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Linksnappy.com hook plugin""" +    __author_name__ = "stickell" +    __author_mail__ = "l.stickell@yahoo.it" + + +    def getHoster(self): +        json_data = getURL('http://gen.linksnappy.com/lseAPI.php?act=FILEHOSTS') +        json_data = json_loads(json_data) + +        return json_data['return'].keys() diff --git a/pyload/plugins/hooks/MegaDebridEu.py b/pyload/plugins/hooks/MegaDebridEu.py new file mode 100644 index 000000000..0345e47fa --- /dev/null +++ b/pyload/plugins/hooks/MegaDebridEu.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class MegaDebridEu(MultiHoster): +    __name__ = "MegaDebridEu" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False)] + +    __description__ = """mega-debrid.eu hook plugin""" +    __author_name__ = "D.Ducatel" +    __author_mail__ = "dducatel@je-geek.fr" + + +    def getHoster(self): +        reponse = getURL('http://www.mega-debrid.eu/api.php?action=getHosters') +        json_data = json_loads(reponse) + +        if json_data['response_code'] == "ok": +            host_list = [element[0] for element in json_data['hosters']] +        else: +            self.logError(_("Unable to retrieve hoster list")) +            host_list = list() + +        return host_list diff --git a/pyload/plugins/hooks/MergeFiles.py b/pyload/plugins/hooks/MergeFiles.py new file mode 100644 index 000000000..540ebafdc --- /dev/null +++ b/pyload/plugins/hooks/MergeFiles.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +import os +import re +import traceback + +from pyload.plugins.Hook import Hook, threaded +from pyload.utils import safe_join, fs_encode + + +class MergeFiles(Hook): +    __name__ = "MergeFiles" +    __type__ = "hook" +    __version__ = "0.12" + +    __config__ = [("activated", "bool", "Activated", False)] + +    __description__ = """Merges parts splitted with hjsplit""" +    __author_name__ = "and9000" +    __author_mail__ = "me@has-no-mail.com" + +    BUFFER_SIZE = 4096 + + +    def setup(self): +        # nothing to do +        pass + +    @threaded +    def packageFinished(self, pack): +        files = {} +        fid_dict = {} +        for fid, data in pack.getChildren().iteritems(): +            if re.search("\.[0-9]{3}$", data['name']): +                if data['name'][:-4] not in files: +                    files[data['name'][:-4]] = [] +                files[data['name'][:-4]].append(data['name']) +                files[data['name'][:-4]].sort() +                fid_dict[data['name']] = fid + +        download_folder = self.config['general']['download_folder'] + +        if self.config['general']['folder_per_package']: +            download_folder = safe_join(download_folder, pack.folder) + +        for name, file_list in files.iteritems(): +            self.logInfo(_("Starting merging of"), name) +            final_file = open(safe_join(download_folder, name), "wb") + +            for splitted_file in file_list: +                self.logDebug("Merging part", splitted_file) +                pyfile = self.core.files.getFile(fid_dict[splitted_file]) +                pyfile.setStatus("processing") +                try: +                    s_file = open(os.path.join(download_folder, splitted_file), "rb") +                    size_written = 0 +                    s_file_size = int(os.path.getsize(os.path.join(download_folder, splitted_file))) +                    while True: +                        f_buffer = s_file.read(self.BUFFER_SIZE) +                        if f_buffer: +                            final_file.write(f_buffer) +                            size_written += self.BUFFER_SIZE +                            pyfile.setProgress((size_written * 100) / s_file_size) +                        else: +                            break +                    s_file.close() +                    self.logDebug("Finished merging part", splitted_file) +                except Exception, e: +                    print traceback.print_exc() +                finally: +                    pyfile.setProgress(100) +                    pyfile.setStatus("finished") +                    pyfile.release() + +            final_file.close() +            self.logInfo(_("Finished merging of"), name) diff --git a/pyload/plugins/hooks/MultiHome.py b/pyload/plugins/hooks/MultiHome.py new file mode 100644 index 000000000..968214b5c --- /dev/null +++ b/pyload/plugins/hooks/MultiHome.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +from time import time + +from pyload.plugins.Hook import Hook + + +class MultiHome(Hook): +    __name__ = "MultiHome" +    __type__ = "hook" +    __version__ = "0.11" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("interfaces", "str", "Interfaces", "None")] + +    __description__ = """Ip address changer""" +    __author_name__ = "mkaay" +    __author_mail__ = "mkaay@mkaay.de" + + +    def setup(self): +        self.register = {} +        self.interfaces = [] +        self.parseInterfaces(self.getConfig("interfaces").split(";")) +        if not self.interfaces: +            self.parseInterfaces([self.config['download']['interface']]) +            self.setConfig("interfaces", self.toConfig()) + +    def toConfig(self): +        return ";".join([i.adress for i in self.interfaces]) + +    def parseInterfaces(self, interfaces): +        for interface in interfaces: +            if not interface or str(interface).lower() == "none": +                continue +            self.interfaces.append(Interface(interface)) + +    def coreReady(self): +        requestFactory = self.core.requestFactory +        oldGetRequest = requestFactory.getRequest + +        def getRequest(pluginName, account=None): +            iface = self.bestInterface(pluginName, account) +            if iface: +                iface.useFor(pluginName, account) +                requestFactory.iface = lambda: iface.adress +                self.logDebug("Using address", iface.adress) +            return oldGetRequest(pluginName, account) + +        requestFactory.getRequest = getRequest + +    def bestInterface(self, pluginName, account): +        best = None +        for interface in self.interfaces: +            if not best or interface.lastPluginAccess(pluginName, account) < best.lastPluginAccess(pluginName, account): +                best = interface +        return best + + +class Interface(object): + +    def __init__(self, adress): +        self.adress = adress +        self.history = {} + +    def lastPluginAccess(self, pluginName, account): +        if (pluginName, account) in self.history: +            return self.history[(pluginName, account)] +        return 0 + +    def useFor(self, pluginName, account): +        self.history[(pluginName, account)] = time() + +    def __repr__(self): +        return "<Interface - %s>" % self.adress diff --git a/pyload/plugins/hooks/MultishareCz.py b/pyload/plugins/hooks/MultishareCz.py new file mode 100644 index 000000000..9e1bd50a4 --- /dev/null +++ b/pyload/plugins/hooks/MultishareCz.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class MultishareCz(MultiHoster): +    __name__ = "MultishareCz" +    __type__ = "hook" +    __version__ = "0.04" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", "uloz.to")] + +    __description__ = """MultiShare.cz hook plugin""" +    __author_name__ = "zoidberg" +    __author_mail__ = "zoidberg@mujmail.cz" + +    HOSTER_PATTERN = r'<img class="logo-shareserveru"[^>]*?alt="([^"]+)"></td>\s*<td class="stav">[^>]*?alt="OK"' + + +    def getHoster(self): +        page = getURL("http://www.multishare.cz/monitoring/") +        return re.findall(self.HOSTER_PATTERN, page) diff --git a/pyload/plugins/hooks/MyfastfileCom.py b/pyload/plugins/hooks/MyfastfileCom.py new file mode 100644 index 000000000..216dcaf5d --- /dev/null +++ b/pyload/plugins/hooks/MyfastfileCom.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster +from pyload.utils import json_loads + + +class MyfastfileCom(MultiHoster): +    __name__ = "MyfastfileCom" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Myfastfile.com hook plugin""" +    __author_name__ = "stickell" +    __author_mail__ = "l.stickell@yahoo.it" + + +    def getHoster(self): +        json_data = getURL('http://myfastfile.com/api.php?hosts', decode=True) +        self.logDebug("JSON data", json_data) +        json_data = json_loads(json_data) + +        return json_data['hosts'] diff --git a/pyload/plugins/hooks/OverLoadMe.py b/pyload/plugins/hooks/OverLoadMe.py new file mode 100644 index 000000000..fae4209f8 --- /dev/null +++ b/pyload/plugins/hooks/OverLoadMe.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class OverLoadMe(MultiHoster): +    __name__ = "OverLoadMe" +    __type__ = "hook" +    __version__ = "0.01" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("https", "bool", "Enable HTTPS", True), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 12)] + +    __description__ = """Over-Load.me hook plugin""" +    __author_name__ = "marley" +    __author_mail__ = "marley@over-load.me" + + +    def getHoster(self): +        https = "https" if self.getConfig("https") else "http" +        page = getURL(https + "://api.over-load.me/hoster.php", +                      get={"auth": "0001-cb1f24dadb3aa487bda5afd3b76298935329be7700cd7-5329be77-00cf-1ca0135f"} +                      ).replace("\"", "").strip() +        self.logDebug("Hosterlist", page) + +        return [x.strip() for x in page.split(",") if x.strip()] diff --git a/pyload/plugins/hooks/PremiumTo.py b/pyload/plugins/hooks/PremiumTo.py new file mode 100644 index 000000000..b95b15b53 --- /dev/null +++ b/pyload/plugins/hooks/PremiumTo.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class PremiumTo(MultiHoster): +    __name__ = "PremiumTo" +    __type__ = "hook" +    __version__ = "0.04" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for downloads from supported hosters:", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", "")] + +    __description__ = """Premium.to hook plugin""" +    __author_name__ = ("RaNaN", "zoidberg", "stickell") +    __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz", "l.stickell@yahoo.it") + + +    def getHoster(self): +        page = getURL("http://premium.to/api/hosters.php", +                      get={'username': self.account.username, 'password': self.account.password}) +        return [x.strip() for x in page.replace("\"", "").split(";")] + +    def coreReady(self): +        self.account = self.core.accountManager.getAccountPlugin("PremiumTo") + +        user = self.account.selectAccount()[0] + +        if not user: +            self.logError(_("Please add your premium.to account first and restart pyLoad")) +            return + +        return MultiHoster.coreReady(self) diff --git a/pyload/plugins/hooks/PremiumizeMe.py b/pyload/plugins/hooks/PremiumizeMe.py new file mode 100644 index 000000000..b6d89adb6 --- /dev/null +++ b/pyload/plugins/hooks/PremiumizeMe.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class PremiumizeMe(MultiHoster): +    __name__ = "PremiumizeMe" +    __type__ = "hook" +    __version__ = "0.12" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Premiumize.me hook plugin""" +    __author_name__ = "Florian Franzen" +    __author_mail__ = "FlorianFranzen@gmail.com" + + +    def getHoster(self): +        # If no accounts are available there will be no hosters available +        if not self.account or not self.account.canUse(): +            return [] + +        # Get account data +        (user, data) = self.account.selectAccount() + +        # Get supported hosters list from premiumize.me using the +        # json API v1 (see https://secure.premiumize.me/?show=api) +        answer = getURL("https://api.premiumize.me/pm-api/v1.php?method=hosterlist¶ms[login]=%s¶ms[pass]=%s" % ( +                        user, data['password'])) +        data = json_loads(answer) + +        # If account is not valid thera are no hosters available +        if data['status'] != 200: +            return [] + +        # Extract hosters from json file +        return data['result']['hosterlist'] + +    def coreReady(self): +        # Get account plugin and check if there is a valid account available +        self.account = self.core.accountManager.getAccountPlugin("PremiumizeMe") +        if not self.account.canUse(): +            self.account = None +            self.logError(_("Please add a valid premiumize.me account first and restart pyLoad.")) +            return + +        # Run the overwriten core ready which actually enables the multihoster hook +        return MultiHoster.coreReady(self) diff --git a/pyload/plugins/hooks/RPNetBiz.py b/pyload/plugins/hooks/RPNetBiz.py new file mode 100644 index 000000000..b46e326e6 --- /dev/null +++ b/pyload/plugins/hooks/RPNetBiz.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class RPNetBiz(MultiHoster): +    __name__ = "RPNetBiz" +    __type__ = "hook" +    __version__ = "0.1" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """RPNet.biz hook plugin""" +    __author_name__ = "Dman" +    __author_mail__ = "dmanugm@gmail.com" + + +    def getHoster(self): +        # No hosts supported if no account +        if not self.account or not self.account.canUse(): +            return [] + +        # Get account data +        (user, data) = self.account.selectAccount() + +        response = getURL("https://premium.rpnet.biz/client_api.php", +                          get={"username": user, "password": data['password'], "action": "showHosterList"}) +        hoster_list = json_loads(response) + +        # If account is not valid thera are no hosters available +        if 'error' in hoster_list: +            return [] + +        # Extract hosters from json file +        return hoster_list['hosters'] + +    def coreReady(self): +        # Get account plugin and check if there is a valid account available +        self.account = self.core.accountManager.getAccountPlugin("RPNetBiz") +        if not self.account.canUse(): +            self.account = None +            self.logError(_("Please enter your %s account or deactivate this plugin") % "rpnet") +            return + +        # Run the overwriten core ready which actually enables the multihoster hook +        return MultiHoster.coreReady(self) diff --git a/pyload/plugins/hooks/RealdebridCom.py b/pyload/plugins/hooks/RealdebridCom.py new file mode 100644 index 000000000..c1c519ace --- /dev/null +++ b/pyload/plugins/hooks/RealdebridCom.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class RealdebridCom(MultiHoster): +    __name__ = "RealdebridCom" +    __type__ = "hook" +    __version__ = "0.43" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("https", "bool", "Enable HTTPS", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Real-Debrid.com hook plugin""" +    __author_name__ = "Devirex Hazzard" +    __author_mail__ = "naibaf_11@yahoo.de" + + +    def getHoster(self): +        https = "https" if self.getConfig("https") else "http" +        page = getURL(https + "://real-debrid.com/api/hosters.php").replace("\"", "").strip() + +        return [x.strip() for x in page.split(",") if x.strip()] diff --git a/pyload/plugins/hooks/RehostTo.py b/pyload/plugins/hooks/RehostTo.py new file mode 100644 index 000000000..059f36284 --- /dev/null +++ b/pyload/plugins/hooks/RehostTo.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class RehostTo(MultiHoster): +    __name__ = "RehostTo" +    __type__ = "hook" +    __version__ = "0.43" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to stanard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24)] + +    __description__ = """Rehost.to hook plugin""" +    __author_name__ = "RaNaN" +    __author_mail__ = "RaNaN@pyload.org" + + +    def getHoster(self): +        page = getURL("http://rehost.to/api.php?cmd=get_supported_och_dl&long_ses=%s" % self.long_ses) +        return [x.strip() for x in page.replace("\"", "").split(",")] + +    def coreReady(self): +        self.account = self.core.accountManager.getAccountPlugin("RehostTo") + +        user = self.account.selectAccount()[0] + +        if not user: +            self.logError(_("Please add your rehost.to account first and restart pyLoad")) +            return + +        data = self.account.getAccountInfo(user) +        self.ses = data['ses'] +        self.long_ses = data['long_ses'] + +        return MultiHoster.coreReady(self) diff --git a/pyload/plugins/hooks/RestartFailed.py b/pyload/plugins/hooks/RestartFailed.py new file mode 100644 index 000000000..8bad74620 --- /dev/null +++ b/pyload/plugins/hooks/RestartFailed.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +from pyload.plugins.Hook import Hook + + +class RestartFailed(Hook): +    __name__ = "RestartFailed" +    __type__ = "hook" +    __version__ = "1.55" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("interval", "int", "Check interval in minutes", 90)] + +    __description__ = """Periodically restart all failed downloads in queue""" +    __author_name__ = "Walter Purcaro" +    __author_mail__ = "vuolter@gmail.com" + +    MIN_INTERVAL = 15 * 60  #: 15m minimum check interval (value is in seconds) + +    event_list = ["pluginConfigChanged"] + + +    def pluginConfigChanged(self, plugin, name, value): +        if name == "interval": +            interval = value * 60 +            if self.MIN_INTERVAL <= interval != self.interval: +                self.core.scheduler.removeJob(self.cb) +                self.interval = interval +                self.initPeriodical() +            else: +                self.logDebug("Invalid interval value, kept current") + +    def periodical(self): +        self.logInfo(_("Restart failed downloads")) +        self.api.restartFailed() + +    def setup(self): +        self.api = self.core.api +        self.interval = self.MIN_INTERVAL + +    def coreReady(self): +        self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) diff --git a/pyload/plugins/hooks/SimplyPremiumCom.py b/pyload/plugins/hooks/SimplyPremiumCom.py new file mode 100644 index 000000000..f0df36b22 --- /dev/null +++ b/pyload/plugins/hooks/SimplyPremiumCom.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class SimplyPremiumCom(MultiHoster): +    __name__ = "SimplyPremiumCom" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", "False"), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", "False"), +                  ("interval", "int", "Reload interval in hours (0 to disable)", "24")] + +    __description__ = """Simply-Premium.com hook plugin""" +    __author_name__ = "EvolutionClip" +    __author_mail__ = "evolutionclip@live.de" + + +    def getHoster(self): +        json_data = getURL('http://www.simply-premium.com/api/hosts.php?format=json&online=1') +        json_data = json_loads(json_data) + +        host_list = [element['regex'] for element in json_data['result']] + +        return host_list diff --git a/pyload/plugins/hooks/SimplydebridCom.py b/pyload/plugins/hooks/SimplydebridCom.py new file mode 100644 index 000000000..f7c899a48 --- /dev/null +++ b/pyload/plugins/hooks/SimplydebridCom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class SimplydebridCom(MultiHoster): +    __name__ = "SimplydebridCom" +    __type__ = "hook" +    __version__ = "0.01" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", "")] + +    __description__ = """Simply-Debrid.com hook plugin""" +    __author_name__ = "Kagenoshin" +    __author_mail__ = "kagenoshin@gmx.ch" + + +    def getHoster(self): +        page = getURL("http://simply-debrid.com/api.php?list=1") +        return [x.strip() for x in page.rstrip(';').replace("\"", "").split(";")] diff --git a/pyload/plugins/hooks/UnSkipOnFail.py b/pyload/plugins/hooks/UnSkipOnFail.py new file mode 100644 index 000000000..40b0233f5 --- /dev/null +++ b/pyload/plugins/hooks/UnSkipOnFail.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +from os.path import basename + +from pyload.datatypes.PyFile import PyFile +from pyload.plugins.Hook import Hook +from pyload.utils import fs_encode + + +class UnSkipOnFail(Hook): +    __name__ = "UnSkipOnFail" +    __type__ = "hook" +    __version__ = "0.01" + +    __config__ = [("activated", "bool", "Activated", True)] + +    __description__ = """When a download fails, restart skipped duplicates""" +    __author_name__ = "hagg" +    __author_mail__ = None + + +    def downloadFailed(self, pyfile): +        pyfile_name = basename(pyfile.name) +        pid = pyfile.package().id +        msg = _('look for skipped duplicates for %s (pid:%s)') +        self.logInfo(msg % (pyfile_name, pid)) +        dups = self.findDuplicates(pyfile) +        for link in dups: +            # check if link is "skipped"(=4) +            if link.status == 4: +                lpid = link.packageID +                self.logInfo(_('restart "%s" (pid:%s)') % (pyfile_name, lpid)) +                self.setLinkStatus(link, "queued") + +    def findDuplicates(self, pyfile): +        """ Search all packages for duplicate links to "pyfile". +            Duplicates are links that would overwrite "pyfile". +            To test on duplicity the package-folder and link-name +            of twolinks are compared (basename(link.name)). +            So this method returns a list of all links with equal +            package-folders and filenames as "pyfile", but except +            the data for "pyfile" iotselöf. +            It does MOT check the link's status. +        """ +        dups = [] +        pyfile_name = fs_encode(basename(pyfile.name)) +        # get packages (w/o files, as most file data is useless here) +        queue = self.core.api.getQueue() +        for package in queue: +            # check if package-folder equals pyfile's package folder +            if fs_encode(package.folder) == fs_encode(pyfile.package().folder): +                # now get packaged data w/ files/links +                pdata = self.core.api.getPackageData(package.pid) +                if pdata.links: +                    for link in pdata.links: +                        link_name = fs_encode(basename(link.name)) +                        # check if link name collides with pdata's name +                        if link_name == pyfile_name: +                            # at last check if it is not pyfile itself +                            if link.fid != pyfile.id: +                                dups.append(link) +        return dups + +    def setLinkStatus(self, link, new_status): +        """ Change status of "link" to "new_status". +            "link" has to be a valid FileData object, +            "new_status" has to be a valid status name +              (i.e. "queued" for this Plugin) +            It creates a temporary PyFile object using +            "link" data, changes its status, and tells +            the core.files-manager to save its data. +        """ +        pyfile = PyFile(self.core.files, +                        link.fid, +                        link.url, +                        link.name, +                        link.size, +                        link.status, +                        link.error, +                        link.plugin, +                        link.packageID, +                        link.order) +        pyfile.setStatus(new_status) +        self.core.files.save() +        pyfile.release() diff --git a/pyload/plugins/hooks/UnrestrictLi.py b/pyload/plugins/hooks/UnrestrictLi.py new file mode 100644 index 000000000..2ca5ad907 --- /dev/null +++ b/pyload/plugins/hooks/UnrestrictLi.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from pyload.utils import json_loads +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class UnrestrictLi(MultiHoster): +    __name__ = "UnrestrictLi" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", ""), +                  ("unloadFailing", "bool", "Revert to standard download if download fails", False), +                  ("interval", "int", "Reload interval in hours (0 to disable)", 24), +                  ("history", "bool", "Delete History", False)] + +    __description__ = """Unrestrict.li hook plugin""" +    __author_name__ = "stickell" +    __author_mail__ = "l.stickell@yahoo.it" + + +    def getHoster(self): +        json_data = getURL('http://unrestrict.li/api/jdownloader/hosts.php?format=json') +        json_data = json_loads(json_data) + +        host_list = [element['host'] for element in json_data['result']] + +        return host_list diff --git a/pyload/plugins/hooks/UpdateManager.py b/pyload/plugins/hooks/UpdateManager.py new file mode 100644 index 000000000..6da39b239 --- /dev/null +++ b/pyload/plugins/hooks/UpdateManager.py @@ -0,0 +1,281 @@ +# -*- coding: utf-8 -*- + +import re +import sys + +from operator import itemgetter +from os import path, remove, stat + +from pyload.network.RequestFactory import getURL +from pyload.plugins.Hook import Expose, Hook, threaded +from pyload.utils import safe_join + + +class UpdateManager(Hook): +    __name__ = "UpdateManager" +    __type__ = "hook" +    __version__ = "0.35" + +    __config__ = [("activated", "bool", "Activated", True), +                  ("mode", "pyLoad + plugins;plugins only", "Check updates for", "pyLoad + plugins"), +                  ("interval", "int", "Check interval in hours", 8), +                  ("reloadplugins", "bool", "Monitor plugins for code changes (debug mode only)", True), +                  ("nodebugupdate", "bool", "Don't check for updates in debug mode", True)] + +    __description__ = """ Check for updates """ +    __author_name__ = "Walter Purcaro" +    __author_mail__ = "vuolter@gmail.com" + + +    event_list = ["pluginConfigChanged"] + +    SERVER_URL = "http://updatemanager.pyload.org" +    MIN_INTERVAL = 3 * 60 * 60  #: 3h minimum check interval (value is in seconds) + + +    def pluginConfigChanged(self, plugin, name, value): +        if name == "interval": +            interval = value * 60 * 60 +            if self.MIN_INTERVAL <= interval != self.interval: +                self.core.scheduler.removeJob(self.cb) +                self.interval = interval +                self.initPeriodical() +            else: +                self.logDebug("Invalid interval value, kept current") +        elif name == "reloadplugins": +            if self.cb2: +                self.core.scheduler.removeJob(self.cb2) +            if value is True and self.core.debug: +                self.periodical2() + +    def coreReady(self): +        self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) +        x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins")) +        self.core.scheduler.addJob(10, x, threaded=False) + +    def unload(self): +        self.pluginConfigChanged(self.__name__, "reloadplugins", False) + +    def setup(self): +        self.cb2 = None +        self.interval = self.MIN_INTERVAL +        self.updating = False +        self.info = {'pyload': False, 'version': None, 'plugins': False} +        self.mtimes = {}  #: store modification time for each plugin + +    def periodical2(self): +        if not self.updating: +            self.autoreloadPlugins() +        self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False) + +    @Expose +    def autoreloadPlugins(self): +        """ reload and reindex all modified plugins """ +        modules = filter( +            lambda m: m and (m.__name__.startswith("pyload.plugins.") or +                             m.__name__.startswith("userplugins.")) and +                             m.__name__.count(".") >= 2, sys.modules.itervalues() +        ) + +        reloads = [] + +        for m in modules: +            root, type, name = m.__name__.rsplit(".", 2) +            id = (type, name) +            if type in self.core.pluginManager.plugins: +                f = m.__file__.replace(".pyc", ".py") +                if not path.isfile(f): +                    continue + +                mtime = stat(f).st_mtime + +                if id not in self.mtimes: +                    self.mtimes[id] = mtime +                elif self.mtimes[id] < mtime: +                    reloads.append(id) +                    self.mtimes[id] = mtime + +        return True if self.core.pluginManager.reloadPlugins(reloads) else False + +    def periodical(self): +        if not self.info['pyload'] and not (self.getConfig("nodebugupdate") and self.core.debug): +            self.updateThread() + +    def server_request(self): +        try: +            return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines() +        except: +            self.logWarning(_("Unable to contact server to get updates")) + +    @threaded +    def updateThread(self): +        self.updating = True +        status = self.update(onlyplugin=self.getConfig("mode") == "plugins only") +        if status == 2: +            self.core.api.restart() +        else: +            self.updating = False + +    @Expose +    def updatePlugins(self): +        """ simple wrapper for calling plugin update quickly """ +        return self.update(onlyplugin=True) + +    @Expose +    def update(self, onlyplugin=False): +        """ check for updates """ +        data = self.server_request() +        if not data: +            exitcode = 0 +        elif data[0] == "None": +            self.logInfo(_("No new pyLoad version available")) +            updates = data[1:] +            exitcode = self._updatePlugins(updates) +        elif onlyplugin: +            exitcode = 0 +        else: +            newversion = data[0] +            self.logInfo(_("***  New pyLoad Version %s available  ***") % newversion) +            self.logInfo(_("***  Get it here: https://github.com/pyload/pyload/releases  ***")) +            exitcode = 3 +            self.info['pyload'] = True +            self.info['version'] = newversion +        return exitcode  #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required; 3 = No plugins updated, new pyLoad version available + +    def _updatePlugins(self, updates): +        """ check for plugin updates """ + +        if self.info['plugins']: +            return False  #: plugins were already updated + +        updated = [] + +        vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)') +        url = updates[0] +        schema = updates[1].split('|') +        if "BLACKLIST" in updates: +            blacklist = updates[updates.index('BLACKLIST') + 1:] +            updates = updates[2:updates.index('BLACKLIST')] +        else: +            blacklist = None +            updates = updates[2:] + +        upgradable = sorted(map(lambda x: dict(zip(schema, x.split('|'))), updates), key=itemgetter("type", "name")) +        for plugin in upgradable: +            filename = plugin['name'] +            prefix = plugin['type'] +            version = plugin['version'] + +            if filename.endswith(".pyc"): +                name = filename[:filename.find("_")] +            else: +                name = filename.replace(".py", "") + +            #@TODO: obsolete after 0.4.10 +            if prefix.endswith("s"): +                type = prefix[:-1] +            else: +                type = prefix + +            plugins = getattr(self.core.pluginManager, "%sPlugins" % type) + +            oldver = float(plugins[name]['v']) if name in plugins else None +            newver = float(version) + +            if not oldver: +                msg = "New [%(type)s] %(name)s (v%(newver)s)" +            elif newver > oldver: +                msg = "New version of [%(type)s] %(name)s (v%(oldver)s -> v%(newver)s)" +            else: +                continue + +            self.logInfo(_(msg) % { +                'type': type, +                'name': name, +                'oldver': oldver, +                'newver': newver, +            }) + +            try: +                content = getURL(url % plugin) +                m = vre.search(content) +                if m and m.group(2) == version: +                    f = open(safe_join("userplugins", prefix, filename), "wb") +                    f.write(content) +                    f.close() +                    updated.append((prefix, name)) +                else: +                    raise Exception, _("Version mismatch") +            except Exception, e: +                self.logError(_("Error updating plugin %s") % filename, e) + +        if blacklist: +            blacklisted = sorted(map(lambda x: (x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]), blacklist)) + +            # Always protect UpdateManager from self-removing +            try: +                blacklisted.remove(("hook", "UpdateManager")) +            except: +                pass + +            removed = self.removePlugins(blacklisted) +            for t, n in removed: +                self.logInfo(_("Removed blacklisted plugin [%(type)s] %(name)s") % { +                    'type': t, +                    'name': n, +                }) + +        if updated: +            reloaded = self.core.pluginManager.reloadPlugins(updated) +            if reloaded: +                self.logInfo(_("Plugins updated and reloaded")) +                exitcode = 1 +            else: +                self.logInfo(_("*** Plugins have been updated, but need a pyLoad restart to be reloaded ***")) +                self.info['plugins'] = True +                exitcode = 2 +        else: +            self.logInfo(_("No plugin updates available")) +            exitcode = 0 + +        return exitcode  #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required + +    @Expose +    def removePlugins(self, type_plugins): +        """ delete plugins from disk """ + +        if not type_plugins: +            return + +        self.logDebug("Requested deletion of plugins", type_plugins) + +        removed = [] + +        for type, name in type_plugins: +            err = False +            file = name + ".py" + +            for root in ("userplugins", path.join(pypath, "pyload", "plugins")): + +                filename = safe_join(root, type, file) +                try: +                    remove(filename) +                except Exception, e: +                    self.logDebug("Error deleting", path.basename(filename), e) +                    err = True + +                filename += "c" +                if path.isfile(filename): +                    try: +                        if type == "hook": +                            self.manager.deactivateHook(name) +                        remove(filename) +                    except Exception, e: +                        self.logDebug("Error deleting", path.basename(filename), e) +                        err = True + +            if not err: +                id = (type, name) +                removed.append(id) + +        return removed  #: return a list of the plugins successfully removed diff --git a/pyload/plugins/hooks/WindowsPhoneToastNotify.py b/pyload/plugins/hooks/WindowsPhoneToastNotify.py new file mode 100644 index 000000000..79812cefa --- /dev/null +++ b/pyload/plugins/hooks/WindowsPhoneToastNotify.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +import httplib +import time + +from pyload.plugins.Hook import Hook + + +class WindowsPhoneToastNotify(Hook): +    __name__ = "WindowsPhoneToastNotify" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("force", "bool", "Force even if client is connected", False), +                  ("pushId", "str", "pushId", ""), +                  ("pushUrl", "str", "pushUrl", ""), +                  ("pushTimeout", "int", "Timeout between notifications in seconds", 0)] + +    __description__ = """Send push notifications to Windows Phone""" +    __author_name__ = "Andy Voigt" +    __author_mail__ = "phone-support@hotmail.de" + + +    def setup(self): +        self.info = {} + +    def getXmlData(self): +        myxml = ("<?xml version='1.0' encoding='utf-8'?> <wp:Notification xmlns:wp='WPNotification'> " +                 "<wp:Toast> <wp:Text1>Pyload Mobile</wp:Text1> <wp:Text2>Captcha waiting!</wp:Text2> " +                 "</wp:Toast> </wp:Notification>") +        return myxml + +    def doRequest(self): +        URL = self.getConfig("pushUrl") +        request = self.getXmlData() +        webservice = httplib.HTTP(URL) +        webservice.putrequest("POST", self.getConfig("pushId")) +        webservice.putheader("Host", URL) +        webservice.putheader("Content-type", "text/xml") +        webservice.putheader("X-NotificationClass", "2") +        webservice.putheader("X-WindowsPhone-Target", "toast") +        webservice.putheader("Content-length", "%d" % len(request)) +        webservice.endheaders() +        webservice.send(request) +        webservice.close() +        self.setStorage("LAST_NOTIFY", time.time()) + +    def newCaptchaTask(self, task): +        if not self.getConfig("pushId") or not self.getConfig("pushUrl"): +            return False + +        if self.core.isClientConnected() and not self.getConfig("force"): +            return False + +        if (time.time() - float(self.getStorage("LAST_NOTIFY", 0))) < self.getConf("pushTimeout"): +            return False + +        self.doRequest() diff --git a/pyload/plugins/hooks/XFileSharingPro.py b/pyload/plugins/hooks/XFileSharingPro.py new file mode 100644 index 000000000..635d5302b --- /dev/null +++ b/pyload/plugins/hooks/XFileSharingPro.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +import re + +from pyload.plugins.Hook import Hook + + +class XFileSharingPro(Hook): +    __name__ = "XFileSharingPro" +    __type__ = "hook" +    __version__ = "0.12" + +    __config__ = [("activated", "bool", "Activated", True), +                  ("loadDefault", "bool", "Include default (built-in) hoster list", True), +                  ("includeList", "str", "Include hosters (comma separated)", ""), +                  ("excludeList", "str", "Exclude hosters (comma separated)", "")] + +    __description__ = """XFileSharingPro hook plugin""" +    __author_name__ = "zoidberg" +    __author_mail__ = "zoidberg@mujmail.cz" + + +    def coreReady(self): +        self.loadPattern() + + +    def loadPattern(self): +        hosterList = self.getConfigSet('includeList') +        excludeList = self.getConfigSet('excludeList') + +        if self.getConfig('loadDefault'): +            hosterList |= set(( +                #WORKING HOSTERS: +                "aieshare.com", "asixfiles.com", "banashare.com", "cyberlocker.ch", "eyesfile.co", "eyesfile.com", +                "fileband.com", "filedwon.com", "filedownloads.org", "hipfile.com", "kingsupload.com", "mlfat4arab.com", +                "netuploaded.com", "odsiebie.pl", "q4share.com", "ravishare.com", "uptobox.com", "verzend.be", +                "xvidstage.com", "thefile.me", "sharesix.com", "hostingbulk.com", +                #NOT TESTED: +                "bebasupload.com", "boosterking.com", "divxme.com", "filevelocity.com", "glumbouploads.com", +                "grupload.com", "heftyfile.com", "host4desi.com", "laoupload.com", "linkzhost.com", "movreel.com", +                "rockdizfile.com", "limfile.com", "share76.com", "sharebeast.com", "sharehut.com", "sharerun.com", +                "shareswift.com", "sharingonline.com", "6ybh-upload.com", "skipfile.com", "spaadyshare.com", +                "space4file.com", "uploadbaz.com", "uploadc.com", "uploaddot.com", "uploadfloor.com", "uploadic.com", +                "uploadville.com", "vidbull.com", "zalaa.com", "zomgupload.com", "kupload.org", "movbay.org", +                "multishare.org", "omegave.org", "toucansharing.org", "uflinq.org", "banicrazy.info", "flowhot.info", +                "upbrasil.info", "shareyourfilez.biz", "bzlink.us", "cloudcache.cc", "fileserver.cc", "farshare.to", +                "filemaze.ws", "filehost.ws", "filestock.ru", "moidisk.ru", "4up.im", "100shared.com", "sharesix.com", +                "thefile.me", "filenuke.com", "sharerepo.com", "mightyupload.com", +                #WRONG FILE NAME: +                "sendmyway.com", "upchi.co.il", +                #NOT WORKING: +                "amonshare.com", "imageporter.com", "file4safe.com", +                #DOWN OR BROKEN: +                "ddlanime.com", "fileforth.com", "loombo.com", "goldfile.eu", "putshare.com" +            )) + +        hosterList -= (excludeList) +        hosterList -= set(('', u'')) + +        if not hosterList: +            self.unload() +            return + +        regexp = r"http://(?:[^/]*\.)?(%s)/(?:embed-)?\w{12}" % ("|".join(sorted(hosterList)).replace('.', '\.')) + +        dict = self.core.pluginManager.hosterPlugins['XFileSharingPro'] +        dict['pattern'] = regexp +        dict['re'] = re.compile(regexp) +        self.logDebug("Pattern loaded - handling %d hosters" % len(hosterList)) + + +    def getConfigSet(self, option): +        s = self.getConfig(option).lower().replace('|', ',').replace(';', ',') +        return set([x.strip() for x in s.split(',')]) + + +    def unload(self): +        dict = self.core.pluginManager.hosterPlugins['XFileSharingPro'] +        dict['pattern'] = r'^unmatchable$' +        dict['re'] = re.compile(r'^unmatchable$') diff --git a/pyload/plugins/hooks/XMPPInterface.py b/pyload/plugins/hooks/XMPPInterface.py new file mode 100644 index 000000000..4a01493a6 --- /dev/null +++ b/pyload/plugins/hooks/XMPPInterface.py @@ -0,0 +1,233 @@ +# -*- coding: utf-8 -*- + +from pyxmpp import streamtls +from pyxmpp.all import JID, Message +from pyxmpp.interface import implements +from pyxmpp.interfaces import * +from pyxmpp.jabber.client import JabberClient + +from pyload.plugins.hooks.IRCInterface import IRCInterface + + +class XMPPInterface(IRCInterface, JabberClient): +    __name__ = "XMPPInterface" +    __type__ = "hook" +    __version__ = "0.11" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"), +                  ("pw", "str", "Password", ""), +                  ("tls", "bool", "Use TLS", False), +                  ("owners", "str", "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"), +                  ("info_file", "bool", "Inform about every file finished", False), +                  ("info_pack", "bool", "Inform about every package finished", True), +                  ("captcha", "bool", "Send captcha requests", True)] + +    __description__ = """Connect to jabber and let owner perform different tasks""" +    __author_name__ = "RaNaN" +    __author_mail__ = "RaNaN@pyload.org" + + +    implements(IMessageHandlersProvider) + +    def __init__(self, core, manager): +        IRCInterface.__init__(self, core, manager) + +        self.jid = JID(self.getConfig("jid")) +        password = self.getConfig("pw") + +        # if bare JID is provided add a resource -- it is required +        if not self.jid.resource: +            self.jid = JID(self.jid.node, self.jid.domain, "pyLoad") + +        if self.getConfig("tls"): +            tls_settings = streamtls.TLSSettings(require=True, verify_peer=False) +            auth = ("sasl:PLAIN", "sasl:DIGEST-MD5") +        else: +            tls_settings = None +            auth = ("sasl:DIGEST-MD5", "digest") + +        # setup client with provided connection information +        # and identity data +        JabberClient.__init__(self, self.jid, password, +                              disco_name="pyLoad XMPP Client", disco_type="bot", +                              tls_settings=tls_settings, auth_methods=auth) + +        self.interface_providers = [ +            VersionHandler(self), +            self, +        ] + +    def coreReady(self): +        self.new_package = {} + +        self.start() + +    def packageFinished(self, pypack): +        try: +            if self.getConfig("info_pack"): +                self.announce(_("Package finished: %s") % pypack.name) +        except: +            pass + +    def downloadFinished(self, pyfile): +        try: +            if self.getConfig("info_file"): +                self.announce( +                    _("Download finished: %(name)s @ %(plugin)s") % {"name": pyfile.name, "plugin": pyfile.pluginname}) +        except: +            pass + +    def run(self): +        # connect to IRC etc. +        self.connect() +        try: +            self.loop() +        except Exception, ex: +            self.logError(ex) + +    def stream_state_changed(self, state, arg): +        """This one is called when the state of stream connecting the component +        to a server changes. This will usually be used to let the user +        know what is going on.""" +        self.logDebug("*** State changed: %s %r ***" % (state, arg)) + +    def disconnected(self): +        self.logDebug("Client was disconnected") + +    def stream_closed(self, stream): +        self.logDebug("Stream was closed", stream) + +    def stream_error(self, err): +        self.logDebug("Stream Error", err) + +    def get_message_handlers(self): +        """Return list of (message_type, message_handler) tuples. + +        The handlers returned will be called when matching message is received +        in a client session.""" +        return [("normal", self.message)] + +    def message(self, stanza): +        """Message handler for the component.""" +        subject = stanza.get_subject() +        body = stanza.get_body() +        t = stanza.get_type() +        self.logDebug("Message from %s received." % unicode(stanza.get_from())) +        self.logDebug("Body: %s Subject: %s Type: %s" % (body, subject, t)) + +        if t == "headline": +            # 'headline' messages should never be replied to +            return True +        if subject: +            subject = u"Re: " + subject + +        to_jid = stanza.get_from() +        from_jid = stanza.get_to() + +        #j = JID() +        to_name = to_jid.as_utf8() +        from_name = from_jid.as_utf8() + +        names = self.getConfig("owners").split(";") + +        if to_name in names or to_jid.node + "@" + to_jid.domain in names: +            messages = [] + +            trigger = "pass" +            args = None + +            try: +                temp = body.split() +                trigger = temp[0] +                if len(temp) > 1: +                    args = temp[1:] +            except: +                pass + +            handler = getattr(self, "event_%s" % trigger, self.event_pass) +            try: +                res = handler(args) +                for line in res: +                    m = Message( +                        to_jid=to_jid, +                        from_jid=from_jid, +                        stanza_type=stanza.get_type(), +                        subject=subject, +                        body=line) + +                    messages.append(m) +            except Exception, e: +                self.logError(repr(e)) + +            return messages + +        else: +            return True + +    def response(self, msg, origin=""): +        return self.announce(msg) + +    def announce(self, message): +        """ send message to all owners""" +        for user in self.getConfig("owners").split(";"): +            self.logDebug("Send message to", user) + +            to_jid = JID(user) + +            m = Message(from_jid=self.jid, +                        to_jid=to_jid, +                        stanza_type="chat", +                        body=message) + +            stream = self.get_stream() +            if not stream: +                self.connect() +                stream = self.get_stream() + +            stream.send(m) + +    def beforeReconnecting(self, ip): +        self.disconnect() + +    def afterReconnecting(self, ip): +        self.connect() + + +class VersionHandler(object): +    """Provides handler for a version query. + +    This class will answer version query and announce 'jabber:iq:version' namespace +    in the client's disco#info results.""" + +    implements(IIqHandlersProvider, IFeaturesProvider) + +    def __init__(self, client): +        """Just remember who created this.""" +        self.client = client + +    def get_features(self): +        """Return namespace which should the client include in its reply to a +        disco#info query.""" +        return ["jabber:iq:version"] + +    def get_iq_get_handlers(self): +        """Return list of tuples (element_name, namespace, handler) describing +        handlers of <iq type='get'/> stanzas""" +        return [("query", "jabber:iq:version", self.get_version)] + +    def get_iq_set_handlers(self): +        """Return empty list, as this class provides no <iq type='set'/> stanza handler.""" +        return [] + +    def get_version(self, iq): +        """Handler for jabber:iq:version queries. + +        jabber:iq:version queries are not supported directly by PyXMPP, so the +        XML node is accessed directly through the libxml2 API.  This should be +        used very carefully!""" +        iq = iq.make_result_response() +        q = iq.new_query("jabber:iq:version") +        q.newTextChild(q.ns(), "name", "Echo component") +        q.newTextChild(q.ns(), "version", "1.0") +        return iq diff --git a/pyload/plugins/hooks/ZeveraCom.py b/pyload/plugins/hooks/ZeveraCom.py new file mode 100644 index 000000000..155143f64 --- /dev/null +++ b/pyload/plugins/hooks/ZeveraCom.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + +from pyload.network.RequestFactory import getURL +from pyload.plugins.internal.MultiHoster import MultiHoster + + +class ZeveraCom(MultiHoster): +    __name__ = "ZeveraCom" +    __type__ = "hook" +    __version__ = "0.02" + +    __config__ = [("activated", "bool", "Activated", False), +                  ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +                  ("hosterList", "str", "Hoster list (comma separated)", "")] + +    __description__ = """Real-Debrid.com hook plugin""" +    __author_name__ = "zoidberg" +    __author_mail__ = "zoidberg@mujmail.cz" + + +    def getHoster(self): +        page = getURL("http://www.zevera.com/jDownloader.ashx?cmd=gethosters") +        return [x.strip() for x in page.replace("\"", "").split(",")] diff --git a/pyload/plugins/hooks/__init__.py b/pyload/plugins/hooks/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/pyload/plugins/hooks/__init__.py | 
