diff options
Diffstat (limited to 'module/plugins/hooks')
49 files changed, 1936 insertions, 1610 deletions
diff --git a/module/plugins/hooks/AlldebridCom.py b/module/plugins/hooks/AlldebridCom.py index 1ef9b252a..d5986053f 100644 --- a/module/plugins/hooks/AlldebridCom.py +++ b/module/plugins/hooks/AlldebridCom.py @@ -1,30 +1,27 @@ # -*- coding: utf-8 -*- -# should be working - from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class AlldebridCom(MultiHoster): - __name__ = "AlldebridCom" - __version__ = "0.13" - __type__ = "hook" +class AlldebridCom(MultiHook): + __name__ = "AlldebridCom" + __type__ = "hook" + __version__ = "0.14" - __config__ = [("activated", "bool", "Activated", False), - ("https", "bool", "Enable HTTPS", False), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Andy Voigt", "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() + page = getURL(https + "://www.alldebrid.com/api.php", get={'action': "get_host"}).replace("\"", "").strip() return [x.strip() for x in page.split(",") if x.strip()] diff --git a/module/plugins/hooks/BypassCaptcha.py b/module/plugins/hooks/BypassCaptcha.py index 5ac117818..a32de7f42 100644 --- a/module/plugins/hooks/BypassCaptcha.py +++ b/module/plugins/hooks/BypassCaptcha.py @@ -1,73 +1,69 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - -from thread import start_new_thread from pycurl import FORM_FILE, LOW_SPEED_TIME +from thread import start_new_thread -from module.network.RequestFactory import getURL, getRequest from module.network.HTTPRequest import BadHeader - +from module.network.RequestFactory import getURL, getRequest from module.plugins.Hook import Hook -PYLOAD_KEY = "4f771155b640970d5607f919a615bdefc67e7d32" - 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" - __version__ = "0.04" - __type__ = "hook" + __name__ = "BypassCaptcha" + __type__ = "hook" + __version__ = "0.05" - __config__ = [("activated", "bool", "Activated", False), - ("force", "bool", "Force BC even if client is connected", False), + __config__ = [("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") + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("Godofdream", "soilfcition@gmail.com"), + ("zoidberg", "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" + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + def setup(self): - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + def getCredits(self): - response = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")}) + res = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")}) - data = dict([x.split(' ', 1) for x in response.splitlines()]) + data = dict([x.split(' ', 1) for x in res.splitlines()]) return int(data['Left']) + def submit(self, captcha, captchaType="file", match=None): req = getRequest() @@ -75,31 +71,33 @@ class BypassCaptcha(Hook): req.c.setopt(LOW_SPEED_TIME, 80) try: - response = req.load(self.SUBMIT_URL, - post={"vendor_key": PYLOAD_KEY, - "key": self.getConfig("passkey"), - "gen_task_id": "1", - "file": (FORM_FILE, captcha)}, - multipart=True) + res = 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()]) + data = dict([x.split(' ', 1) for x in res.splitlines()]) if not data or "Value" not in data: - raise BypassCaptchaException(response) + raise BypassCaptchaException(res) result = data['Value'] ticket = data['TaskId'] - self.logDebug("result %s : %s" % (ticket, result)) + 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"), + res = 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.", str(e)) + self.logError(_("Could not send response"), e) + def newCaptchaTask(self, task): if "service" in task.data: @@ -121,16 +119,19 @@ class BypassCaptcha(Hook): start_new_thread(self.processCaptcha, (task,)) else: - self.logInfo("Your %s account has not enough credits" % self.__name__) + 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: diff --git a/module/plugins/hooks/Captcha9kw.py b/module/plugins/hooks/Captcha9kw.py index b4e3ad34b..33ad00c49 100755 --- a/module/plugins/hooks/Captcha9kw.py +++ b/module/plugins/hooks/Captcha9kw.py @@ -1,170 +1,261 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" from __future__ import with_statement -from thread import start_new_thread +import re + from base64 import b64encode -import time +from thread import start_new_thread +from time import sleep -from module.network.RequestFactory import getURL from module.network.HTTPRequest import BadHeader +from module.network.RequestFactory import getURL from module.plugins.Hook import Hook class Captcha9kw(Hook): - __name__ = "Captcha9kw" - __version__ = "0.09" - __type__ = "hook" - - __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", "")] + __name__ = "Captcha9kw" + __type__ = "hook" + __version__ = "0.26" + + __config__ = [("activated" , "bool" , "Activated" , True ), + ("ssl" , "bool" , "Use HTTPS" , True ), + ("force" , "bool" , "Force captcha resolving even if client is connected" , True ), + ("confirm" , "bool" , "Confirm Captcha (cost +6 credits)" , False ), + ("captchaperhour", "int" , "Captcha per hour" , "9999" ), + ("captchapermin" , "int" , "Captcha per minute" , "9999" ), + ("prio" , "int" , "Priority (max 10)(cost +0 -> +10 credits)" , "0" ), + ("queue" , "int" , "Max. Queue (max 999)" , "50" ), + ("hoster_options", "string" , "Hoster options (format: pluginname:prio=1:selfsolfe=1:confirm=1:timeout=900|...)", "ShareonlineBiz:prio=0:timeout=999 | UploadedTo:prio=0:timeout=999"), + ("selfsolve" , "bool" , "Selfsolve (manually solve your captcha in your 9kw client if active)" , "0" ), + ("passkey" , "password", "API key" , "" ), + ("timeout" , "int" , "Timeout in seconds (min 60, max 3999)" , "900" )] __description__ = """Send captchas to 9kw.eu""" - __author_name__ = "RaNaN" - __author_mail__ = "RaNaN@pyload.org" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("Walter Purcaro", "vuolter@gmail.com")] + - API_URL = "://www.9kw.eu/index.cgi" + API_URL = "http://www.9kw.eu/index.cgi" + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass def setup(self): - self.API_URL = "https" + self.API_URL if self.getConfig("https") else "http" + self.API_URL - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + if self.getConfig("ssl"): + self.API_URL = self.API_URL.replace("http://", "https://") - 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) + def getCredits(self): + res = getURL(self.API_URL, + get={'apikey': self.getConfig("passkey"), + 'pyload': "1", + 'source': "pyload", + 'action': "usercaptchaguthaben"}) + + if res.isdigit(): + self.logInfo(_("%s credits left") % res) + credits = self.info['credits'] = int(res) return credits else: - self.logError(response) + self.logError(res) return 0 - def processCaptcha(self, task): - result = None - with open(task.captchaFile, 'rb') as f: - data = f.read() + def _processCaptcha(self, task): + try: + with open(task.captchaFile, 'rb') as f: + data = f.read() + + except IOError, e: + self.logError(e) + return + data = b64encode(data) - self.logDebug("%s : %s" % (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 != "": + mouse = 1 if task.isPositional() else 0 + pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1) + + option = {'min' : 2, + 'max' : 50, + 'phrase' : 0, + 'numeric' : 0, + 'case_sensitive': 0, + 'math' : 0, + 'prio' : min(max(self.getConfig("prio"), 0), 10), + 'confirm' : self.getConfig("confirm"), + 'timeout' : min(max(self.getConfig("timeout"), 300), 3999), + 'selfsolve' : self.getConfig("selfsolve"), + 'cph' : self.getConfig("captchaperhour"), + 'cpm' : self.getConfig("captchapermin")} + + for opt in str(self.getConfig("hoster_options").split('|')): + + details = map(str.strip, opt.split(':')) + + if not details or details[0].lower() != pluginname.lower(): + continue + + for d in details: + hosteroption = d.split("=") + + if len(hosteroption) < 2 or not hosteroption[1].isdigit(): + continue + + o = hosteroption[0].lower() + if o in option: + option[o] = hosteroption[1] + + break + + post_data = {'apikey' : self.getConfig("passkey"), + 'prio' : option['prio'], + 'confirm' : option['confirm'], + 'maxtimeout' : option['timeout'], + 'selfsolve' : option['selfsolve'], + 'captchaperhour': option['cph'], + 'captchapermin' : option['cpm'], + 'case-sensitive': option['case_sensitive'], + 'min_len' : option['min'], + 'max_len' : option['max'], + 'phrase' : option['phrase'], + 'numeric' : option['numeric'], + 'math' : option['math'], + 'oldsource' : pluginname, + 'pyload' : "1", + 'source' : "pyload", + 'base64' : "1", + 'mouse' : mouse, + 'file-upload-01': data, + 'action' : "usercaptchaupload"} + + for _i in xrange(5): + try: + res = getURL(self.API_URL, post=post_data) + except BadHeader, e: + sleep(3) + else: + if res and res.isdigit(): break + else: + self.logError(_("Bad upload: %s") % res) + return + + self.logDebug(_("NewCaptchaID ticket: %s") % res, task.captchaFile) + + task.data["ticket"] = res + + for _i in xrange(int(self.getConfig("timeout") / 5)): + result = getURL(self.API_URL, + get={'apikey': self.getConfig("passkey"), + 'id' : res, + 'pyload': "1", + 'info' : "1", + 'source': "pyload", + 'action': "usercaptchacorrectdata"}) + + if not result or result == "NO DATA": + sleep(5) + else: + break + else: + self.logDebug("Could not send request: %s" % res) + result = None - time.sleep(3) + self.logInfo(_("Captcha result for ticket %s: %s") % (res, result)) + + task.setResult(result) - result = response2 - task.data['ticket'] = response - self.logInfo("result %s : %s" % (response, result)) - task.setResult(result) - else: - self.logError("Bad upload: %s" % response) - return False def newCaptchaTask(self, task): if not task.isTextual() and not task.isPositional(): - return False + return if not self.getConfig("passkey"): - return False + return if self.core.isClientConnected() and not self.getConfig("force"): - return False + return - if self.getCredits() > 0: - task.handler.append(self) - task.setWaiting(self.getConfig("timeout")) - start_new_thread(self.processCaptcha, (task,)) + credits = self.getCredits() - else: - self.logError(_("Your Captcha 9kw.eu Account has not enough credits")) + if not credits: + self.logError(_("Your captcha 9kw.eu account has not enough credits")) + return - def captchaCorrect(self, task): - if "ticket" in task.data: + queue = min(self.getConfig("queue"), 999) + timeout = min(max(self.getConfig("timeout"), 300), 3999) + pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1) - 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: %s" % response) + for _i in xrange(5): + servercheck = getURL("http://www.9kw.eu/grafik/servercheck.txt") + if queue < re.search(r'queue=(\d+)', servercheck).group(1): + break - except BadHeader, e: - self.logError("Could not send correct request.", str(e)) + sleep(10) else: - self.logError("No CaptchaID for correct request (task %s) found." % task) + self.fail(_("Too many captchas in queue")) - def captchaInvalid(self, task): - if "ticket" in task.data: + for opt in str(self.getConfig("hoster_options").split('|')): + details = map(str.strip, opt.split(':')) - 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: %s" % response) + if not details or details[0].lower() != pluginname.lower(): + continue - except BadHeader, e: - self.logError("Could not send refund request.", str(e)) + for d in details: + hosteroption = d.split("=") + + if (len(hosteroption) > 1 + and hosteroption[0].lower() == 'timeout' + and hosteroption[1].isdigit()): + timeout = int(hosteroption[1]) + + break + + task.handler.append(self) + + task.setWaiting(timeout) + + self._processCaptcha(task) + + + def _captchaResponse(self, task, correct): + type = "correct" if correct else "refund" + + if 'ticket' not in task.data: + self.logDebug("No CaptchaID for %s request (task: %s)" % (type, task)) + return + + passkey = self.getConfig("passkey") + + for _i in xrange(3): + res = getURL(self.API_URL, + get={'action' : "usercaptchacorrectback", + 'apikey' : passkey, + 'api_key': passkey, + 'correct': "1" if correct else "2", + 'pyload' : "1", + 'source' : "pyload", + 'id' : task.data["ticket"]}) + + self.logDebug("Request %s: %s" % (type, res)) + + if res == "OK": + break + + sleep(5) else: - self.logError("No CaptchaID for not correct request (task %s) found." % task) + self.logDebug("Could not send %s request: %s" % (type, res)) + + + def captchaCorrect(self, task): + self._captchaResponse(task, True) + + + def captchaInvalid(self, task): + self._captchaResponse(task, False) diff --git a/module/plugins/hooks/CaptchaBrotherhood.py b/module/plugins/hooks/CaptchaBrotherhood.py index 54dfbe4d6..b6e38d8bb 100644 --- a/module/plugins/hooks/CaptchaBrotherhood.py +++ b/module/plugins/hooks/CaptchaBrotherhood.py @@ -1,28 +1,18 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" from __future__ import with_statement -from thread import start_new_thread - -import pycurl import StringIO -from urllib import urlencode +import pycurl + +try: + from PIL import Image +except ImportError: + import Image + +from thread import start_new_thread from time import sleep -from PIL import Image +from urllib import urlencode from module.network.RequestFactory import getURL, getRequest from module.plugins.Hook import Hook @@ -33,47 +23,58 @@ 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" - __version__ = "0.05" - __type__ = "hook" + __name__ = "CaptchaBrotherhood" + __type__ = "hook" + __version__ = "0.06" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), + __config__ = [("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") + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("zoidberg", "zoidberg@mujmail.cz")] + API_URL = "http://www.captchabrotherhood.com/" + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + def setup(self): - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + 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) + res = getURL(self.API_URL + "askCredits.aspx", + get={"username": self.getConfig("username"), "password": self.getConfig("passkey")}) + if not res.startswith("OK"): + raise CaptchaBrotherhoodException(res) else: - credits = int(response[3:]) + credits = int(res[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) @@ -105,34 +106,36 @@ class CaptchaBrotherhood(Hook): try: req.c.perform() - response = req.getResponse() + res = req.getResponse() except Exception, e: raise CaptchaBrotherhoodException("Submit captcha image failed") req.close() - if not response.startswith("OK"): - raise CaptchaBrotherhoodException(response[1]) + if not res.startswith("OK"): + raise CaptchaBrotherhoodException(res[1]) - ticket = response[3:] + ticket = res[3:] - for _ in xrange(15): + for _i in xrange(15): sleep(5) - response = self.get_api("askCaptchaResult", ticket) - if response.startswith("OK-answered"): - return ticket, response[12:] + res = self.get_api("askCaptchaResult", ticket) + if res.startswith("OK-answered"): + return ticket, res[12:] raise CaptchaBrotherhoodException("No solution received in time") + def get_api(self, api, ticket): - response = getURL("%s%s.aspx" % (self.API_URL, api), + res = 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) + if not res.startswith("OK"): + raise CaptchaBrotherhoodException("Unknown response: %s" % res) + + return res - return response def newCaptchaTask(self, task): if "service" in task.data: @@ -153,11 +156,13 @@ class CaptchaBrotherhood(Hook): task.setWaiting(100) start_new_thread(self.processCaptcha, (task,)) else: - self.logInfo("Your CaptchaBrotherhood Account has not enough credits") + 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']) + res = self.get_api("complainCaptcha", task.data['ticket']) + def processCaptcha(self, task): c = task.captchaFile diff --git a/module/plugins/hooks/Checksum.py b/module/plugins/hooks/Checksum.py index a51eb4b47..8d9f8f981 100644 --- a/module/plugins/hooks/Checksum.py +++ b/module/plugins/hooks/Checksum.py @@ -1,29 +1,16 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - from __future__ import with_statement + import hashlib +import re import zlib + from os import remove from os.path import getsize, isfile, splitext -import re -from module.utils import save_join, fs_encode from module.plugins.Hook import Hook +from module.utils import save_join, fs_encode def computeChecksum(local_file, algorithm): @@ -31,7 +18,7 @@ def computeChecksum(local_file, algorithm): h = getattr(hashlib, algorithm)() with open(local_file, 'rb') as f: - for chunk in iter(lambda: f.read(128 * h.block_size), b''): + for chunk in iter(lambda: f.read(128 * h.block_size), ''): h.update(chunk) return h.hexdigest() @@ -41,7 +28,7 @@ def computeChecksum(local_file, algorithm): last = 0 with open(local_file, 'rb') as f: - for chunk in iter(lambda: f.read(8192), b''): + for chunk in iter(lambda: f.read(8192), ''): last = hf(chunk, last) return "%x" % last @@ -51,30 +38,39 @@ def computeChecksum(local_file, algorithm): class Checksum(Hook): - __name__ = "Checksum" - __version__ = "0.12" - __type__ = "hook" + __name__ = "Checksum" + __type__ = "hook" + __version__ = "0.15" - __config__ = [("activated", "bool", "Activated", False), + __config__ = [("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") - __author_mail__ = ("zoidberg@mujmail.cz", "vuolter@gmail.com") + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("Walter Purcaro", "vuolter@gmail.com"), + ("stickell", "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>.+)$'} + 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>.+)$'} + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass def coreReady(self): - if not self.config['general']['checksum']: - self.logInfo("Checksum validation is disabled in general configuration") + if not self.getConfig("check_checksum"): + self.logInfo(_("Checksum validation is disabled in plugin configuration")) + def setup(self): self.algorithms = sorted( @@ -82,6 +78,7 @@ class Checksum(Hook): 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. @@ -89,10 +86,15 @@ class Checksum(Hook): 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)): + 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)): + + elif hasattr(pyfile.plugin, "api_data") and isinstance(pyfile.plugin.api_data, dict): data = pyfile.plugin.api_data.copy() + + # elif hasattr(pyfile.plugin, "info") and isinstance(pyfile.plugin.info, dict): + # data = pyfile.plugin.info.copy() + else: return @@ -113,12 +115,12 @@ class Checksum(Hook): 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.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.config['general']['checksum']: + if data and self.getConfig("check_checksum"): if "checksum" in data: data['md5'] = data['checksum'] @@ -127,17 +129,18 @@ class Checksum(Hook): 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).' % + 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)" % + 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: %s" % key.upper()) + self.logWarning(_("Unsupported hashing algorithm"), key.upper()) else: - self.logWarning("Unable to validate checksum for file %s" % pyfile.name) + self.logWarning(_("Unable to validate checksum for file: ") + pyfile.name) + def checkFailed(self, pyfile, local_file, msg): check_action = self.getConfig("check_action") @@ -147,26 +150,26 @@ class Checksum(Hook): 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) + pyfile.plugin.retry(max_tries, self.getConfig("wait_time"), msg) elif retry_action == "nothing": return elif check_action == "nothing": return pyfile.plugin.fail(reason=msg) + def packageFinished(self, pypack): download_folder = save_join(self.config['general']['download_folder'], pypack.folder, "") for link in pypack.getChildren().itervalues(): file_type = splitext(link['name'])[1][1:].lower() - #self.logDebug(link, file_type) if file_type not in self.formats: continue hash_file = fs_encode(save_join(download_folder, link['name'])) if not isfile(hash_file): - self.logWarning("File not found: %s" % link['name']) + self.logWarning(_("File not found"), link['name']) continue with open(hash_file) as f: @@ -176,12 +179,12 @@ class Checksum(Hook): data = m.groupdict() self.logDebug(link['name'], data) - local_file = fs_encode(save_join(download_folder, data['name'])) + local_file = fs_encode(save_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)) + 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'])) + self.logWarning(_("%s checksum for file %s does not match (%s != %s)") % + (algorithm, data['NAME'], checksum, data['HASH'])) diff --git a/module/plugins/hooks/ClickAndLoad.py b/module/plugins/hooks/ClickAndLoad.py index 3c47d30ce..04aac2f10 100644 --- a/module/plugins/hooks/ClickAndLoad.py +++ b/module/plugins/hooks/ClickAndLoad.py @@ -1,53 +1,11 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - import socket import thread from module.plugins.Hook import Hook -class ClickAndLoad(Hook): - __name__ = "ClickAndLoad" - __version__ = "0.22" - __type__ = "hook" - - __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() @@ -89,3 +47,31 @@ def forward(source, destination): else: #source.shutdown(socket.SHUT_RD) destination.shutdown(socket.SHUT_WR) + + +class ClickAndLoad(Hook): + __name__ = "ClickAndLoad" + __type__ = "hook" + __version__ = "0.24" + + __config__ = [("activated", "bool", "Activated", True), + ("extern", "bool", "Allow external link adding", False)] + + __description__ = """Click'N'Load hook plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.de"), + ("mkaay", "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")) diff --git a/module/plugins/hooks/DeathByCaptcha.py b/module/plugins/hooks/DeathByCaptcha.py index 64ed2280f..f03ac4567 100644 --- a/module/plugins/hooks/DeathByCaptcha.py +++ b/module/plugins/hooks/DeathByCaptcha.py @@ -1,31 +1,18 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" from __future__ import with_statement -from thread import start_new_thread +import re + +from base64 import b64encode from pycurl import FORM_FILE, HTTPHEADER +from thread import start_new_thread from time import sleep -from base64 import b64encode -import re -from module.network.RequestFactory import getRequest +from module.common.json_layer import json_loads from module.network.HTTPRequest import BadHeader +from module.network.RequestFactory import getRequest from module.plugins.Hook import Hook -from module.common.json_layer import json_loads class DeathByCaptchaException(Exception): @@ -38,44 +25,56 @@ class DeathByCaptchaException(Exception): '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" - __version__ = "0.03" - __type__ = "hook" + __name__ = "DeathByCaptcha" + __type__ = "hook" + __version__ = "0.04" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), + __config__ = [("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") + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("zoidberg", "zoidberg@mujmail.cz")] + API_URL = "http://api.dbcapi.me/api/" + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + def setup(self): - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + def call_api(self, api="captcha", post=False, multipart=False): req = getRequest() @@ -87,18 +86,18 @@ class DeathByCaptcha(Hook): post.update({"username": self.getConfig("username"), "password": self.getConfig("passkey")}) - response = None + res = None try: json = req.load("%s%s" % (self.API_URL, api), post=post, multipart=multipart) self.logDebug(json) - response = json_loads(json) + res = json_loads(json) - if "error" in response: - raise DeathByCaptchaException(response['error']) - elif "status" not in response: - raise DeathByCaptchaException(str(response)) + if "error" in res: + raise DeathByCaptchaException(res['error']) + elif "status" not in res: + raise DeathByCaptchaException(str(res)) except BadHeader, e: if 403 == e.code: @@ -115,27 +114,30 @@ class DeathByCaptcha(Hook): finally: req.close() - return response + return res + def getCredits(self): - response = self.call_api("user", True) + res = self.call_api("user", True) - if 'is_banned' in response and response['is_banned']: + if 'is_banned' in res and res['is_banned']: raise DeathByCaptchaException('banned') - elif 'balance' in response and 'rate' in response: - self.info.update(response) + elif 'balance' in res and 'rate' in res: + self.info.update(res) else: - raise DeathByCaptchaException(response) + raise DeathByCaptchaException(res) + def getStatus(self): - response = self.call_api("status", False) + res = self.call_api("status", False) - if 'is_service_overloaded' in response and response['is_service_overloaded']: + if 'is_service_overloaded' in res and res['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")): + #workaround multipart-post bug in HTTPRequest.py + if re.match("^\w*$", self.getConfig("passkey")): multipart = True data = (FORM_FILE, captcha) else: @@ -144,25 +146,26 @@ class DeathByCaptcha(Hook): data = f.read() data = "base64:" + b64encode(data) - response = self.call_api("captcha", {"captchafile": data}, multipart) + res = self.call_api("captcha", {"captchafile": data}, multipart) - if "captcha" not in response: - raise DeathByCaptchaException(response) - ticket = response['captcha'] + if "captcha" not in res: + raise DeathByCaptchaException(res) + ticket = res['captcha'] - for _ in xrange(24): + for _i in xrange(24): sleep(5) - response = self.call_api("captcha/%d" % ticket, False) - if response['text'] and response['is_correct']: + res = self.call_api("captcha/%d" % ticket, False) + if res['text'] and res['is_correct']: break else: raise DeathByCaptchaException('timed-out') - result = response['text'] - self.logDebug("result %s : %s" % (ticket, result)) + result = res['text'] + self.logDebug("Result %s : %s" % (ticket, result)) return ticket, result + def newCaptchaTask(self, task): if "service" in task.data: return False @@ -184,8 +187,9 @@ class DeathByCaptcha(Hook): 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)) + 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) @@ -193,15 +197,19 @@ class DeathByCaptcha(Hook): 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) + res = 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: diff --git a/module/plugins/hooks/DebridItaliaCom.py b/module/plugins/hooks/DebridItaliaCom.py index 1a081da7a..e31bc98d7 100644 --- a/module/plugins/hooks/DebridItaliaCom.py +++ b/module/plugins/hooks/DebridItaliaCom.py @@ -1,43 +1,26 @@ # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU Affero General Public License as # -# published by the Free Software Foundation, either version 3 of the # -# License, or (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU Affero General Public License for more details. # -# # -# You should have received a copy of the GNU Affero General Public License # -# along with this program. If not, see <http://www.gnu.org/licenses/>. # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster - - -class DebridItaliaCom(MultiHoster): - __name__ = "DebridItaliaCom" - __version__ = "0.07" - __type__ = "hook" - - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + +import re + +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook + + +class DebridItaliaCom(MultiHook): + __name__ = "DebridItaliaCom" + __type__ = "hook" + __version__ = "0.10" + + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it"), + ("Walter Purcaro", "vuolter@gmail.com")] 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"] + return getURL("http://debriditalia.com/api.php", get={'hosts': ""}).replace('"', '').split(',') diff --git a/module/plugins/hooks/DeleteFinished.py b/module/plugins/hooks/DeleteFinished.py index 1c80facc9..5d2b78d50 100644 --- a/module/plugins/hooks/DeleteFinished.py +++ b/module/plugins/hooks/DeleteFinished.py @@ -1,36 +1,24 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - from module.database import style from module.plugins.Hook import Hook class DeleteFinished(Hook): - __name__ = "DeleteFinished" - __version__ = "1.09" - __type__ = "hook" + __name__ = "DeleteFinished" + __type__ = "hook" + __version__ = "1.11" __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" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + # event_list = ["pluginConfigChanged"] ## overwritten methods ## @@ -38,47 +26,54 @@ class DeleteFinished(Hook): 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')) + 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: + 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.pluginConfigChanged(self.__name__, '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 %s' % func) + 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/module/plugins/hooks/DownloadScheduler.py b/module/plugins/hooks/DownloadScheduler.py index 070c0634e..4996e212d 100644 --- a/module/plugins/hooks/DownloadScheduler.py +++ b/module/plugins/hooks/DownloadScheduler.py @@ -1,47 +1,40 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - Original idea by new.cze -""" - import re + from time import localtime from module.plugins.Hook import Hook class DownloadScheduler(Hook): - __name__ = "DownloadScheduler" - __version__ = "0.21" - __type__ = "hook" + __name__ = "DownloadScheduler" + __type__ = "hook" + __version__ = "0.22" - __config__ = [("activated", "bool", "Activated", False), - ("timetable", "str", "List time periods as hh:mm full or number(kB/s)", + __config__ = [("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") + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it")] + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass 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") @@ -49,7 +42,7 @@ class DownloadScheduler(Hook): 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") + self.logError(_("Invalid schedule")) return t0 = localtime() @@ -69,10 +62,11 @@ class DownloadScheduler(Hook): 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.logInfo(_("Stopping download server. (Running downloads will %sbe aborted.)") % '' if abort else _('not ')) self.core.api.pauseServer() if abort: self.core.api.stopAllDownloads() @@ -80,10 +74,10 @@ class DownloadScheduler(Hook): self.core.api.unpauseServer() if speed > 0: - self.logInfo("Setting download speed to %d kB/s" % speed) + 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.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/module/plugins/hooks/EasybytezCom.py b/module/plugins/hooks/EasybytezCom.py index 1cf5e40ae..0dab2a7fe 100644 --- a/module/plugins/hooks/EasybytezCom.py +++ b/module/plugins/hooks/EasybytezCom.py @@ -2,21 +2,20 @@ import re -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class EasybytezCom(MultiHoster): - __name__ = "EasybytezCom" - __version__ = "0.03" - __type__ = "hook" +class EasybytezCom(MultiHook): + __name__ = "EasybytezCom" + __type__ = "hook" + __version__ = "0.04" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] def getHoster(self): @@ -24,14 +23,17 @@ class EasybytezCom(MultiHoster): user = self.account.selectAccount()[0] try: - req = self.account.getAccountRequest(user) + 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(',') + hosters = re.search(r'</textarea>\s*Supported sites:(.*)', page).group(1).split(',') + except Exception, e: + self.logWarning(_("Unable to load supported hoster list, using last known")) 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"] + + hosters = ["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"] + finally: + return hosters diff --git a/module/plugins/hooks/Ev0InFetcher.py b/module/plugins/hooks/Ev0InFetcher.py deleted file mode 100644 index 3a98abbf7..000000000 --- a/module/plugins/hooks/Ev0InFetcher.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- - -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" -from time import mktime, time - -from module.lib import feedparser -from module.plugins.Hook import Hook - - -class Ev0InFetcher(Hook): - __name__ = "Ev0InFetcher" - __version__ = "0.21" - __type__ = "hook" - - __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("Ev0InFetcher: 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: - #self.logDebug("Ev0InFetcher: no new episodes 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("Ev0InFetcher: cleaned '%s' record" % show) diff --git a/module/plugins/hooks/ExpertDecoders.py b/module/plugins/hooks/ExpertDecoders.py index 9e151f8f6..54de8eb53 100644 --- a/module/plugins/hooks/ExpertDecoders.py +++ b/module/plugins/hooks/ExpertDecoders.py @@ -1,62 +1,55 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" from __future__ import with_statement -from thread import start_new_thread +from base64 import b64encode from pycurl import LOW_SPEED_TIME +from thread import start_new_thread from uuid import uuid4 -from base64 import b64encode -from module.network.RequestFactory import getURL, getRequest from module.network.HTTPRequest import BadHeader - +from module.network.RequestFactory import getURL, getRequest from module.plugins.Hook import Hook class ExpertDecoders(Hook): - __name__ = "ExpertDecoders" - __version__ = "0.01" - __type__ = "hook" + __name__ = "ExpertDecoders" + __type__ = "hook" + __version__ = "0.03" - __config__ = [("activated", "bool", "Activated", False), - ("force", "bool", "Force CT even if client is connected", False), + __config__ = [("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") + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("zoidberg", "zoidberg@mujmail.cz")] + API_URL = "http://www.fasttypers.org/imagepost.ashx" + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + def setup(self): - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + def getCredits(self): - response = getURL(self.API_URL, post={"key": self.getConfig("passkey"), "action": "balance"}) + res = 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) + if res.isdigit(): + self.logInfo(_("%s credits left") % res) + self.info['credits'] = credits = int(res) return credits else: - self.logError(response) + self.logError(res) return 0 + def processCaptcha(self, task): task.data['ticket'] = ticket = uuid4() result = None @@ -64,21 +57,21 @@ class ExpertDecoders(Hook): with open(task.captchaFile, 'rb') as f: data = f.read() data = b64encode(data) - #self.logDebug("%s: %s : %s" % (ticket, task.captchaFile, 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"), + 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)) + self.logDebug("Result %s : %s" % (ticket, result)) task.setResult(result) + def newCaptchaTask(self, task): if not task.isTextual(): return False @@ -97,13 +90,14 @@ class ExpertDecoders(Hook): 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: %s" % response) + res = getURL(self.API_URL, + post={'action': "refund", 'key': self.getConfig("passkey"), 'gen_task_id': task.data['ticket']}) + self.logInfo(_("Request refund"), res) except BadHeader, e: - self.logError("Could not send refund request.", str(e)) + self.logError(_("Could not send refund request"), e) diff --git a/module/plugins/hooks/ExternalScripts.py b/module/plugins/hooks/ExternalScripts.py index 7f76df1cd..a35e47c03 100644 --- a/module/plugins/hooks/ExternalScripts.py +++ b/module/plugins/hooks/ExternalScripts.py @@ -1,21 +1,8 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - import subprocess + +from itertools import chain from os import listdir, access, X_OK, makedirs from os.path import join, exists, basename, abspath @@ -24,25 +11,38 @@ from module.utils import save_join class ExternalScripts(Hook): - __name__ = "ExternalScripts" - __version__ = "0.23" - __type__ = "hook" + __name__ = "ExternalScripts" + __type__ = "hook" + __version__ = "0.25" __config__ = [("activated", "bool", "Activated", True)] __description__ = """Run external scripts""" - __author_name__ = ("mkaay", "RaNaN", "spoob") - __author_mail__ = ("mkaay@mkaay.de", "ranan@pyload.org", "spoob@pyload.org") + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de"), + ("RaNaN", "ranan@pyload.org"), + ("spoob", "spoob@pyload.org"), + ("Walter Purcaro", "vuolter@gmail.com")] + + + event_list = ["archive_extracted", "package_extracted", "all_archives_extracted", "all_archives_processed", + "allDownloadsFinished", "allDownloadsProcessed"] + - event_list = ["unrarFinished", "allDownloadsFinished", "allDownloadsProcessed"] + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass def setup(self): self.scripts = {} - folders = ["download_preparing", "download_finished", "package_finished", - "before_reconnect", "after_reconnect", "unrar_finished", - "all_dls_finished", "all_dls_processed"] + 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] = [] @@ -52,7 +52,8 @@ class ExternalScripts(Hook): for script_type, names in self.scripts.iteritems(): if names: - self.logInfo((_("Installed scripts for %s: ") % script_type) + ", ".join([basename(x) for x in names])) + self.logInfo(_("Installed scripts for"), script_type, ", ".join([basename(x) for x in names])) + def initPluginType(self, folder, path): if not exists(path): @@ -71,48 +72,75 @@ class ExternalScripts(Hook): 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 %(script)s: %(cmd)s" % {"script": abspath(script), "cmd": " ".join(cmd)}) + 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": str(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']: - self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, - save_join(self.config['general']['download_folder'], - pyfile.package().folder, pyfile.name), pyfile.id) + filename = save_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 = self.config['general']['download_folder'] - folder = save_join(folder, pypack.folder) - + folder = save_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 unrarFinished(self, folder, fname): - for script in self.scripts['unrar_finished']: - self.callScript(script, folder, fname) + + 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 self.scripts['all_dls_finished']: + for script in chain(self.scripts['all_downloads_finished'], self.scripts['all_dls_finished']): self.callScript(script) + def allDownloadsProcessed(self): - for script in self.scripts['all_dls_processed']: + for script in chain(self.scripts['all_downloads_processed'], self.scripts['all_dls_processed']): self.callScript(script) diff --git a/module/plugins/hooks/ExtractArchive.py b/module/plugins/hooks/ExtractArchive.py index c307deb8f..af78ffc93 100644 --- a/module/plugins/hooks/ExtractArchive.py +++ b/module/plugins/hooks/ExtractArchive.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- -import sys +from __future__ import with_statement + import os +import sys + +from copy import copy from os import remove, chmod, makedirs -from os.path import exists, basename, isfile, isdir, join +from os.path import exists, basename, isfile, isdir from traceback import print_exc -from copy import copy # monkey patch bug in python 2.6 and lower -# see http://bugs.python.org/issue6122 -# http://bugs.python.org/issue1236 -# http://bugs.python.org/issue1731717 +# 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": - from subprocess import Popen import errno + from subprocess import Popen def _eintr_retry_call(func, *args): while True: @@ -24,6 +25,7 @@ if sys.version_info < (2, 7) and os.name != "nt": continue raise + # unsued timeout option for older python version def wait(self, timeout=0): """Wait for child process to terminate. Returns returncode @@ -44,48 +46,54 @@ if sys.version_info < (2, 7) and os.name != "nt": Popen.wait = wait if os.name != "nt": + from grp import getgrnam from os import chown from pwd import getpwnam - from grp import getgrnam -from module.utils import save_join, fs_encode from module.plugins.Hook import Hook, threaded, Expose -from module.plugins.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword +from module.plugins.internal.Extractor import ArchiveError, CRCError, PasswordError +from module.utils import save_join, uniqify class ExtractArchive(Hook): - """ - Provides: unrarFinished (folder, filename) - """ - __name__ = "ExtractArchive" - __version__ = "0.16" - __type__ = "hook" - - __config__ = [("activated", "bool", "Activated", True), - ("fullpath", "bool", "Extract full path", True), - ("overwrite", "bool", "Overwrite files", True), - ("passwordfile", "file", "password file", "unrar_passwords.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)] + __name__ = "ExtractArchive" + __type__ = "hook" + __version__ = "1.02" + + __config__ = [("activated" , "bool" , "Activated" , True ), + ("fullpath" , "bool" , "Extract full path" , True ), + ("overwrite" , "bool" , "Overwrite files" , False ), + ("keepbroken" , "bool" , "Extract broken archives" , False ), + ("repair" , "bool" , "Repair broken archives" , True ), + ("passwordfile" , "file" , "Store passwords in file" , "archive_password.txt" ), + ("delete" , "bool" , "Delete archive when successfully extracted", False ), + ("subfolder" , "bool" , "Create subfolder for each package" , False ), + ("destination" , "folder", "Extract files to" , "" ), + ("extensions" , "str" , "Extract the following extensions" , "7z,bz2,bzip2,gz,gzip,lha,lzh,lzma,rar,tar,taz,tbz,tbz2,tgz,xar,xz,z,zip"), + ("excludefiles" , "str" , "Don't extract the following files" , "*.nfo,*.DS_Store,index.dat,thumb.db" ), + ("recursive" , "bool" , "Extract archives in archives" , True ), + ("queue" , "bool" , "Wait for all downloads to be finished" , True ), + ("renice" , "int" , "CPU Priority" , 0 )] __description__ = """Extract different kind of archives""" - __author_name__ = ("pyLoad Team", "AndroKev") - __author_mail__ = ("admin@pyload.org", "@pyloadforum") + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + event_list = ["allDownloadsProcessed"] + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + def setup(self): - self.plugins = [] + self.plugins = [] self.passwords = [] names = [] - for p in ("UnRar", "UnZip"): + for p in ("UnRar", "SevenZip", "UnZip"): try: module = self.core.pluginManager.loadModule("internal", p) klass = getattr(module, p) @@ -97,12 +105,12 @@ class ExtractArchive(Hook): if e.errno == 2: self.logInfo(_("No %s installed") % p) else: - self.logWarning(_("Could not activate %s") % p, str(e)) + self.logWarning(_("Could not activate %s") % p, e) if self.core.debug: print_exc() except Exception, e: - self.logWarning(_("Could not activate %s") % p, str(e)) + self.logWarning(_("Could not activate %s") % p, e) if self.core.debug: print_exc() @@ -114,138 +122,230 @@ class ExtractArchive(Hook): # 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(pypack.id) + self.queue.append(pid) else: - self.manager.startThread(self.extract, [pypack.id]) + self.extractPackage(pid) + @threaded def allDownloadsProcessed(self, thread): local = copy(self.queue) + del self.queue[:] - self.extract(local, thread) + + 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 = [] + + clearlist = lambda string: [x.lstrip('.') for x in string.replace(' ', '').replace(',', '|').replace(';', '|').split('|')] + + destination = self.getConfig("destination") + subfolder = self.getConfig("subfolder") + fullpath = self.getConfig("fullpath") + overwrite = self.getConfig("overwrite") + extensions = clearlist(self.getConfig("extensions")) + excludefiles = clearlist(self.getConfig("excludefiles")) + renice = self.getConfig("renice") + recursive = self.getConfig("recursive") + delete = self.getConfig("delete") + keepbroken = self.getConfig("keepbroken") + + if extensions: + self.logDebug("Extensions allowed: %s" % "|.".join(extensions)) + # reload from txt file self.reloadPasswords() # dl folder dl = self.config['general']['download_folder'] - extracted = [] - #iterate packages -> plugins -> targets for pid in ids: p = self.core.files.getPackage(pid) - self.logInfo(_("Check package %s") % p.name) + self.logInfo(_("Check package: %s") % p.name) if not p: continue # determine output folder - out = save_join(dl, p.folder, "") - # force trailing slash - - if self.getConfig("destination") and self.getConfig("destination").lower() != "none": - - out = save_join(dl, p.folder, self.getConfig("destination"), "") - #relative to package folder if destination is relative, otherwise absolute path overwrites them + out = save_join(dl, p.folder, destination, "") #: force trailing slash - if self.getConfig("subfolder"): - out = join(out, fs_encode(p.folder)) + if subfolder: + out = save_join(out, p.folder) - if not exists(out): - makedirs(out) + if not exists(out): + makedirs(out) files_ids = [(save_join(dl, p.folder, x['name']), x['id']) for x in p.getChildren().itervalues()] - matched = False + matched = False + success = True # check as long there are unseen files while files_ids: new_files_ids = [] + if extensions: + files_ids = [(file, id) for file, id in files_ids if filter(lambda ext: file.endswith(ext), extensions)] + 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 extracted: + if target in processed: self.logDebug(basename(target), "skipped") continue - extracted.append(target) # prevent extracting same file twice - klass = plugin(self, target, out, self.getConfig("fullpath"), self.getConfig("overwrite"), self.getConfig("excludefiles"), - self.getConfig("renice")) - klass.init() + processed.append(target) # prevent extracting same file twice + + self.logInfo(basename(target), _("Extract to: %s") % out) + try: + klass = plugin(self, + target, + out, + p.password, + fullpath, + overwrite, + excludefiles, + renice, + delete, + keepbroken) + klass.init() + + new_files = self._extract(klass, fid, thread) + + except Exception, e: + self.logError(basename(target), e) + new_files = None + + if new_files is None: + self.logWarning(basename(target), _("No files extracted")) + success = False + continue - self.logInfo(basename(target), _("Extract to %s") % out) - new_files = self.startExtracting(klass, fid, p.password.strip().splitlines(), thread) - self.logDebug("Extracted: %s" % new_files) + self.logDebug("Extracted files: %s" % new_files) self.setPermissions(new_files) for file in new_files: if not exists(file): - self.logDebug("new file %s does not exists" % file) + self.logDebug("New file %s does not exists" % file) continue - if self.getConfig("recursive") and isfile(file): + 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 not matched: + 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")) - def startExtracting(self, plugin, fid, passwords, thread): + if not matched or not success and subfolder: + try: + os.rmdir(out) + except OSError: + pass + + return True if not failed else False + + + def _extract(self, plugin, fid, thread): pyfile = self.core.files.getFile(fid) - if not pyfile: - return [] pyfile.setCustomStatus(_("extracting")) thread.addActive(pyfile) # keep this file until everything is done try: - progress = lambda x: pyfile.setProgress(x) - success = False + progress = lambda x: pyfile.setProgress(x) + encrypted = False + passwords = self.getPasswords() - if not plugin.checkArchive(): + try: + self.logInfo(basename(plugin.file), "Verifying...") + + tmp_password = plugin.password + plugin.password = "" #: Force verifying without password + + plugin.verify() + + except PasswordError: + encrypted = True + + except CRCError: + self.logWarning(basename(plugin.file), _("Archive damaged")) + + if not self.getConfig("repair"): + raise CRCError + + elif plugin.repair(): + self.logInfo(basename(plugin.file), _("Successfully repaired")) + + elif not self.getConfig("keepbroken"): + raise ArchiveError(_("Broken archive")) + + else: + self.logInfo(basename(plugin.file), _("All OK")) + + plugin.password = tmp_password + + if not encrypted: plugin.extract(progress) - success = True + else: self.logInfo(basename(plugin.file), _("Password protected")) - self.logDebug("Passwords: %s" % str(passwords)) - pwlist = copy(self.getPasswords()) - #remove already supplied pws from list (only local) - for pw in passwords: - if pw in pwlist: - pwlist.remove(pw) + if plugin.password: + passwords.insert(0, plugin.password) + passwords = uniqify(self.passwords) + self.logDebug("Password: %s" % plugin.password) + else: + self.logDebug("No package password provided") - for pw in passwords + pwlist: + for pw in passwords: try: self.logDebug("Try password: %s" % pw) - if plugin.checkPassword(pw): - plugin.extract(progress, pw) + + if plugin.setPassword(pw): + plugin.extract(progress) self.addPassword(pw) - success = True break - except WrongPassword: - self.logDebug("Password was wrong") + else: + raise PasswordError - if not success: - self.logError(basename(plugin.file), _("Wrong password")) - return [] + except PasswordError: + self.logDebug("Password was wrong") + else: + raise PasswordError if self.core.debug: self.logDebug("Would delete: %s" % ", ".join(plugin.getDeleteFiles())) - if self.getConfig("deletearchive"): + if self.getConfig("delete"): files = plugin.getDeleteFiles() self.logInfo(_("Deleting %s files") % len(files)) for f in files: @@ -255,57 +355,76 @@ class ExtractArchive(Hook): self.logDebug("%s does not exists" % f) self.logInfo(basename(plugin.file), _("Extracting finished")) - self.manager.dispatchEvent("unrarFinished", plugin.out, plugin.file) - return plugin.getExtractedFiles() + extracted_files = plugin.getExtractedFiles() + self.manager.dispatchEvent("archive_extracted", pyfile, plugin.out, plugin.file, extracted_files) + + return extracted_files + + except PasswordError: + self.logError(basename(plugin.file), _("Wrong password" if passwords else "No password found")) + plugin.password = "" - except ArchiveError, e: - self.logError(basename(plugin.file), _("Archive Error"), str(e)) except CRCError: self.logError(basename(plugin.file), _("CRC Mismatch")) + + except ArchiveError, e: + self.logError(basename(plugin.file), _("Archive Error"), e) + except Exception, e: if self.core.debug: print_exc() - self.logError(basename(plugin.file), _("Unknown Error"), str(e)) + self.logError(basename(plugin.file), _("Unknown Error"), e) + + self.manager.dispatchEvent("archive_extract_failed", pyfile) + + self.logError(basename(plugin.file), _("Extract failed")) - return [] @Expose def getPasswords(self): """ List of saved passwords """ return self.passwords + def reloadPasswords(self): - pwfile = self.getConfig("passwordfile") - if not exists(pwfile): - open(pwfile, "wb").close() + passwordfile = self.getConfig("passwordfile") - passwords = [] - f = open(pwfile, "rb") - for pw in f.read().splitlines(): - passwords.append(pw) - f.close() + try: + passwords = [] + with open(passwordfile, "a+") as f: + for pw in f.read().splitlines(): + passwords.append(pw) + + except IOError, e: + self.logError(e) + + else: + self.passwords = passwords - self.passwords = passwords @Expose def addPassword(self, pw): """ Adds a password to saved list""" - pwfile = self.getConfig("passwordfile") + passwordfile = self.getConfig("passwordfile") - if pw in self.passwords: - self.passwords.remove(pw) self.passwords.insert(0, pw) + self.passwords = uniqify(self.passwords) + + try: + with open(passwordfile, "wb") as f: + for pw in self.passwords: + f.write(pw + '\n') + + except IOError, e: + self.logError(e) - f = open(pwfile, "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): @@ -317,5 +436,6 @@ class ExtractArchive(Hook): 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/module/plugins/hooks/FastixRu.py b/module/plugins/hooks/FastixRu.py index aa020ea37..73297eb23 100644 --- a/module/plugins/hooks/FastixRu.py +++ b/module/plugins/hooks/FastixRu.py @@ -1,30 +1,28 @@ # -*- coding: utf-8 -*- -# should be working - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster from module.common.json_layer import json_loads +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class FastixRu(MultiHoster): - __name__ = "FastixRu" - __version__ = "0.02" - __type__ = "hook" +class FastixRu(MultiHook): + __name__ = "FastixRu" + __type__ = "hook" + __version__ = "0.03" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Massimo Rosamilia", "max@spiritix.eu")] def getHoster(self): - page = getURL( - "http://fastix.ru/api_v2/?apikey=5182964c3f8f9a7f0b00000a_kelmFB4n1IrnCDYuIFn2y&sub=allowed_sources") + page = getURL("http://fastix.ru/api_v2", + get={'apikey': "5182964c3f8f9a7f0b00000a_kelmFB4n1IrnCDYuIFn2y", + 'sub' : "allowed_sources"}) host_list = json_loads(page) host_list = host_list['allow'] return host_list diff --git a/module/plugins/hooks/FreeWayMe.py b/module/plugins/hooks/FreeWayMe.py index 3d2f7fcef..0b71fc35b 100644 --- a/module/plugins/hooks/FreeWayMe.py +++ b/module/plugins/hooks/FreeWayMe.py @@ -1,41 +1,25 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class FreeWayMe(MultiHoster): - __name__ = "FreeWayMe" - __version__ = "0.11" - __type__ = "hook" +class FreeWayMe(MultiHook): + __name__ = "FreeWayMe" + __type__ = "hook" + __version__ = "0.12" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Nicolas Giese", "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: %s" % hostis) + 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/module/plugins/hooks/HotFolder.py b/module/plugins/hooks/HotFolder.py index 08ef93812..b0b59e2ba 100644 --- a/module/plugins/hooks/HotFolder.py +++ b/module/plugins/hooks/HotFolder.py @@ -1,82 +1,66 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - -from os import makedirs -from os import listdir -from os.path import exists -from os.path import join -from os.path import isfile -from shutil import move +from __future__ import with_statement + import time +from os import listdir, makedirs +from os.path import exists, isfile, join +from shutil import move + from module.plugins.Hook import Hook +from module.utils import fs_encode, save_join class HotFolder(Hook): - __name__ = "HotFolder" - __version__ = "0.11" - __type__ = "hook" + __name__ = "HotFolder" + __type__ = "hook" + __version__ = "0.12" - __config__ = [("activated", "bool", "Activated", False), - ("folder", "str", "Folder to observe", "container"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "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")) + folder = fs_encode(self.getConfig("folder")) + + try: + if not exists(join(folder, "finished")): + makedirs(join(folder, "finished")) - if self.getConfig("watch_file"): + if self.getConfig("watch_file"): + with open(fs_encode(self.getConfig("file")), "a+") as f: + content = f.read().strip() - if not exists(self.getConfig("file")): - 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(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")) + with open(save_join(folder, "finished", name), "wb") as f: + f.write(content) - f = open(join(self.getConfig("folder"), "finished", name), "wb") - f.write(content) - f.close() + self.core.api.addPackage(f.name, [f.name], 1) - self.core.api.addPackage(f.name, [f.name], 1) + for f in listdir(folder): + path = join(folder, f) - 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 - if not isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."): - continue + newpath = join(folder, "finished", f if self.getConfig("keep") else "tmp_" + f) + move(path, newpath) - 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) - self.logInfo(_("Added %s from HotFolder") % f) - self.core.api.addPackage(f, [newpath], 1) + except IOError, e: + self.logError(e) diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py index a29874469..efd4e411d 100644 --- a/module/plugins/hooks/IRCInterface.py +++ b/module/plugins/hooks/IRCInterface.py @@ -1,45 +1,32 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" +import re +import socket +import ssl +import time +from pycurl import FORM_FILE from select import select -import socket from threading import Thread -import time from time import sleep from traceback import print_exc -import re -from pycurl import FORM_FILE -from module.plugins.Hook import Hook +from module.Api import PackageDoesNotExists, FileDoesNotExists from module.network.RequestFactory import getURL +from module.plugins.Hook import Hook from module.utils import formatSize -from module.Api import PackageDoesNotExists, FileDoesNotExists class IRCInterface(Thread, Hook): - __name__ = "IRCInterface" - __version__ = "0.11" - __type__ = "hook" + __name__ = "IRCInterface" + __type__ = "hook" + __version__ = "0.13" - __config__ = [("activated", "bool", "Activated", False), - ("host", "str", "IRC-Server Address", "Enter your server here!"), + __config__ = [("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"), + ("ssl", "bool", "Use SSL", False), ("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), @@ -47,16 +34,20 @@ class IRCInterface(Thread, Hook): ("captcha", "bool", "Send captcha requests", True)] __description__ = """Connect to irc and let owner perform different tasks""" - __author_name__ = "Jeix" - __author_mail__ = "Jeix@hasnomail.com" + __license__ = "GPLv3" + __authors__ = [("Jeix", "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 + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + def coreReady(self): self.abort = False @@ -65,6 +56,7 @@ class IRCInterface(Thread, Hook): self.start() + def packageFinished(self, pypack): try: if self.getConfig("info_pack"): @@ -72,6 +64,7 @@ class IRCInterface(Thread, Hook): except: pass + def downloadFinished(self, pyfile): try: if self.getConfig("info_file"): @@ -80,6 +73,7 @@ class IRCInterface(Thread, Hook): except: pass + def newCaptchaTask(self, task): if self.getConfig("captcha") and task.isTextual(): task.handler.append(self) @@ -92,19 +86,24 @@ class IRCInterface(Thread, Hook): 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"))) + + if self.getConfig("ssl"): + self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support certificate + 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("pyLoad IRC: Connected to %s!" % host) - self.logInfo("pyLoad IRC: Switching to listening mode!") + self.logInfo(_("Connected to"), host) + self.logInfo(_("Switching to listening mode!")) try: self.main_loop() @@ -113,6 +112,7 @@ class IRCInterface(Thread, Hook): print_exc() self.sock.close() + def main_loop(self): readbuffer = "" while True: @@ -151,6 +151,7 @@ class IRCInterface(Thread, Hook): self.handle_events(msg) + def handle_events(self, msg): if not msg['origin'].split("!", 1)[0] in self.getConfig("owner").split(): return @@ -163,15 +164,15 @@ class IRCInterface(Thread, Hook): # HANDLE CTCP ANTI FLOOD/BOT PROTECTION if msg['text'] == "\x01VERSION\x01": - self.logDebug("Sending CTCP VERSION.") + 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.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 + self.logDebug("Received CTCP LAG") #: don't know how to answer return trigger = "pass" @@ -191,7 +192,8 @@ class IRCInterface(Thread, Hook): for line in res: self.response(line, msg['origin']) except Exception, e: - self.logError("pyLoad IRC: " + repr(e)) + self.logError(e) + def response(self, msg, origin=""): if origin == "": @@ -200,13 +202,15 @@ class IRCInterface(Thread, Hook): 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() + downloads = self.core.api.statusDownloads() if not downloads: return ["INFO: There are no active downloads currently."] @@ -230,8 +234,9 @@ class IRCInterface(Thread, Hook): )) return lines + def event_queue(self, args): - ps = self.api.getQueueData() + ps = self.core.api.getQueueData() if not ps: return ["INFO: There are no packages in queue."] @@ -242,8 +247,9 @@ class IRCInterface(Thread, Hook): return lines + def event_collector(self, args): - ps = self.api.getCollectorData() + ps = self.core.api.getCollectorData() if not ps: return ["INFO: No packages in collector!"] @@ -253,19 +259,21 @@ class IRCInterface(Thread, Hook): 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])) + info = self.core.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>"] @@ -273,7 +281,7 @@ class IRCInterface(Thread, Hook): lines = [] pack = None try: - pack = self.api.getPackageData(int(args[0])) + pack = self.core.api.getPackageData(int(args[0])) except PackageDoesNotExists: return ["ERROR: Package doesn't exists."] @@ -297,6 +305,7 @@ class IRCInterface(Thread, Hook): return lines + def event_more(self, args): if not self.more: return ["No more information to display."] @@ -307,14 +316,17 @@ class IRCInterface(Thread, Hook): return lines + def event_start(self, args): - self.api.unpauseServer() + self.core.api.unpauseServer() return ["INFO: Starting downloads."] + def event_stop(self, args): - self.api.pauseServer() + self.core.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". ', @@ -327,7 +339,7 @@ class IRCInterface(Thread, Hook): count_failed = 0 try: id = int(pack) - pack = self.api.getPackageData(id) + pack = self.core.api.getPackageData(id) if not pack: return ["ERROR: Package doesn't exists."] @@ -337,48 +349,52 @@ class IRCInterface(Thread, Hook): except: # create new package - id = self.api.addPackage(pack, links, 1) + id = self.core.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:])) + ret = self.core.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:])) + ret = self.core.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) + info = self.core.api.getPackageInfo(id) except PackageDoesNotExists: return ["ERROR: Package #%d does not exist." % id] - self.api.pushToQueue(id) + self.core.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): + if not self.core.api.getPackageData(id): return ["ERROR: Package #%d does not exist." % id] - self.api.pullFromQueue(id) + self.core.api.pullFromQueue(id) return ["INFO: Pulled package #%d from queue to collector." % id] + def event_c(self, args): """ captcha answer """ if not args: @@ -391,6 +407,7 @@ class IRCInterface(Thread, Hook): 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)", @@ -414,5 +431,6 @@ class IRCError(Exception): def __init__(self, value): self.value = value + def __str__(self): return repr(self.value) diff --git a/module/plugins/hooks/ImageTyperz.py b/module/plugins/hooks/ImageTyperz.py index 4d7be96e3..f89d64c37 100644 --- a/module/plugins/hooks/ImageTyperz.py +++ b/module/plugins/hooks/ImageTyperz.py @@ -1,24 +1,12 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" from __future__ import with_statement -from thread import start_new_thread -from pycurl import FORM_FILE, LOW_SPEED_TIME + import re + from base64 import b64encode +from pycurl import FORM_FILE, LOW_SPEED_TIME +from thread import start_new_thread from module.network.RequestFactory import getURL, getRequest from module.plugins.Hook import Hook @@ -29,61 +17,74 @@ 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" - __version__ = "0.04" - __type__ = "hook" + __name__ = "ImageTyperz" + __type__ = "hook" + __version__ = "0.05" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), + __config__ = [("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") + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("zoidberg", "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" + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + def setup(self): - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + def getCredits(self): - response = getURL(self.GETCREDITS_URL, post={"action": "REQUESTBALANCE", "username": self.getConfig("username"), - "password": self.getConfig("passkey")}) + res = getURL(self.GETCREDITS_URL, + post={'action': "REQUESTBALANCE", + 'username': self.getConfig("username"), + 'password': self.getConfig("passkey")}) - if response.startswith('ERROR'): - raise ImageTyperzException(response) + if res.startswith('ERROR'): + raise ImageTyperzException(res) try: - balance = float(response) + balance = float(res) except: - raise ImageTyperzException("invalid response") + raise ImageTyperzException("Invalid response") - self.logInfo("Account balance: $%s left" % response) + self.logInfo(_("Account balance: $%s left") % res) 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")): + #workaround multipart-post bug in HTTPRequest.py + if re.match("^\w*$", self.getConfig("passkey")): multipart = True data = (FORM_FILE, captcha) else: @@ -92,24 +93,26 @@ class ImageTyperz(Hook): 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) + res = 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) + if res.startswith("ERROR"): + raise ImageTyperzException(res) else: - data = response.split('|') + data = res.split('|') if len(data) == 2: ticket, result = data else: - raise ImageTyperzException("Unknown response %s" % response) + raise ImageTyperzException("Unknown response: %s" % res) return ticket, result + def newCaptchaTask(self, task): if "service" in task.data: return False @@ -130,18 +133,22 @@ class ImageTyperz(Hook): start_new_thread(self.processCaptcha, (task,)) else: - self.logInfo("Your %s account has not enough credits" % self.__name__) + 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") + res = getURL(self.RESPOND_URL, + post={'action': "SETBADIMAGE", + 'username': self.getConfig("username"), + 'password': self.getConfig("passkey"), + 'imageid': task.data['ticket']}) + + if res == "SUCCESS": + self.logInfo(_("Bad captcha solution received, requested refund")) else: - self.logError("Bad captcha solution received, refund request failed", response) + self.logError(_("Bad captcha solution received, refund request failed"), res) + def processCaptcha(self, task): c = task.captchaFile diff --git a/module/plugins/hooks/LinkdecrypterCom.py b/module/plugins/hooks/LinkdecrypterCom.py index 4e08372fe..b0ce335d0 100644 --- a/module/plugins/hooks/LinkdecrypterCom.py +++ b/module/plugins/hooks/LinkdecrypterCom.py @@ -1,37 +1,25 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - import re -from module.plugins.Hook import Hook from module.network.RequestFactory import getURL +from module.plugins.Hook import Hook from module.utils import remove_chars class LinkdecrypterCom(Hook): - __name__ = "LinkdecrypterCom" - __version__ = "0.19" - __type__ = "hook" - - __config__ = [("activated", "bool", "Activated", False)] + __name__ = "LinkdecrypterCom" + __type__ = "hook" + __version__ = "0.21" __description__ = """Linkdecrypter.com hook plugin""" - __author_name__ = "zoidberg" - __author_mail__ = "zoidberg@mujmail.cz" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass def coreReady(self): @@ -40,9 +28,16 @@ class LinkdecrypterCom(Hook): except Exception, e: self.logError(e) + def loadPatterns(self): - page = getURL("http://linkdecrypter.com/") - m = re.search(r'<b>Supported\(\d+\)</b>: <i>([^+<]*)', page) + html = getURL("http://linkdecrypter.com/") + + m = re.search(r'<title>', html) + if m is None: + self.logError(_("Linkdecrypter site is down")) + return + + m = re.search(r'<b>Supported\(\d+\)</b>: <i>([^+<]*)', html) if m is None: self.logError(_("Crypter list not found")) return @@ -61,10 +56,10 @@ class LinkdecrypterCom(Hook): self.logError(_("Crypter list is empty")) return - regexp = r"https?://([^.]+\.)*?(%s)/.*" % "|".join(online) + 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) + self.logDebug("Loaded pattern: %s" % regexp) diff --git a/module/plugins/hooks/LinksnappyCom.py b/module/plugins/hooks/LinksnappyCom.py index a0e5b8d38..96bf1c0d1 100644 --- a/module/plugins/hooks/LinksnappyCom.py +++ b/module/plugins/hooks/LinksnappyCom.py @@ -1,28 +1,27 @@ # -*- coding: utf-8 -*- -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL from module.common.json_layer import json_loads +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class LinksnappyCom(MultiHoster): - __name__ = "LinksnappyCom" - __version__ = "0.01" - __type__ = "hook" +class LinksnappyCom(MultiHook): + __name__ = "LinksnappyCom" + __type__ = "hook" + __version__ = "0.02" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] def getHoster(self): - json_data = getURL('http://gen.linksnappy.com/lseAPI.php?act=FILEHOSTS') + json_data = getURL("http://gen.linksnappy.com/lseAPI.php", get={'act': "FILEHOSTS"}) json_data = json_loads(json_data) return json_data['return'].keys() diff --git a/module/plugins/hooks/MegaDebridEu.py b/module/plugins/hooks/MegaDebridEu.py index 22945cc0f..f3a0c31ea 100644 --- a/module/plugins/hooks/MegaDebridEu.py +++ b/module/plugins/hooks/MegaDebridEu.py @@ -1,45 +1,30 @@ # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU Affero General Public License as # -# published by the Free Software Foundation, either version 3 of the # -# License, or (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU Affero General Public License for more details. # -# # -# You should have received a copy of the GNU Affero General Public License # -# along with this program. If not, see <http://www.gnu.org/licenses/>. # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL + from module.common.json_layer import json_loads +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class MegaDebridEu(MultiHoster): - __name__ = "MegaDebridEu" - __version__ = "0.02" - __type__ = "hook" +class MegaDebridEu(MultiHook): + __name__ = "MegaDebridEu" + __type__ = "hook" + __version__ = "0.03" - __config__ = [("activated", "bool", "Activated", False), - ("unloadFailing", "bool", "Revert to standard download if download fails", False)] + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("D.Ducatel", "dducatel@je-geek.fr")] def getHoster(self): - reponse = getURL('http://www.mega-debrid.eu/api.php?action=getHosters') + reponse = getURL("http://www.mega-debrid.eu/api.php", get={'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") + self.logError(_("Unable to retrieve hoster list")) host_list = list() return host_list diff --git a/module/plugins/hooks/MergeFiles.py b/module/plugins/hooks/MergeFiles.py index 5d6c208a7..4de45f958 100644 --- a/module/plugins/hooks/MergeFiles.py +++ b/module/plugins/hooks/MergeFiles.py @@ -1,53 +1,47 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" +from __future__ import with_statement import os import re -import traceback -from os.path import join -from module.utils import save_join, fs_encode -from module.plugins.Hook import Hook, threaded +from traceback import print_exc -BUFFER_SIZE = 4096 +from module.plugins.Hook import Hook, threaded +from module.utils import save_join, fs_encode class MergeFiles(Hook): - __name__ = "MergeFiles" - __version__ = "0.12" - __type__ = "hook" + __name__ = "MergeFiles" + __type__ = "hook" + __version__ = "0.13" - __config__ = [("activated", "bool", "Activated", False)] + __config__ = [("activated", "bool", "Activated", True)] __description__ = """Merges parts splitted with hjsplit""" - __author_name__ = "and9000" - __author_mail__ = "me@has-no-mail.com" + __license__ = "GPLv3" + __authors__ = [("and9000", "me@has-no-mail.com")] + + + BUFFER_SIZE = 4096 + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass 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 re.search("\.\d{3}$", data['name']): if data['name'][:-4] not in files: files[data['name'][:-4]] = [] files[data['name'][:-4]].append(data['name']) @@ -60,33 +54,36 @@ class MergeFiles(Hook): download_folder = save_join(download_folder, pack.folder) for name, file_list in files.iteritems(): - self.logInfo("Starting merging of %s" % name) - final_file = open(join(download_folder, fs_encode(name)), "wb") - - for splitted_file in file_list: - self.logDebug("Merging part %s" % 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(BUFFER_SIZE) - if f_buffer: - final_file.write(f_buffer) - size_written += BUFFER_SIZE - pyfile.setProgress((size_written * 100) / s_file_size) - else: - break - s_file.close() - self.logDebug("Finished merging part %s" % 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 %s" % name) + self.logInfo(_("Starting merging of"), name) + + with open(save_join(download_folder, name), "wb") as final_file: + 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: + with open(os.path.join(download_folder, splitted_file), "rb") as s_file: + 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 + self.logDebug("Finished merging part", splitted_file) + + except Exception, e: + print_exc() + + finally: + pyfile.setProgress(100) + pyfile.setStatus("finished") + pyfile.release() + + self.logInfo(_("Finished merging of"), name) diff --git a/module/plugins/hooks/MultiDebridCom.py b/module/plugins/hooks/MultiDebridCom.py deleted file mode 100644 index 3309fb9a8..000000000 --- a/module/plugins/hooks/MultiDebridCom.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU Affero General Public License as # -# published by the Free Software Foundation, either version 3 of the # -# License, or (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU Affero General Public License for more details. # -# # -# You should have received a copy of the GNU Affero General Public License # -# along with this program. If not, see <http://www.gnu.org/licenses/>. # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL -from module.common.json_layer import json_loads - - -class MultiDebridCom(MultiHoster): - __name__ = "MultiDebridCom" - __version__ = "0.01" - __type__ = "hook" - - __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__ = """Multi-debrid.com hook plugin""" - __author_name__ = "stickell" - __author_mail__ = "l.stickell@yahoo.it" - - - def getHoster(self): - json_data = getURL('http://multi-debrid.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/module/plugins/hooks/MultiHome.py b/module/plugins/hooks/MultiHome.py index b9c663046..105a42abd 100644 --- a/module/plugins/hooks/MultiHome.py +++ b/module/plugins/hooks/MultiHome.py @@ -1,36 +1,25 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - from time import time from module.plugins.Hook import Hook class MultiHome(Hook): - __name__ = "MultiHome" - __version__ = "0.11" - __type__ = "hook" + __name__ = "MultiHome" + __type__ = "hook" + __version__ = "0.12" - __config__ = [("activated", "bool", "Activated", False), - ("interfaces", "str", "Interfaces", "None")] + __config__ = [("interfaces", "str", "Interfaces", "None")] __description__ = """Ip address changer""" - __author_name__ = "mkaay" - __author_mail__ = "mkaay@mkaay.de" + __license__ = "GPLv3" + __authors__ = [("mkaay", "mkaay@mkaay.de")] + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass def setup(self): @@ -41,29 +30,34 @@ class MultiHome(Hook): 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("Multihome: using address: " + 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: @@ -78,13 +72,16 @@ class Interface(object): 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/module/plugins/hooks/MultishareCz.py b/module/plugins/hooks/MultishareCz.py index 8baa4bb07..5ec5b63b6 100644 --- a/module/plugins/hooks/MultishareCz.py +++ b/module/plugins/hooks/MultishareCz.py @@ -3,21 +3,21 @@ import re from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class MultishareCz(MultiHoster): - __name__ = "MultishareCz" - __version__ = "0.04" - __type__ = "hook" +class MultishareCz(MultiHook): + __name__ = "MultishareCz" + __type__ = "hook" + __version__ = "0.05" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + HOSTER_PATTERN = r'<img class="logo-shareserveru"[^>]*?alt="([^"]+)"></td>\s*<td class="stav">[^>]*?alt="OK"' diff --git a/module/plugins/hooks/MyfastfileCom.py b/module/plugins/hooks/MyfastfileCom.py new file mode 100644 index 000000000..ec7c4e55b --- /dev/null +++ b/module/plugins/hooks/MyfastfileCom.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from module.common.json_layer import json_loads +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook + + +class MyfastfileCom(MultiHook): + __name__ = "MyfastfileCom" + __type__ = "hook" + __version__ = "0.03" + + __config__ = [("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""" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] + + + def getHoster(self): + json_data = getURL("http://myfastfile.com/api.php", get={'hosts': ""}, decode=True) + self.logDebug("JSON data", json_data) + json_data = json_loads(json_data) + + return json_data['hosts'] diff --git a/module/plugins/hooks/NoPremiumPl.py b/module/plugins/hooks/NoPremiumPl.py new file mode 100644 index 000000000..f60cb3dd6 --- /dev/null +++ b/module/plugins/hooks/NoPremiumPl.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.MultiHoster import MultiHoster +from module.network.RequestFactory import getURL +from module.common.json_layer import json_loads as loads + + +class NoPremiumPl(MultiHoster): + __name__ = "NoPremiumPl" + __version__ = "0.01" + __type__ = "hook" + + __config__ = [("activated", "bool", "Activated", "False"), + ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), + ("hosterList", "str", "Hoster list (comma separated)", ""), + ("unloadFailing", "bool", "Try standard download if download fails", "False"), + ("interval", "int", "Reload supported hosts interval in hours (0 to disable)", "24")] + + __description__ = "NoPremium.pl hook" + __license__ = "GPLv3" + __authors__ = [("goddie", "dev@nopremium.pl")] + + def getHoster(self): + hostings = loads(getURL("https://www.nopremium.pl/clipboard.php?json=3").strip()) + + return [domain for row in hostings for domain in row["domains"] if row["sdownload"] == "0"] + + def getHosterCached(self): + return self.getHoster() + + diff --git a/module/plugins/hooks/OverLoadMe.py b/module/plugins/hooks/OverLoadMe.py index dcc57aa75..378ce0a65 100644 --- a/module/plugins/hooks/OverLoadMe.py +++ b/module/plugins/hooks/OverLoadMe.py @@ -1,31 +1,29 @@ # -*- coding: utf-8 -*- from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class OverLoadMe(MultiHoster): - __name__ = "OverLoadMe" - __version__ = "0.01" - __type__ = "hook" +class OverLoadMe(MultiHook): + __name__ = "OverLoadMe" + __type__ = "hook" + __version__ = "0.02" - __config__ = [("activated", "bool", "Activated", False), - ("https", "bool", "Enable HTTPS", True), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("marley", "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: %s" % page) + 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/module/plugins/hooks/Premium4Me.py b/module/plugins/hooks/Premium4Me.py deleted file mode 100644 index 242e72f3e..000000000 --- a/module/plugins/hooks/Premium4Me.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster - - -class Premium4Me(MultiHoster): - __name__ = "Premium4Me" - __version__ = "0.03" - __type__ = "hook" - - __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?authcode=%s" % self.account.authcode) - return [x.strip() for x in page.replace("\"", "").split(";")] - - def coreReady(self): - self.account = self.core.accountManager.getAccountPlugin("Premium4Me") - - 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/module/plugins/hooks/PremiumTo.py b/module/plugins/hooks/PremiumTo.py new file mode 100644 index 000000000..3087db552 --- /dev/null +++ b/module/plugins/hooks/PremiumTo.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook + + +class PremiumTo(MultiHook): + __name__ = "PremiumTo" + __type__ = "hook" + __version__ = "0.05" + + __config__ = [("hosterListMode", "all;listed;unlisted", "Use for downloads from supported hosters:", "all"), + ("hosterList", "str", "Hoster list (comma separated)", "")] + + __description__ = """Premium.to hook plugin""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "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 MultiHook.coreReady(self) diff --git a/module/plugins/hooks/PremiumizeMe.py b/module/plugins/hooks/PremiumizeMe.py index 1a35460de..b37728e06 100644 --- a/module/plugins/hooks/PremiumizeMe.py +++ b/module/plugins/hooks/PremiumizeMe.py @@ -1,25 +1,23 @@ # -*- coding: utf-8 -*- -from module.plugins.internal.MultiHoster import MultiHoster - from module.common.json_layer import json_loads from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class PremiumizeMe(MultiHoster): - __name__ = "PremiumizeMe" - __version__ = "0.12" - __type__ = "hook" +class PremiumizeMe(MultiHook): + __name__ = "PremiumizeMe" + __type__ = "hook" + __version__ = "0.14" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Florian Franzen", "FlorianFranzen@gmail.com")] def getHoster(self): @@ -32,24 +30,25 @@ class PremiumizeMe(MultiHoster): # 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'])) + answer = getURL("https://api.premiumize.me/pm-api/v1.php", + get={'method': "hosterlist", 'params[login]': user, 'params[pass]': 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 + # 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.")) + 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) + # Run the overwriten core ready which actually enables the multihoster hook + return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/RPNetBiz.py b/module/plugins/hooks/RPNetBiz.py index 36cf69b4b..c54f7d445 100644 --- a/module/plugins/hooks/RPNetBiz.py +++ b/module/plugins/hooks/RPNetBiz.py @@ -1,24 +1,23 @@ # -*- coding: utf-8 -*- -from module.plugins.internal.MultiHoster import MultiHoster from module.common.json_layer import json_loads from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class RPNetBiz(MultiHoster): - __name__ = "RPNetBiz" - __version__ = "0.1" - __type__ = "hook" +class RPNetBiz(MultiHook): + __name__ = "RPNetBiz" + __type__ = "hook" + __version__ = "0.11" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Dman", "dmanugm@gmail.com")] def getHoster(self): @@ -29,17 +28,18 @@ class RPNetBiz(MultiHoster): # 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) + res = getURL("https://premium.rpnet.biz/client_api.php", + get={'username': user, 'password': data['password'], 'action': "showHosterList"}) + hoster_list = json_loads(res) # If account is not valid thera are no hosters available if 'error' in hoster_list: return [] - # Extract hosters from json file + # 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") @@ -48,5 +48,5 @@ class RPNetBiz(MultiHoster): 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) + # Run the overwriten core ready which actually enables the multihoster hook + return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/RapideoPl.py b/module/plugins/hooks/RapideoPl.py new file mode 100644 index 000000000..a5d7a34a5 --- /dev/null +++ b/module/plugins/hooks/RapideoPl.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +from module.plugins.internal.MultiHoster import MultiHoster +from module.network.RequestFactory import getURL +from module.common.json_layer import json_loads as loads + + +class RapideoPl(MultiHoster): + __name__ = "RapideoPl" + __version__ = "0.01" + __type__ = "hook" + + __config__ = [("activated", "bool", "Activated", "False"), + ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), + ("hosterList", "str", "Hoster list (comma separated)", ""), + ("unloadFailing", "bool", "Try standard download if download fails", "False"), + ("interval", "int", "Reload supported hosts interval in hours (0 to disable)", "24")] + + __description__ = "Rapideo.pl hook" + __license__ = "GPLv3" + __authors__ = [("goddie", "dev@rapideo.pl")] + + def getHoster(self): + hostings = loads(getURL("https://www.rapideo.pl/clipboard.php?json=3").strip()) + + return [domain for row in hostings for domain in row["domains"] if row["sdownload"] == "0"] + + def getHosterCached(self): + return self.getHoster() + + diff --git a/module/plugins/hooks/RealdebridCom.py b/module/plugins/hooks/RealdebridCom.py index 4477fba4a..066aa52c4 100644 --- a/module/plugins/hooks/RealdebridCom.py +++ b/module/plugins/hooks/RealdebridCom.py @@ -1,24 +1,23 @@ # -*- coding: utf-8 -*- from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class RealdebridCom(MultiHoster): - __name__ = "RealdebridCom" - __version__ = "0.43" - __type__ = "hook" +class RealdebridCom(MultiHook): + __name__ = "RealdebridCom" + __type__ = "hook" + __version__ = "0.44" - __config__ = [("activated", "bool", "Activated", False), - ("https", "bool", "Enable HTTPS", False), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Devirex Hazzard", "naibaf_11@yahoo.de")] def getHoster(self): diff --git a/module/plugins/hooks/RehostTo.py b/module/plugins/hooks/RehostTo.py index 96e06950e..48afa2342 100644 --- a/module/plugins/hooks/RehostTo.py +++ b/module/plugins/hooks/RehostTo.py @@ -1,40 +1,41 @@ # -*- coding: utf-8 -*- from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class RehostTo(MultiHoster): - __name__ = "RehostTo" - __version__ = "0.43" - __type__ = "hook" +class RehostTo(MultiHook): + __name__ = "RehostTo" + __type__ = "hook" + __version__ = "0.44" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] def getHoster(self): - page = getURL("http://rehost.to/api.php?cmd=get_supported_och_dl&long_ses=%s" % self.long_ses) + page = getURL("http://rehost.to/api.php", + get={'cmd': "get_supported_och_dl", 'long_ses': 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("Rehost.to: " + _("Please add your rehost.to account first and restart pyLoad")) + 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) + return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/RestartFailed.py b/module/plugins/hooks/RestartFailed.py index 2bd0e28db..07fb80967 100644 --- a/module/plugins/hooks/RestartFailed.py +++ b/module/plugins/hooks/RestartFailed.py @@ -1,37 +1,23 @@ # -*- coding: utf-8 -*- -############################################################################### -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -############################################################################### from module.plugins.Hook import Hook class RestartFailed(Hook): - __name__ = "RestartFailed" - __version__ = "1.55" - __type__ = "hook" + __name__ = "RestartFailed" + __type__ = "hook" + __version__ = "1.57" - __config__ = [("activated", "bool", "Activated", False), - ("interval", "int", "Check interval in minutes", 90)] + __config__ = [("interval", "int", "Check interval in minutes", 90)] __description__ = """Periodically restart all failed downloads in queue""" - __author_name__ = "Walter Purcaro" - __author_mail__ = "vuolter@gmail.com" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] - MIN_INTERVAL = 15 * 60 #: 15m minimum check interval (value is in seconds) - event_list = ["pluginConfigChanged"] + # event_list = ["pluginConfigChanged"] + + MIN_INTERVAL = 15 * 60 #: 15m minimum check interval (value is in seconds) def pluginConfigChanged(self, plugin, name, value): @@ -44,13 +30,15 @@ class RestartFailed(Hook): else: self.logDebug("Invalid interval value, kept current") + def periodical(self): - self.logInfo("Restart failed downloads") - self.api.restartFailed() + self.logDebug(_("Restart failed downloads")) + self.core.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/module/plugins/hooks/RestartSlow.py b/module/plugins/hooks/RestartSlow.py new file mode 100644 index 000000000..c2fdf6f95 --- /dev/null +++ b/module/plugins/hooks/RestartSlow.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- + +import pycurl + +from module.plugins.Hook import Hook + + +class RestartSlow(Hook): + __name__ = "RestartSlow" + __type__ = "hook" + __version__ = "0.03" + + __config__ = [("free_limit" , "int" , "Transfer speed threshold in kilobytes" , 100 ), + ("free_time" , "int" , "Sample interval in minutes" , 5 ), + ("premium_limit", "int" , "Transfer speed threshold for premium download in kilobytes", 300 ), + ("premium_time" , "int" , "Sample interval for premium download in minutes" , 2 ), + ("safe_mode" , "bool", "Don't restart if download is not resumable" , True)] + + __description__ = """Restart slow downloads""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + event_list = ["downloadStarts"] + + + def setup(self): + self.info = {'chunk': {}} + + + def initPeriodical(self): + pass + + + def periodical(self): + if not self.pyfile.req.dl: + return + + if self.getConfig("safe_mode") and not self.pyfile.plugin.resumeDownload: + time = 30 + limit = 5 + else: + type = "premium" if self.pyfile.plugin.premium else "free" + time = max(30, self.getConfig("%s_time" % type) * 60) + limit = max(5, self.getConfig("%s_limit" % type) * 1024) + + chunks = [chunk for chunk in self.pyfile.req.dl.chunks \ + if chunk.id not in self.info['chunk'] or self.info['chunk'][chunk.id] is not (time, limit)] + + for chunk in chunks: + chunk.c.setopt(pycurl.LOW_SPEED_TIME , time) + chunk.c.setopt(pycurl.LOW_SPEED_LIMIT, limit) + + self.info['chunk'][chunk.id] = (time, limit) + + + def downloadStarts(self, pyfile, url, filename): + if self.cb or (self.getConfig("safe_mode") and not pyfile.plugin.resumeDownload): + return + + super(RestartSlow, self).initPeriodical() diff --git a/module/plugins/hooks/SimplyPremiumCom.py b/module/plugins/hooks/SimplyPremiumCom.py index 41268e231..10a1655c2 100644 --- a/module/plugins/hooks/SimplyPremiumCom.py +++ b/module/plugins/hooks/SimplyPremiumCom.py @@ -1,28 +1,14 @@ # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU Affero General Public License as # -# published by the Free Software Foundation, either version 3 of the # -# License, or (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU Affero General Public License for more details. # -# # -# You should have received a copy of the GNU Affero General Public License # -# along with this program. If not, see <http://www.gnu.org/licenses/>. # -############################################################################ from module.common.json_layer import json_loads from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class SimplyPremiumCom(MultiHoster): - __name__ = "SimplyPremiumCom" - __version__ = "0.02" - __type__ = "hook" +class SimplyPremiumCom(MultiHook): + __name__ = "SimplyPremiumCom" + __type__ = "hook" + __version__ = "0.03" __config__ = [("activated", "bool", "Activated", "False"), ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), @@ -31,12 +17,12 @@ class SimplyPremiumCom(MultiHoster): ("interval", "int", "Reload interval in hours (0 to disable)", "24")] __description__ = """Simply-Premium.com hook plugin""" - __author_name__ = "EvolutionClip" - __author_mail__ = "evolutionclip@live.de" + __license__ = "GPLv3" + __authors__ = [("EvolutionClip", "evolutionclip@live.de")] def getHoster(self): - json_data = getURL('http://www.simply-premium.com/api/hosts.php?format=json&online=1') + json_data = getURL("http://www.simply-premium.com/api/hosts.php", get={'format': "json", 'online': 1}) json_data = json_loads(json_data) host_list = [element['regex'] for element in json_data['result']] diff --git a/module/plugins/hooks/SimplydebridCom.py b/module/plugins/hooks/SimplydebridCom.py index 345d37e4a..48568f870 100644 --- a/module/plugins/hooks/SimplydebridCom.py +++ b/module/plugins/hooks/SimplydebridCom.py @@ -1,23 +1,22 @@ # -*- coding: utf-8 -*- from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class SimplydebridCom(MultiHoster): - __name__ = "SimplydebridCom" - __version__ = "0.01" - __type__ = "hook" +class SimplydebridCom(MultiHook): + __name__ = "SimplydebridCom" + __type__ = "hook" + __version__ = "0.02" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")] def getHoster(self): - page = getURL("http://simply-debrid.com/api.php?list=1") + page = getURL("http://simply-debrid.com/api.php", get={'list': 1}) return [x.strip() for x in page.rstrip(';').replace("\"", "").split(";")] diff --git a/module/plugins/hooks/SkipRev.py b/module/plugins/hooks/SkipRev.py new file mode 100644 index 000000000..cc32c365e --- /dev/null +++ b/module/plugins/hooks/SkipRev.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- + +from types import MethodType +from urllib import unquote +from urlparse import urlparse + +from module.PyFile import PyFile +from module.plugins.Hook import Hook +from module.plugins.Plugin import SkipDownload + + +def _setup(self): + self.pyfile.plugin._setup() + if self.pyfile.hasStatus("skipped"): + raise SkipDownload(self.pyfile.statusname or self.pyfile.pluginname) + + +class SkipRev(Hook): + __name__ = "SkipRev" + __type__ = "hook" + __version__ = "0.21" + + __config__ = [("tokeep", "int", "Number of rev files to keep for package (-1 to auto)", -1)] + + __description__ = """Skip files ending with extension rev""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass + + + def _pyname(self, pyfile): + url = pyfile.url + plugin = pyfile.plugin + + if hasattr(plugin, "info") and 'name' in plugin.info and plugin.info['name']: + name = plugin.info['name'] + + elif hasattr(plugin, "parseInfos"): + name = next(plugin.parseInfos([url]))['name'] + + elif hasattr(plugin, "getInfo"): #@NOTE: if parseInfos was not found, getInfo should be missing too + name = plugin.getInfo(url)['name'] + + else: + self.logWarning("Unable to grab file name") + name = urlparse(unquote(url)).path.split('/')[-1] + + return name + + + def _pyfile(self, link): + return PyFile(self.core.files, + link.fid, + link.url, + link.name, + link.size, + link.status, + link.error, + link.plugin, + link.packageID, + link.order) + + + def downloadPreparing(self, pyfile): + if pyfile.statusname is "unskipped" or not self._pyname(pyfile).endswith(".rev"): + return + + tokeep = self.getConfig("tokeep") + + if tokeep: + saved = [True for link in self.core.api.getPackageData(pyfile.package().id).links \ + if link.name.endswith(".rev") and link.status in (0, 12)].count(True) + + if not saved or saved < tokeep: #: keep one rev at least in auto mode + return + + pyfile.setCustomStatus("SkipRev", "skipped") + pyfile.plugin._setup = pyfile.plugin.setup + pyfile.plugin.setup = MethodType(_setup, pyfile.plugin) #: work-around: inject status checker inside the preprocessing routine of the plugin + + + def downloadFailed(self, pyfile): + #: Check if pyfile is still "failed", + # maybe might has been restarted in meantime + if pyfile.status != 8: + return + + tokeep = self.getConfig("tokeep") + + if not tokeep: + return + + for link in self.core.api.getPackageData(pyfile.package().id).links: + if link.status is 4 and link.name.endswith(".rev"): + pylink = self._pyfile(link) + + if tokeep > -1 or pyfile.name.endswith(".rev"): + pylink.setStatus("queued") + else: + pylink.setCustomStatus("unskipped", "queued") + + self.core.files.save() + pylink.release() + return diff --git a/module/plugins/hooks/UnSkipOnFail.py b/module/plugins/hooks/UnSkipOnFail.py index d40854e99..1becb937a 100644 --- a/module/plugins/hooks/UnSkipOnFail.py +++ b/module/plugins/hooks/UnSkipOnFail.py @@ -1,99 +1,95 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" -from os.path import basename - -from module.utils import fs_encode -from module.plugins.Hook import Hook from module.PyFile import PyFile +from module.plugins.Hook import Hook class UnSkipOnFail(Hook): - __name__ = "UnSkipOnFail" - __version__ = "0.01" - __type__ = "hook" + __name__ = "UnSkipOnFail" + __type__ = "hook" + __version__ = "0.05" __config__ = [("activated", "bool", "Activated", True)] - __description__ = """When a download fails, restart skipped duplicates""" - __author_name__ = "hagg" - __author_mail__ = None + __description__ = """Queue skipped duplicates when download fails""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass 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): + #: Check if pyfile is still "failed", + # maybe might has been restarted in meantime + if pyfile.status != 8: + return + + msg = _("Looking for skipped duplicates of: %s (pid:%s)") + self.logInfo(msg % (pyfile.name, pyfile.package().id)) + + dup = self.findDuplicate(pyfile) + if dup: + self.logInfo(_("Queue found duplicate: %s (pid:%s)") % (dup.name, dup.packageID)) + + #: 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. + pylink = _pyfile(link) + + pylink.setCustomStatus("UnSkipOnFail", "queued") + + self.core.files.save() + pylink.release() + + else: + self.logInfo(_("No duplicates found")) + + + def findDuplicate(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)). + of twolinks are compared (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() + queue = self.core.api.getQueue() #: get packages (w/o files, as most file data is useless here) + 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() + #: check if package-folder equals pyfile's package folder + if package.folder != pyfile.package().folder: + continue + + #: now get packaged data w/ files/links + pdata = self.core.api.getPackageData(package.pid) + for link in pdata.links: + #: check if link is "skipped" + if link.status != 4: + continue + + #: check if link name collides with pdata's name + #: AND at last check if it is not pyfile itself + if link.name == pyfile.name and link.fid != pyfile.id: + return link + + + def _pyfile(self, link): + return PyFile(self.core.files, + link.fid, + link.url, + link.name, + link.size, + link.status, + link.error, + link.plugin, + link.packageID, + link.order) diff --git a/module/plugins/hooks/UnrestrictLi.py b/module/plugins/hooks/UnrestrictLi.py index 3303c7355..245264d44 100644 --- a/module/plugins/hooks/UnrestrictLi.py +++ b/module/plugins/hooks/UnrestrictLi.py @@ -1,43 +1,28 @@ # -*- coding: utf-8 -*- -############################################################################ -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU Affero General Public License as # -# published by the Free Software Foundation, either version 3 of the # -# License, or (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU Affero General Public License for more details. # -# # -# You should have received a copy of the GNU Affero General Public License # -# along with this program. If not, see <http://www.gnu.org/licenses/>. # -############################################################################ - -from module.plugins.internal.MultiHoster import MultiHoster -from module.network.RequestFactory import getURL + from module.common.json_layer import json_loads +from module.network.RequestFactory import getURL +from module.plugins.internal.MultiHook import MultiHook -class UnrestrictLi(MultiHoster): - __name__ = "UnrestrictLi" - __version__ = "0.02" - __type__ = "hook" +class UnrestrictLi(MultiHook): + __name__ = "UnrestrictLi" + __type__ = "hook" + __version__ = "0.03" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] def getHoster(self): - json_data = getURL('http://unrestrict.li/api/jdownloader/hosts.php?format=json') + json_data = getURL("http://unrestrict.li/api/jdownloader/hosts.php", get={'format': "json"}) json_data = json_loads(json_data) host_list = [element['host'] for element in json_data['result']] diff --git a/module/plugins/hooks/UpdateManager.py b/module/plugins/hooks/UpdateManager.py index 6ed756d13..c72699228 100644 --- a/module/plugins/hooks/UpdateManager.py +++ b/module/plugins/hooks/UpdateManager.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- +from __future__ import with_statement + import re import sys from operator import itemgetter -from os import remove, stat -from os.path import isfile -from time import time +from os import path, remove, stat from module.network.RequestFactory import getURL from module.plugins.Hook import Expose, Hook, threaded @@ -14,23 +14,26 @@ from module.utils import save_join class UpdateManager(Hook): - __name__ = "UpdateManager" - __version__ = "0.33" - __type__ = "hook" + __name__ = "UpdateManager" + __type__ = "hook" + __version__ = "0.42" + + __config__ = [("activated" , "bool" , "Activated" , True ), + ("mode" , "pyLoad + plugins;plugins only", "Check updates for" , "pyLoad + plugins"), + ("interval" , "int" , "Check interval in hours" , 8 ), + ("autorestart" , "bool" , "Automatically restart pyLoad when required" , True ), + ("reloadplugins", "bool" , "Monitor plugins for code changes in debug mode", True ), + ("nodebugupdate", "bool" , "Don't check for updates in debug mode" , True )] - __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 """ + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] - __description__ = """Check for updates""" - __author_name__ = "Walter Purcaro" - __author_mail__ = "vuolter@gmail.com" - event_list = ["pluginConfigChanged"] + # event_list = ["pluginConfigChanged"] - SERVER_URL = "http://updatemanager.pyload.org" + SERVER_URL = "http://updatemanager.pyload.org" + VERSION = re.compile(r'__version__.*=.*("|\')([\d.]+)') MIN_INTERVAL = 3 * 60 * 60 #: 3h minimum check interval (value is in seconds) @@ -38,33 +41,43 @@ class UpdateManager(Hook): if name == "interval": interval = value * 60 * 60 if self.MIN_INTERVAL <= interval != self.interval: - self.scheduler.removeJob(self.cb) + 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.scheduler.removeJob(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")) - self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins")) + 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.scheduler = self.core.scheduler - self.cb2 = None + 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 + 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.scheduler.addJob(10, self.periodical2, threaded=False) + + self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False) + @Expose def autoreloadPlugins(self): @@ -82,7 +95,7 @@ class UpdateManager(Hook): id = (type, name) if type in self.core.pluginManager.plugins: f = m.__file__.replace(".pyc", ".py") - if not isfile(f): + if not path.isfile(f): continue mtime = stat(f).st_mtime @@ -95,42 +108,53 @@ class UpdateManager(Hook): 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=True if self.getConfig("mode") == "plugins only" else False) - if status == 2: + + status = self.update(onlyplugin=self.getConfig("mode") == "plugins only") + + if status is 2 and self.getConfig("autorestart"): 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) @@ -138,38 +162,62 @@ class UpdateManager(Hook): 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 = [] + exitcode = 0 + updated = [] - vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)') - url = updates[0] + url = updates[0] schema = updates[1].split('|') + if "BLACKLIST" in updates: blacklist = updates[updates.index('BLACKLIST') + 1:] - updates = updates[2:updates.index('BLACKLIST')] + updates = updates[2:updates.index('BLACKLIST')] else: blacklist = None - updates = updates[2:] + updates = updates[2:] + + upgradable = [dict(zip(schema, x.split('|'))) for x in updates] + blacklisted = [(x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]) for x in blacklist] if blacklist else [] + + if blacklist: + # Protect UpdateManager from self-removing + try: + blacklisted.remove(("hook", "UpdateManager")) + except: + pass + + for t, n in blacklisted: + for idx, plugin in enumerate(upgradable): + if n == plugin['name'] and t == plugin['type']: + upgradable.pop(idx) + break + + for t, n in self.removePlugins(sorted(blacklisted)): + self.logInfo(_("Removed blacklisted plugin [%(type)s] %(name)s") % { + 'type': t, + 'name': n, + }) - upgradable = sorted(map(lambda x: dict(zip(schema, x.split('|'))), updates), key=itemgetter("type", "name")) - for plugin in upgradable: + for plugin in sorted(upgradable, key=itemgetter("type", "name")): filename = plugin['name'] - prefix = plugin['type'] - version = plugin['version'] + prefix = plugin['type'] + version = plugin['version'] if filename.endswith(".pyc"): name = filename[:filename.find("_")] else: name = filename.replace(".py", "") - #TODO: obsolete in 0.5.0 + #@TODO: obsolete after 0.4.10 if prefix.endswith("s"): type = prefix[:-1] else: @@ -181,47 +229,30 @@ class UpdateManager(Hook): newver = float(version) if not oldver: - msg = "New version of [%(type)s] %(name)s (v%(newver)s)" + msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)" elif newver > oldver: - msg = "New version of [%(type)s] %(name)s (v%(oldver)s -> v%(newver)s)" + msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)" else: continue - self.logInfo(_(msg) % { - "type": type, - "name": name, - "oldver": oldver, - "newver": newver - }) - + self.logInfo(_(msg) % {'type' : type, + 'name' : name, + 'oldver': oldver, + 'newver': newver}) try: content = getURL(url % plugin) - m = vre.search(content) + m = self.VERSION.search(content) + if m and m.group(2) == version: - f = open(save_join("userplugins", prefix, filename), "wb") - f.write(content) - f.close() + with open(save_join("userplugins", prefix, filename), "wb") as f: + f.write(content) + updated.append((prefix, name)) else: - raise Exception(_("Version mismatch")) - except Exception, e: - self.logError(_("Error updating plugin %s") % filename, str(e)) - - if blacklist: - blacklisted = sorted(map(lambda x: (x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]), blacklist)) + raise Exception, _("Version mismatch") - # 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 - }) + except Exception, e: + self.logError(_("Error updating plugin: %s") % filename, str(e)) if updated: reloaded = self.core.pluginManager.reloadPlugins(updated) @@ -234,47 +265,45 @@ class UpdateManager(Hook): 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""" + """ delete plugins from disk """ if not type_plugins: - return None + return - self.logDebug("Request deletion of plugins: %s" % type_plugins) + self.logDebug("Requested deletion of plugins: %s" % type_plugins) removed = [] for type, name in type_plugins: - rflag = False - py_file = name + ".py" - pyc_file = name + ".pyc" + err = False + file = name + ".py" - for root in ("userplugins", save_join(pypath, "module", "plugins")): - py_filename = save_join(root, type, py_file) - pyc_filename = save_join(root, type, pyc_file) + for root in ("userplugins", path.join(pypath, "module", "plugins")): - if isfile(py_filename): - try: - remove(py_filename) - except Exception, e: - self.logError("Error deleting file %s" % py_filename, str(e)) - rflag = False - else: - rflag = True + filename = save_join(root, type, file) + try: + remove(filename) + except Exception, e: + self.logDebug("Error removing: %s" % path.basename(filename), str(e)) + err = True - if isfile(pyc_filename): + filename += "c" + if path.isfile(filename): try: if type == "hook": self.manager.deactivateHook(name) - remove(pyc_filename) + remove(filename) except Exception, e: - self.logError("Error deleting file %s" % pyc_filename, str(e)) - if rflag: + self.logDebug("Error removing: %s" % path.basename(filename), str(e)) + err = True + + if not err: id = (type, name) removed.append(id) diff --git a/module/plugins/hooks/WindowsPhoneToastNotify.py b/module/plugins/hooks/WindowsPhoneToastNotify.py index 25fa3abe5..ed305778c 100644 --- a/module/plugins/hooks/WindowsPhoneToastNotify.py +++ b/module/plugins/hooks/WindowsPhoneToastNotify.py @@ -1,42 +1,34 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" -import time import httplib +import time + from module.plugins.Hook import Hook class WindowsPhoneToastNotify(Hook): - __name__ = "WindowsPhoneToastNotify" - __version__ = "0.02" - __type__ = "hook" + __name__ = "WindowsPhoneToastNotify" + __type__ = "hook" + __version__ = "0.03" - __config__ = [("activated", "bool", "Activated", False), - ("force", "bool", "Force even if client is connected", False), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("Andy Voigt", "phone-support@hotmail.de")] + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass def setup(self): - self.info = {} + self.info = {} #@TODO: Remove in 0.4.10 + def getXmlData(self): myxml = ("<?xml version='1.0' encoding='utf-8'?> <wp:Notification xmlns:wp='WPNotification'> " @@ -44,6 +36,7 @@ class WindowsPhoneToastNotify(Hook): "</wp:Toast> </wp:Notification>") return myxml + def doRequest(self): URL = self.getConfig("pushUrl") request = self.getXmlData() @@ -59,6 +52,7 @@ class WindowsPhoneToastNotify(Hook): webservice.close() self.setStorage("LAST_NOTIFY", time.time()) + def newCaptchaTask(self, task): if not self.getConfig("pushId") or not self.getConfig("pushUrl"): return False diff --git a/module/plugins/hooks/XFileSharingPro.py b/module/plugins/hooks/XFileSharingPro.py index 37a464b33..589143547 100644 --- a/module/plugins/hooks/XFileSharingPro.py +++ b/module/plugins/hooks/XFileSharingPro.py @@ -6,73 +6,126 @@ from module.plugins.Hook import Hook class XFileSharingPro(Hook): - __name__ = "XFileSharingPro" - __version__ = "0.11" - __type__ = "hook" + __name__ = "XFileSharingPro" + __type__ = "hook" + __version__ = "0.28" - __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)", "")] + __config__ = [("activated" , "bool", "Activated" , True ), + ("use_hoster_list" , "bool", "Load listed hosters only" , False), + ("use_crypter_list", "bool", "Load listed crypters only" , False), + ("use_builtin_list", "bool", "Load built-in plugin list" , True ), + ("hoster_list" , "str" , "Hoster list (comma separated)" , "" ), + ("crypter_list" , "str" , "Crypter list (comma separated)", "" )] - __description__ = """XFileSharingPro hook plugin""" - __author_name__ = "zoidberg" - __author_mail__ = "zoidberg@mujmail.cz" + __description__ = """Load XFileSharingPro based hosters and crypter which don't need a own plugin to run""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + # event_list = ["pluginConfigChanged"] + regexp = {'hoster' : (r'https?://(?:www\.)?([\w.^_]+(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:embed-)?\w{12}(?:\W|$)', + r'https?://(?:[^/]+\.)?(%s)/(?:embed-)?\w+'), + 'crypter': (r'https?://(?:www\.)?([\w.^_]+(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:user|folder)s?/\w+', + r'https?://(?:[^/]+\.)?(%s)/(?:user|folder)s?/\w+')} + + HOSTER_LIST = [#WORKING HOSTERS: + "eyesfile.ca", "file4safe.com", "fileband.com", "filedwon.com", "filevice.com", "hostingbulk.com", + "ravishare.com", "salefiles.com", "sharesix.com", "thefile.me", "verzend.be", "xvidstage.com", + #NOT TESTED: + "101shared.com", "4upfiles.com", "filemaze.ws", "filenuke.com", "linkzhost.com", "mightyupload.com", + "rockdizfile.com", "sharebeast.com", "sharerepo.com", "shareswift.com", "uploadbaz.com", "uploadc.com", + "vidbull.com", "zalaa.com", "zomgupload.com", + #NOT WORKING: + "amonshare.com", "banicrazy.info", "boosterking.com", "host4desi.com", "laoupload.com", "rd-fs.com"] + CRYPTER_LIST = [] + + + # def pluginConfigChanged(self.__name__, plugin, name, value): + # self.loadPattern() + + + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass 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)/\w{12}" % ("|".join(sorted(hosterList)).replace('.', '\.')) - #self.logDebug(regexp) - - 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(',')]) + use_builtin_list = self.getConfig('use_builtin_list') - def unload(self): - dict = self.core.pluginManager.hosterPlugins['XFileSharingPro'] + for type, plugin in (("hoster", "XFileSharingPro"), + ("crypter", "XFileSharingProFolder")): + every_plugin = not self.getConfig("use_%s_list" % type) + + if every_plugin: + self.logInfo(_("Handling any %s I can!") % type) + pattern = self.regexp[type][0] + else: + s = self.getConfig('%s_list' % type).replace('\\', '').replace('|', ',').replace(';', ',').lower() + plugin_list = set([x.strip() for x in s.split(',')]) + + if use_builtin_list: + plugin_list |= set([x.lower() for x in getattr(self, "%s_LIST" % type.upper())]) + + plugin_list -= set(('', u'')) + + if not plugin_list: + self.logInfo(_("No %s to handle") % type) + self._unload(type, plugin) + return + + match_list = '|'.join(sorted(plugin_list)) + + len_match_list = len(plugin_list) + self.logInfo(_("Handling %d %s%s: %s") % (len_match_list, + type, + "" if len_match_list is 1 else "s", + match_list.replace('|', ', '))) + + pattern = self.regexp[type][1] % match_list.replace('.', '\.') + + dict = self.core.pluginManager.plugins[type][plugin] + dict['pattern'] = pattern + dict['re'] = re.compile(pattern) + + self.logDebug("Loaded %s pattern: %s" % (type, pattern)) + + + def _unload(self, type, plugin): + dict = self.core.pluginManager.plugins[type][plugin] dict['pattern'] = r'^unmatchable$' - dict['re'] = re.compile(r'^unmatchable$') + dict['re'] = re.compile(dict['pattern']) + + + def unload(self): + # self.unloadHoster("BasePlugin") + for type, plugin in (("hoster", "XFileSharingPro"), + ("crypter", "XFileSharingProFolder")): + self._unload(type, plugin) + + + def unloadHoster(self, hoster): + hdict = self.core.pluginManager.hosterPlugins[hoster] + if "new_name" in hdict and hdict['new_name'] is "XFileSharingPro": + if "module" in hdict: + del hdict['module'] + + if "new_module" in hdict: + del hdict['new_module'] + del hdict['new_name'] + + return True + else: + return False + + + # def downloadFailed(self, pyfile): + # if pyfile.pluginname is "BasePlugin" \ + # and pyfile.hasStatus("failed") \ + # and not self.getConfig("use_hoster_list") \ + # and self.unloadHoster("BasePlugin"): + # self.logDebug("Unloaded XFileSharingPro from BasePlugin") + # pyfile.setStatus("queued") diff --git a/module/plugins/hooks/XMPPInterface.py b/module/plugins/hooks/XMPPInterface.py index a60d31b89..bbeab4341 100644 --- a/module/plugins/hooks/XMPPInterface.py +++ b/module/plugins/hooks/XMPPInterface.py @@ -1,36 +1,20 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. -""" - from pyxmpp import streamtls from pyxmpp.all import JID, Message -from pyxmpp.jabber.client import JabberClient from pyxmpp.interface import implements from pyxmpp.interfaces import * +from pyxmpp.jabber.client import JabberClient from module.plugins.hooks.IRCInterface import IRCInterface class XMPPInterface(IRCInterface, JabberClient): - __name__ = "XMPPInterface" + __name__ = "XMPPInterface" + __type__ = "hook" __version__ = "0.11" - __type__ = "hook" - __config__ = [("activated", "bool", "Activated", False), - ("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"), + __config__ = [("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"), @@ -39,12 +23,13 @@ class XMPPInterface(IRCInterface, JabberClient): ("captcha", "bool", "Send captcha requests", True)] __description__ = """Connect to jabber and let owner perform different tasks""" - __author_name__ = "RaNaN" - __author_mail__ = "RaNaN@pyload.org" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] implements(IMessageHandlersProvider) + def __init__(self, core, manager): IRCInterface.__init__(self, core, manager) @@ -73,11 +58,13 @@ class XMPPInterface(IRCInterface, JabberClient): self, ] + def coreReady(self): self.new_package = {} self.start() + def packageFinished(self, pypack): try: if self.getConfig("info_pack"): @@ -85,6 +72,7 @@ class XMPPInterface(IRCInterface, JabberClient): except: pass + def downloadFinished(self, pyfile): try: if self.getConfig("info_file"): @@ -93,28 +81,34 @@ class XMPPInterface(IRCInterface, JabberClient): except: pass + def run(self): # connect to IRC etc. self.connect() try: self.loop() except Exception, ex: - self.logError("pyLoad XMPP: %s" % str(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("pyLoad XMPP: *** State changed: %s %r ***" % (state, arg)) + self.logDebug("*** State changed: %s %r ***" % (state, arg)) + def disconnected(self): - self.logDebug("pyLoad XMPP: Client was disconnected") + self.logDebug("Client was disconnected") + def stream_closed(self, stream): - self.logDebug("pyLoad XMPP: Stream was closed | %s" % stream) + self.logDebug("Stream was closed", stream) + def stream_error(self, err): - self.logDebug("pyLoad XMPP: Stream Error: %s" % err) + self.logDebug("Stream Error", err) + def get_message_handlers(self): """Return list of (message_type, message_handler) tuples. @@ -123,13 +117,14 @@ class XMPPInterface(IRCInterface, JabberClient): 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(u'pyLoad XMPP: Message from %s received.' % (unicode(stanza.get_from(),))) - self.logDebug(u'pyLoad XMPP: Body: %s Subject: %s Type: %s' % (body, subject, t)) + 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 @@ -173,20 +168,22 @@ class XMPPInterface(IRCInterface, JabberClient): messages.append(m) except Exception, e: - self.logError("pyLoad XMPP: " + repr(e)) + self.logError(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("pyLoad XMPP: Send message to %s" % user) + self.logDebug("Send message to", user) to_jid = JID(user) @@ -202,9 +199,11 @@ class XMPPInterface(IRCInterface, JabberClient): stream.send(m) + def beforeReconnecting(self, ip): self.disconnect() + def afterReconnecting(self, ip): self.connect() @@ -217,24 +216,29 @@ class VersionHandler(object): 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. diff --git a/module/plugins/hooks/ZeveraCom.py b/module/plugins/hooks/ZeveraCom.py index 49fc68b30..6ca696f38 100644 --- a/module/plugins/hooks/ZeveraCom.py +++ b/module/plugins/hooks/ZeveraCom.py @@ -1,23 +1,22 @@ # -*- coding: utf-8 -*- from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster +from module.plugins.internal.MultiHook import MultiHook -class ZeveraCom(MultiHoster): - __name__ = "ZeveraCom" - __version__ = "0.02" - __type__ = "hook" +class ZeveraCom(MultiHook): + __name__ = "ZeveraCom" + __type__ = "hook" + __version__ = "0.03" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), + __config__ = [("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" + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] def getHoster(self): - page = getURL("http://www.zevera.com/jDownloader.ashx?cmd=gethosters") + page = getURL("http://www.zevera.com/jDownloader.ashx", get={'cmd': "gethosters"}) return [x.strip() for x in page.replace("\"", "").split(",")] |