diff options
Diffstat (limited to 'module/plugins/hooks')
51 files changed, 2222 insertions, 2074 deletions
diff --git a/module/plugins/hooks/AlldebridCom.py b/module/plugins/hooks/AlldebridCom.py index 0f8d3bfbb..d5986053f 100644 --- a/module/plugins/hooks/AlldebridCom.py +++ b/module/plugins/hooks/AlldebridCom.py @@ -1,29 +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 70e60f56c..a32de7f42 100644 --- a/module/plugins/hooks/BypassCaptcha.py +++ b/module/plugins/hooks/BypassCaptcha.py @@ -1,70 +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/>. - - @author: RaNaN, Godofdream, zoidberg -""" - -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" - __description__ = """Send captchas to BypassCaptcha.com""" - __config__ = [("activated", "bool", "Activated", False), - ("force", "bool", "Force BC even if client is connected", False), + __name__ = "BypassCaptcha" + __type__ = "hook" + __version__ = "0.05" + + __config__ = [("force", "bool", "Force BC even if client is connected", False), ("passkey", "password", "Passkey", "")] - __author_name__ = ("RaNaN", "Godofdream", "zoidberg") - __author_mail__ = ("RaNaN@pyload.org", "soilfcition@gmail.com", "zoidberg@mujmail.cz") + + __description__ = """Send captchas to BypassCaptcha.com""" + __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() @@ -72,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: @@ -118,15 +119,18 @@ 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) + 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) + self.respond(task.data['ticket'], False) + def processCaptcha(self, task): c = task.captchaFile @@ -136,5 +140,5 @@ class BypassCaptcha(Hook): task.error = e.getCode() return - task.data["ticket"] = ticket + task.data['ticket'] = ticket task.setResult(result) diff --git a/module/plugins/hooks/Captcha9kw.py b/module/plugins/hooks/Captcha9kw.py index c86f92972..33ad00c49 100755 --- a/module/plugins/hooks/Captcha9kw.py +++ b/module/plugins/hooks/Captcha9kw.py @@ -1,168 +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/>. - - @author: mkaay, RaNaN, zoidberg -""" 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" + __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""" - __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", "")] - __author_name__ = "RaNaN" - __author_mail__ = "RaNaN@pyload.org" - - API_URL = "://www.9kw.eu/index.cgi" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org"), + ("Walter Purcaro", "vuolter@gmail.com")] + + + 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 23d71ff5f..b6e38d8bb 100644 --- a/module/plugins/hooks/CaptchaBrotherhood.py +++ b/module/plugins/hooks/CaptchaBrotherhood.py @@ -1,76 +1,80 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: mkaay, RaNaN, zoidberg -""" 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 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" - __description__ = """Send captchas to CaptchaBrotherhood.com""" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), + __name__ = "CaptchaBrotherhood" + __type__ = "hook" + __version__ = "0.06" + + __config__ = [("username", "str", "Username", ""), ("force", "bool", "Force CT even if client is connected", False), ("passkey", "password", "Password", "")] - __author_name__ = ("RaNaN", "zoidberg") - __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + + __description__ = """Send captchas to CaptchaBrotherhood.com""" + __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 + self.info['credits'] = credits return credits + def submit(self, captcha, captchaType="file", match=None): try: img = Image.open(captcha) @@ -102,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: @@ -150,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 @@ -164,5 +172,5 @@ class CaptchaBrotherhood(Hook): task.error = e.getCode() return - task.data["ticket"] = ticket + task.data['ticket'] = ticket task.setResult(result) diff --git a/module/plugins/hooks/CaptchaTrader.py b/module/plugins/hooks/CaptchaTrader.py deleted file mode 100644 index 051dc6c2b..000000000 --- a/module/plugins/hooks/CaptchaTrader.py +++ /dev/null @@ -1,157 +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/>. - - @author: mkaay, RaNaN -""" - -from thread import start_new_thread -from pycurl import FORM_FILE, LOW_SPEED_TIME - -from module.common.json_layer import json_loads -from module.network.RequestFactory import getURL, getRequest -from module.network.HTTPRequest import BadHeader -from module.plugins.Hook import Hook - -PYLOAD_KEY = "9f65e7f381c3af2b076ea680ae96b0b7" - - -class CaptchaTraderException(Exception): - def __init__(self, err): - self.err = err - - def getCode(self): - return self.err - - def __str__(self): - return "<CaptchaTraderException %s>" % self.err - - def __repr__(self): - return "<CaptchaTraderException %s>" % self.err - - -class CaptchaTrader(Hook): - __name__ = "CaptchaTrader" - __version__ = "0.16" - __description__ = """Send captchas to captchatrader.com""" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), - ("force", "bool", "Force CT even if client is connected", False), - ("passkey", "password", "Password", "")] - __author_name__ = "RaNaN" - __author_mail__ = "RaNaN@pyload.org" - - SUBMIT_URL = "http://api.captchatrader.com/submit" - RESPOND_URL = "http://api.captchatrader.com/respond" - GETCREDITS_URL = "http://api.captchatrader.com/get_credits/username:%(user)s/password:%(password)s/" - - def setup(self): - self.info = {} - - def getCredits(self): - json = getURL(CaptchaTrader.GETCREDITS_URL % {"user": self.getConfig("username"), - "password": self.getConfig("passkey")}) - response = json_loads(json) - if response[0] < 0: - raise CaptchaTraderException(response[1]) - else: - self.logInfo(_("%s credits left") % response[1]) - self.info["credits"] = response[1] - return response[1] - - def submit(self, captcha, captchaType="file", match=None): - if not PYLOAD_KEY: - raise CaptchaTraderException("No API Key Specified!") - - #if type(captcha) == str and captchaType == "file": - # raise CaptchaTraderException("Invalid Type") - assert captchaType in ("file", "url-jpg", "url-jpeg", "url-png", "url-bmp") - - req = getRequest() - - #raise timeout threshold - req.c.setopt(LOW_SPEED_TIME, 80) - - try: - json = req.load(CaptchaTrader.SUBMIT_URL, post={"api_key": PYLOAD_KEY, - "username": self.getConfig("username"), - "password": self.getConfig("passkey"), - "value": (FORM_FILE, captcha), - "type": captchaType}, multipart=True) - finally: - req.close() - - response = json_loads(json) - if response[0] < 0: - raise CaptchaTraderException(response[1]) - - ticket = response[0] - result = response[1] - self.logDebug("result %s : %s" % (ticket, result)) - - return ticket, result - - def respond(self, ticket, success): - try: - json = getURL(CaptchaTrader.RESPOND_URL, post={"is_correct": 1 if success else 0, - "username": self.getConfig("username"), - "password": self.getConfig("passkey"), - "ticket": ticket}) - - response = json_loads(json) - if response[0] < 0: - raise CaptchaTraderException(response[1]) - - except BadHeader, e: - self.logError(_("Could not send response."), str(e)) - - def newCaptchaTask(self, task): - if not task.isTextual(): - return False - - if not self.getConfig("username") or not self.getConfig("passkey"): - return False - - if self.core.isClientConnected() and not self.getConfig("force"): - return False - - if self.getCredits() > 10: - task.handler.append(self) - task.setWaiting(100) - start_new_thread(self.processCaptcha, (task,)) - - else: - self.logInfo(_("Your CaptchaTrader Account has not enough credits")) - - def captchaCorrect(self, task): - if "ticket" in task.data: - ticket = task.data["ticket"] - self.respond(ticket, True) - - def captchaInvalid(self, task): - if "ticket" in task.data: - ticket = task.data["ticket"] - self.respond(ticket, False) - - def processCaptcha(self, task): - c = task.captchaFile - try: - ticket, result = self.submit(c) - except CaptchaTraderException, e: - task.error = e.getCode() - return - - task.data["ticket"] = ticket - task.setResult(result) diff --git a/module/plugins/hooks/Checksum.py b/module/plugins/hooks/Checksum.py index af37d69e6..8d9f8f981 100644 --- a/module/plugins/hooks/Checksum.py +++ b/module/plugins/hooks/Checksum.py @@ -1,31 +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/>. - - @author: zoidberg -""" - 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): @@ -33,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() @@ -43,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 @@ -53,32 +38,46 @@ def computeChecksum(local_file, algorithm): class Checksum(Hook): - __name__ = "Checksum" - __version__ = "0.12" - __description__ = """Verify downloaded file size and checksum""" - __config__ = [("activated", "bool", "Activated", False), + __name__ = "Checksum" + __type__ = "hook" + __version__ = "0.15" + + __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)] - __author_name__ = ("zoidberg", "Walter Purcaro") - __author_mail__ = ("zoidberg@mujmail.cz", "vuolter@gmail.com") + + __description__ = """Verify downloaded file size and checksum""" + __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( getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")), reverse=True) self.algorithms.extend(["crc32", "adler32"]) - self.formats = self.algorithms + ['sfv', 'crc', 'hash'] + self.formats = self.algorithms + ["sfv", "crc", "hash"] + def downloadFinished(self, pyfile): """ @@ -87,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 @@ -111,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'] @@ -125,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") @@ -145,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) + file_type = splitext(link['name'])[1][1:].lower() if file_type not in self.formats: continue - hash_file = fs_encode(save_join(download_folder, link["name"])) + 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: @@ -172,14 +177,14 @@ class Checksum(Hook): for m in re.finditer(self.regexps.get(file_type, self.regexps['default']), text): data = m.groupdict() - self.logDebug(link["name"], data) + 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 002fd4cd7..04aac2f10 100644 --- a/module/plugins/hooks/ClickAndLoad.py +++ b/module/plugins/hooks/ClickAndLoad.py @@ -1,52 +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/>. - - @author: RaNaN - @interface-version: 0.2 -""" - import socket import thread from module.plugins.Hook import Hook -class ClickAndLoad(Hook): - __name__ = "ClickAndLoad" - __version__ = "0.22" - __description__ = """Gives abillity to use jd's click and load. depends on webinterface""" - __config__ = [("activated", "bool", "Activated", True), - ("extern", "bool", "Allow external link adding", False)] - __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() @@ -88,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 f7bc1b90f..f03ac4567 100644 --- a/module/plugins/hooks/DeathByCaptcha.py +++ b/module/plugins/hooks/DeathByCaptcha.py @@ -1,33 +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/>. - - @author: mkaay, RaNaN, zoidberg -""" 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): @@ -40,40 +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" - __description__ = """Send captchas to DeathByCaptcha.com""" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), + __name__ = "DeathByCaptcha" + __type__ = "hook" + __version__ = "0.04" + + __config__ = [("username", "str", "Username", ""), ("passkey", "password", "Password", ""), ("force", "bool", "Force DBC even if client is connected", False)] - __author_name__ = ("RaNaN", "zoidberg") - __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + + __description__ = """Send captchas to DeathByCaptcha.com""" + __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() @@ -85,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: @@ -113,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: @@ -142,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 @@ -181,9 +186,10 @@ class DeathByCaptcha(Hook): self.logError(e.getDesc()) return False - balance, rate = self.info["balance"], self.info["rate"] - self.logInfo("Account balance: US$%.3f (%d captchas left at %.2f cents each)" % (balance / 100, - balance // rate, rate)) + balance, rate = self.info['balance'], self.info['rate'] + self.logInfo(_("Account balance"), + _("US$%.3f (%d captchas left at %.2f cents each)") % (balance / 100, + balance // rate, rate)) if balance > rate: task.handler.append(self) @@ -191,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: @@ -209,5 +219,5 @@ class DeathByCaptcha(Hook): self.logError(e.getDesc()) return - task.data["ticket"] = ticket + task.data['ticket'] = ticket task.setResult(result) diff --git a/module/plugins/hooks/DebridItaliaCom.py b/module/plugins/hooks/DebridItaliaCom.py index fb6be674f..e31bc98d7 100644 --- a/module/plugins/hooks/DebridItaliaCom.py +++ b/module/plugins/hooks/DebridItaliaCom.py @@ -1,41 +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 3bc98a7b3..5d2b78d50 100644 --- a/module/plugins/hooks/DeleteFinished.py +++ b/module/plugins/hooks/DeleteFinished.py @@ -1,84 +1,79 @@ # -*- 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. +from module.database import style +from module.plugins.Hook import Hook - 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/>. +class DeleteFinished(Hook): + __name__ = "DeleteFinished" + __type__ = "hook" + __version__ = "1.11" - @author: Walter Purcaro -""" + __config__ = [('activated', 'bool', 'Activated', 'False'), + ('interval', 'int', 'Delete every (hours)', '72'), + ('deloffline', 'bool', 'Delete packages with offline links', 'False')] -from module.database import style -from module.plugins.Hook import Hook + __description__ = """Automatically delete all finished packages from queue""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] -class DeleteFinished(Hook): - __name__ = 'DeleteFinished' - __version__ = '1.09' - __description__ = 'Automatically delete all finished packages from queue' - __config__ = [ - ('activated', 'bool', 'Activated', 'False'), - ('interval', 'int', 'Delete every (hours)', '72'), - ('deloffline', 'bool', 'Delete packages with offline links', 'False') - ] - __author_name__ = ('Walter Purcaro') - __author_mail__ = ('vuolter@gmail.com') + # event_list = ["pluginConfigChanged"] + ## overwritten methods ## def periodical(self): if not self.info['sleep']: deloffline = self.getConfig('deloffline') mode = '0,1,4' if deloffline else '0,4' - msg = 'delete all finished packages in queue list (%s packages with offline links)' - self.logInfo(msg % ('including' if deloffline else 'excluding')) + 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 41a20e5d1..4996e212d 100644 --- a/module/plugins/hooks/DownloadScheduler.py +++ b/module/plugins/hooks/DownloadScheduler.py @@ -1,44 +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/>. - - @author: zoidberg - 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" - __description__ = """Download Scheduler""" - __config__ = [("activated", "bool", "Activated", False), - ("timetable", "str", "List time periods as hh:mm full or number(kB/s)", + __name__ = "DownloadScheduler" + __type__ = "hook" + __version__ = "0.22" + + __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)] - __author_name__ = ("zoidberg", "stickell") - __author_mail__ = ("zoidberg@mujmail.cz", "l.stickell@yahoo.it") + + __description__ = """Download Scheduler""" + __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") @@ -46,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() @@ -66,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() @@ -77,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 a3a2dcb92..0dab2a7fe 100644 --- a/module/plugins/hooks/EasybytezCom.py +++ b/module/plugins/hooks/EasybytezCom.py @@ -2,33 +2,38 @@ 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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class EasybytezCom(MultiHook): + __name__ = "EasybytezCom" + __type__ = "hook" + __version__ = "0.04" + + __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): self.account = self.core.accountManager.getAccountPlugin(self.__name__) user = self.account.selectAccount()[0] try: - req = self.account.getAccountRequest(user) + req = self.account.getAccountRequest(user) page = req.load("http://www.easybytez.com") - found = re.search(r'</textarea>\s*Supported sites:(.*)', page) - return found.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 1e2b62062..000000000 --- a/module/plugins/hooks/Ev0InFetcher.py +++ /dev/null @@ -1,90 +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/>. - - @author: mkaay -""" -from time import mktime, time - -from module.lib import feedparser -from module.plugins.Hook import Hook - - -class Ev0InFetcher(Hook): - __name__ = "Ev0InFetcher" - __version__ = "0.21" - __description__ = """Checks rss feeds for Ev0.in""" - __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")] - __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 7be30f86e..54de8eb53 100644 --- a/module/plugins/hooks/ExpertDecoders.py +++ b/module/plugins/hooks/ExpertDecoders.py @@ -1,82 +1,77 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: mkaay, RaNaN, zoidberg -""" 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" - __description__ = """Send captchas to expertdecoders.com""" - __config__ = [("activated", "bool", "Activated", False), - ("force", "bool", "Force CT even if client is connected", False), + __name__ = "ExpertDecoders" + __type__ = "hook" + __version__ = "0.03" + + __config__ = [("force", "bool", "Force CT even if client is connected", False), ("passkey", "password", "Access key", "")] - __author_name__ = ("RaNaN", "zoidberg") - __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + + __description__ = """Send captchas to expertdecoders.com""" + __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() + task.data['ticket'] = ticket = uuid4() result = None 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 @@ -95,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 3d84dcc3d..a35e47c03 100644 --- a/module/plugins/hooks/ExternalScripts.py +++ b/module/plugins/hooks/ExternalScripts.py @@ -1,24 +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/>. - - @author: mkaay - @interface-version: 0.1 -""" - import subprocess + +from itertools import chain from os import listdir, access, X_OK, makedirs from os.path import join, exists, basename, abspath @@ -27,21 +11,38 @@ from module.utils import save_join class ExternalScripts(Hook): - __name__ = "ExternalScripts" - __version__ = "0.23" - __description__ = """Run external scripts""" + __name__ = "ExternalScripts" + __type__ = "hook" + __version__ = "0.25" + __config__ = [("activated", "bool", "Activated", True)] - __author_name__ = ("mkaay", "RaNaN", "spoob") - __author_mail__ = ("mkaay@mkaay.de", "ranan@pyload.org", "spoob@pyload.org") - event_list = ["unrarFinished", "allDownloadsFinished", "allDownloadsProcessed"] + __description__ = """Run external scripts""" + __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"] + + + #@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] = [] @@ -51,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): @@ -70,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 12e53fe50..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,44 +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" + __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""" - __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)] - __author_name__ = ("pyload Team", "AndroKev") - __author_mail__ = ("admin<at>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) @@ -93,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() @@ -110,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 + out = save_join(dl, p.folder, destination, "") #: force trailing slash - if self.getConfig("destination") and self.getConfig("destination").lower() != "none": + if subfolder: + out = save_join(out, p.folder) - out = save_join(dl, p.folder, self.getConfig("destination"), "") - #relative to package folder if destination is relative, otherwise absolute path overwrites them + if not exists(out): + makedirs(out) - if self.getConfig("subfolder"): - out = join(out, fs_encode(p.folder)) - - 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 + files_ids = [(save_join(dl, p.folder, x['name']), x['id']) for x in p.getChildren().itervalues()] + matched = False + success = True # check as long there are unseen files while files_ids: new_files_ids = [] + 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() + + 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")) - if not plugin.checkArchive(): + 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: @@ -251,67 +355,87 @@ 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 self.config['permission']['change_file']: if isfile(f): - chmod(f, int(self.config["permission"]["file"], 8)) + chmod(f, int(self.config['permission']['file'], 8)) elif isdir(f): - chmod(f, int(self.config["permission"]["folder"], 8)) + chmod(f, int(self.config['permission']['folder'], 8)) - if self.config["permission"]["change_dl"] and os.name != "nt": - uid = getpwnam(self.config["permission"]["user"])[2] - gid = getgrnam(self.config["permission"]["group"])[2] + if self.config['permission']['change_dl'] and os.name != "nt": + uid = getpwnam(self.config['permission']['user'])[2] + gid = getgrnam(self.config['permission']['group'])[2] chown(f, uid, gid) + except Exception, e: self.logWarning(_("Setting User and Group failed"), e) diff --git a/module/plugins/hooks/FastixRu.py b/module/plugins/hooks/FastixRu.py index 558da1b86..73297eb23 100644 --- a/module/plugins/hooks/FastixRu.py +++ b/module/plugins/hooks/FastixRu.py @@ -1,27 +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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class FastixRu(MultiHook): + __name__ = "FastixRu" + __type__ = "hook" + __version__ = "0.03" + + __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 7d4bcc852..0b71fc35b 100644 --- a/module/plugins/hooks/FreeWayMe.py +++ b/module/plugins/hooks/FreeWayMe.py @@ -1,40 +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/>. - - @author: Nicolas Giese -""" - 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" - __description__ = """FreeWay.me hook plugin""" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +class FreeWayMe(MultiHook): + __name__ = "FreeWayMe" + __type__ = "hook" + __version__ = "0.12" + + __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)] - __author_name__ = "Nicolas Giese" - __author_mail__ = "james@free-way.me" + + __description__ = """FreeWay.me hook plugin""" + __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 38bcaf440..b0b59e2ba 100644 --- a/module/plugins/hooks/HotFolder.py +++ b/module/plugins/hooks/HotFolder.py @@ -1,83 +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/>. - - @author: RaNaN - @interface-version: 0.2 -""" - -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" - __description__ = """Observe folder and file for changes and add container and links""" - __config__ = [("activated", "bool", "Activated", False), - ("folder", "str", "Folder to observe", "container"), + __name__ = "HotFolder" + __type__ = "hook" + __version__ = "0.12" + + __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")] - __threaded__ = [] - __author_name__ = "RaNaN" - __author_mail__ = "RaNaN@pyload.de" + + __description__ = """Observe folder and file for changes and add container and links""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.de")] + def setup(self): self.interval = 10 + def periodical(self): + folder = fs_encode(self.getConfig("folder")) - if not exists(join(self.getConfig("folder"), "finished")): - makedirs(join(self.getConfig("folder"), "finished")) + 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 760c1a4df..efd4e411d 100644 --- a/module/plugins/hooks/IRCInterface.py +++ b/module/plugins/hooks/IRCInterface.py @@ -1,73 +1,62 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: RaNaN - @author: jeix - @interface-version: 0.2 -""" +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" - __description__ = """Connect to irc and let owner perform different tasks""" - __config__ = [("activated", "bool", "Activated", False), - ("host", "str", "IRC-Server Address", "Enter your server here!"), + __name__ = "IRCInterface" + __type__ = "hook" + __version__ = "0.13" + + __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), ("info_pack", "bool", "Inform about every package finished", True), ("captcha", "bool", "Send captcha requests", True)] - __author_name__ = "Jeix" - __author_mail__ = "Jeix@hasnomail.com" + + __description__ = """Connect to irc and let owner perform different tasks""" + __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 - def coreReady(self): - self.new_package = {} - self.abort = False + #@TODO: Remove in 0.4.10 + def initPeriodical(self): + pass - self.links_added = 0 + + def coreReady(self): + self.abort = False self.more = [] + self.new_package = {} self.start() + def packageFinished(self, pypack): try: if self.getConfig("info_pack"): @@ -75,6 +64,7 @@ class IRCInterface(Thread, Hook): except: pass + def downloadFinished(self, pyfile): try: if self.getConfig("info_file"): @@ -83,6 +73,7 @@ class IRCInterface(Thread, Hook): except: pass + def newCaptchaTask(self, task): if self.getConfig("captcha") and task.isTextual(): task.handler.append(self) @@ -95,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() @@ -116,6 +112,7 @@ class IRCInterface(Thread, Hook): print_exc() self.sock.close() + def main_loop(self): readbuffer = "" while True: @@ -154,34 +151,35 @@ 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(): + if not msg['origin'].split("!", 1)[0] in self.getConfig("owner").split(): return - if msg["target"].split("!", 1)[0] != self.getConfig("nick"): + if msg['target'].split("!", 1)[0] != self.getConfig("nick"): return - if msg["action"] != "PRIVMSG": + if msg['action'] != "PRIVMSG": return # HANDLE CTCP ANTI FLOOD/BOT PROTECTION - if msg["text"] == "\x01VERSION\x01": - self.logDebug("Sending CTCP VERSION.") + if msg['text'] == "\x01VERSION\x01": + self.logDebug("Sending CTCP VERSION") self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) return - elif msg["text"] == "\x01TIME\x01": - self.logDebug("Sending CTCP TIME.") + elif msg['text'] == "\x01TIME\x01": + self.logDebug("Sending CTCP TIME") self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) return - elif msg["text"] == "\x01LAG\x01": - self.logDebug("Received CTCP LAG.") # don't know how to answer + elif msg['text'] == "\x01LAG\x01": + self.logDebug("Received CTCP LAG") #: don't know how to answer return trigger = "pass" args = None try: - temp = msg["text"].split() + temp = msg['text'].split() trigger = temp[0] if len(temp) > 1: args = temp[1:] @@ -192,9 +190,10 @@ class IRCInterface(Thread, Hook): try: res = handler(args) for line in res: - self.response(line, msg["origin"]) + self.response(line, msg['origin']) except Exception, e: - self.logError("pyLoad IRC: " + repr(e)) + self.logError(e) + def response(self, msg, origin=""): if origin == "": @@ -203,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."] @@ -233,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."] @@ -245,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!"] @@ -256,27 +259,29 @@ class IRCInterface(Thread, Hook): return lines + def event_info(self, args): if not args: - return ['ERROR: Use info like this: info <id>'] + 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>'] + return ["ERROR: Use packinfo like this: packinfo <id>"] 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."] @@ -300,6 +305,7 @@ class IRCInterface(Thread, Hook): return lines + def event_more(self, args): if not self.more: return ["No more information to display."] @@ -310,20 +316,21 @@ class IRCInterface(Thread, Hook): return lines - def event_start(self, args): - self.api.unpauseServer() + def event_start(self, args): + self.core.api.unpauseServer() return ["INFO: Starting downloads."] - def event_stop(self, args): - self.api.pauseServer() + def event_stop(self, args): + 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". ', - 'This will add the link <link> to to the package <package> / the package with id <id>!'] + "This will add the link <link> to to the package <package> / the package with id <id>!"] pack = args[0].strip() links = [x.strip() for x in args[1:]] @@ -332,58 +339,62 @@ 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."] #TODO add links - return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack["name"], id)] + return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)] except: # create new package - id = self.api.addPackage(pack, links, 1) + 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: @@ -396,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)", @@ -415,8 +427,10 @@ class IRCInterface(Thread, Hook): 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 e2e9d93d5..f89d64c37 100644 --- a/module/plugins/hooks/ImageTyperz.py +++ b/module/plugins/hooks/ImageTyperz.py @@ -1,86 +1,90 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: mkaay, RaNaN, zoidberg -""" 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 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" - __description__ = """Send captchas to ImageTyperz.com""" - __config__ = [("activated", "bool", "Activated", False), - ("username", "str", "Username", ""), + __name__ = "ImageTyperz" + __type__ = "hook" + __version__ = "0.05" + + __config__ = [("username", "str", "Username", ""), ("passkey", "password", "Password", ""), ("force", "bool", "Force IT even if client is connected", False)] - __author_name__ = ("RaNaN", "zoidberg") - __author_mail__ = ("RaNaN@pyload.org", "zoidberg@mujmail.cz") + + __description__ = """Send captchas to ImageTyperz.com""" + __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: @@ -89,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 @@ -127,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 @@ -148,5 +158,5 @@ class ImageTyperz(Hook): task.error = e.getCode() return - task.data["ticket"] = ticket + task.data['ticket'] = ticket task.setResult(result) diff --git a/module/plugins/hooks/LinkdecrypterCom.py b/module/plugins/hooks/LinkdecrypterCom.py index dd9cd79f2..b0ce335d0 100644 --- a/module/plugins/hooks/LinkdecrypterCom.py +++ b/module/plugins/hooks/LinkdecrypterCom.py @@ -1,36 +1,26 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: zoidberg -""" - 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" + __name__ = "LinkdecrypterCom" + __type__ = "hook" + __version__ = "0.21" + __description__ = """Linkdecrypter.com hook plugin""" - __config__ = [("activated", "bool", "Activated", False)] - __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): try: @@ -38,15 +28,22 @@ 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) - if not m: + 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 builtin = [name.lower() for name in self.core.pluginManager.crypterPlugins.keys()] - builtin.extend(["downloadserienjunkiesorg"]) + builtin.append("downloadserienjunkiesorg") crypter_pattern = re.compile("(\w[\w.-]+)") online = [] @@ -59,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) + 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 110731228..96bf1c0d1 100644 --- a/module/plugins/hooks/LinksnappyCom.py +++ b/module/plugins/hooks/LinksnappyCom.py @@ -1,26 +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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class LinksnappyCom(MultiHook): + __name__ = "LinksnappyCom" + __type__ = "hook" + __version__ = "0.02" + + __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 0c3bb99f6..f3a0c31ea 100644 --- a/module/plugins/hooks/MegaDebridEu.py +++ b/module/plugins/hooks/MegaDebridEu.py @@ -1,42 +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" - __config__ = [("activated", "bool", "Activated", False), - ("unloadFailing", "bool", "Revert to standard download if download fails", False)] +class MegaDebridEu(MultiHook): + __name__ = "MegaDebridEu" + __type__ = "hook" + __version__ = "0.03" + + __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": + 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 99b0aafc6..4de45f958 100644 --- a/module/plugins/hooks/MergeFiles.py +++ b/module/plugins/hooks/MergeFiles.py @@ -1,56 +1,52 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: and9000 -""" +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 +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" + __name__ = "MergeFiles" + __type__ = "hook" + __version__ = "0.13" + + __config__ = [("activated", "bool", "Activated", True)] + __description__ = """Merges parts splitted with hjsplit""" - __config__ = [("activated", "bool", "Activated", False)] - __threaded__ = ["packageFinished"] - __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 data["name"][:-4] not in files: - files[data["name"][:-4]] = [] - files[data["name"][:-4]].append(data["name"]) - files[data["name"][:-4]].sort() - fid_dict[data["name"]] = fid + 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']) + files[data['name'][:-4]].sort() + fid_dict[data['name']] = fid download_folder = self.config['general']['download_folder'] @@ -58,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 f2dfb18ca..000000000 --- a/module/plugins/hooks/MultiDebridCom.py +++ /dev/null @@ -1,41 +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 b1635a588..105a42abd 100644 --- a/module/plugins/hooks/MultiHome.py +++ b/module/plugins/hooks/MultiHome.py @@ -1,67 +1,63 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: mkaay -""" - from time import time from module.plugins.Hook import Hook class MultiHome(Hook): - __name__ = "MultiHome" - __version__ = "0.11" + __name__ = "MultiHome" + __type__ = "hook" + __version__ = "0.12" + + __config__ = [("interfaces", "str", "Interfaces", "None")] + __description__ = """Ip address changer""" - __config__ = [("activated", "bool", "Activated", False), - ("interfaces", "str", "Interfaces", "None")] - __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): self.register = {} self.interfaces = [] self.parseInterfaces(self.getConfig("interfaces").split(";")) if not self.interfaces: - self.parseInterfaces([self.config["download"]["interface"]]) + 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: @@ -71,17 +67,21 @@ class MultiHome(Hook): class Interface(object): + def __init__(self, adress): self.adress = adress self.history = {} + def lastPluginAccess(self, pluginName, account): if (pluginName, account) in self.history: return self.history[(pluginName, account)] return 0 + def useFor(self, pluginName, account): self.history[(pluginName, account)] = time() + def __repr__(self): return "<Interface - %s>" % self.adress diff --git a/module/plugins/hooks/MultishareCz.py b/module/plugins/hooks/MultishareCz.py index 0291738f5..5ec5b63b6 100644 --- a/module/plugins/hooks/MultishareCz.py +++ b/module/plugins/hooks/MultishareCz.py @@ -3,22 +3,25 @@ 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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class MultishareCz(MultiHook): + __name__ = "MultishareCz" + __type__ = "hook" + __version__ = "0.05" + + __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"' + def getHoster(self): page = getURL("http://www.multishare.cz/monitoring/") return re.findall(self.HOSTER_PATTERN, page) 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/OverLoadMe.py b/module/plugins/hooks/OverLoadMe.py index e15d0b05f..378ce0a65 100644 --- a/module/plugins/hooks/OverLoadMe.py +++ b/module/plugins/hooks/OverLoadMe.py @@ -1,28 +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" - __config__ = [("activated", "bool", "Activated", False), - ("https", "bool", "Enable HTTPS", True), +class OverLoadMe(MultiHook): + __name__ = "OverLoadMe" + __type__ = "hook" + __version__ = "0.02" + + __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 57b188bb9..000000000 --- a/module/plugins/hooks/Premium4Me.py +++ /dev/null @@ -1,32 +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 9f1a70a70..b37728e06 100644 --- a/module/plugins/hooks/PremiumizeMe.py +++ b/module/plugins/hooks/PremiumizeMe.py @@ -1,25 +1,24 @@ # -*- 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" - __description__ = """Premiumize.me hook plugin""" +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)] - __author_name__ = "Florian Franzen" - __author_mail__ = "FlorianFranzen@gmail.com" + __description__ = """Premiumize.me hook plugin""" + __license__ = "GPLv3" + __authors__ = [("Florian Franzen", "FlorianFranzen@gmail.com")] + def getHoster(self): # If no accounts are available there will be no hosters available @@ -31,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 54f814231..c54f7d445 100644 --- a/module/plugins/hooks/RPNetBiz.py +++ b/module/plugins/hooks/RPNetBiz.py @@ -1,22 +1,24 @@ # -*- 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" - __description__ = """RPNet.biz hook plugin""" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), +class RPNetBiz(MultiHook): + __name__ = "RPNetBiz" + __type__ = "hook" + __version__ = "0.11" + + __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)] - __author_name__ = "Dman" - __author_mail__ = "dmanugm@gmail.com" + + __description__ = """RPNet.biz hook plugin""" + __license__ = "GPLv3" + __authors__ = [("Dman", "dmanugm@gmail.com")] + def getHoster(self): # No hosts supported if no account @@ -26,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") @@ -45,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 566f9005f..066aa52c4 100644 --- a/module/plugins/hooks/RealdebridCom.py +++ b/module/plugins/hooks/RealdebridCom.py @@ -1,23 +1,24 @@ # -*- 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): https = "https" if self.getConfig("https") else "http" diff --git a/module/plugins/hooks/RehostTo.py b/module/plugins/hooks/RehostTo.py index 6c3a77ca3..48afa2342 100644 --- a/module/plugins/hooks/RehostTo.py +++ b/module/plugins/hooks/RehostTo.py @@ -1,39 +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"] + self.ses = data['ses'] + self.long_ses = data['long_ses'] - return MultiHoster.coreReady(self) + return MultiHook.coreReady(self) diff --git a/module/plugins/hooks/ReloadCc.py b/module/plugins/hooks/ReloadCc.py deleted file mode 100644 index 9960a2699..000000000 --- a/module/plugins/hooks/ReloadCc.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- - -from module.plugins.internal.MultiHoster import MultiHoster - -from module.common.json_layer import json_loads -from module.network.RequestFactory import getURL - - -class ReloadCc(MultiHoster): - __name__ = "ReloadCc" - __version__ = "0.3" - __type__ = "hook" - __description__ = """Reload.cc hook plugin""" - - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"), - ("hosterList", "str", "Hoster list (comma separated)", "")] - - __author_name__ = "Reload Team" - __author_mail__ = "hello@reload.cc" - - interval = 0 # Disable periodic calls - - def getHoster(self): - # If no accounts are available there will be no hosters available - if not self.account or not self.account.canUse(): - print "ReloadCc: No accounts available" - return [] - - # Get account data - (user, data) = self.account.selectAccount() - - # Get supported hosters list from reload.cc using the json API v1 - query_params = dict( - via='pyload', - v=1, - get_supported='true', - get_traffic='true', - user=user - ) - - try: - query_params.update(dict(hash=self.account.infos[user]['pwdhash'])) - except Exception: - query_params.update(dict(pwd=data['password'])) - - answer = getURL("http://api.reload.cc/login", get=query_params) - data = json_loads(answer) - - # If account is not valid thera are no hosters available - if data['status'] != "ok": - print "ReloadCc: Status is not ok: %s" % data['status'] - return [] - - # Extract hosters from json file - return data['msg']['supportedHosters'] - - def coreReady(self): - # Get account plugin and check if there is a valid account available - self.account = self.core.accountManager.getAccountPlugin("ReloadCc") - if not self.account.canUse(): - self.account = None - self.logError("Please add a valid reload.cc account first and restart pyLoad.") - return - - # Run the overwriten core ready which actually enables the multihoster hook - return MultiHoster.coreReady(self) diff --git a/module/plugins/hooks/RestartFailed.py b/module/plugins/hooks/RestartFailed.py index 85553d738..07fb80967 100644 --- a/module/plugins/hooks/RestartFailed.py +++ b/module/plugins/hooks/RestartFailed.py @@ -1,57 +1,44 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: Walter Purcaro -""" - from module.plugins.Hook import Hook class RestartFailed(Hook): - __name__ = "RestartFailed" - __version__ = "1.53" + __name__ = "RestartFailed" + __type__ = "hook" + __version__ = "1.57" + + __config__ = [("interval", "int", "Check interval in minutes", 90)] + __description__ = """Periodically restart all failed downloads in queue""" - __config__ = [("activated", "bool", "Activated", False), - ("interval", "int", "Interval in minutes", 90)] - __author_name__ = "Walter Purcaro" - __author_mail__ = "vuolter@gmail.com" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] - event_list = ["pluginConfigChanged"] - MIN_INTERVAL = 15 * 60 # seconds + # event_list = ["pluginConfigChanged"] - def periodical(self): - self.logDebug("Restart all failed downloads now") - self.core.api.restartFailed() + MIN_INTERVAL = 15 * 60 #: 15m minimum check interval (value is in seconds) - def restartPeriodical(self, interval): - self.logDebug("Set periodical interval to %s seconds" % interval) - if self.cb: - self.core.scheduler.removeJob(self.cb) - self.interval = interval - self.cb = self.core.scheduler.addJob(interval, self._periodical, threaded=False) def pluginConfigChanged(self, plugin, name, value): - value *= 60 if name == "interval": - if self.interval != value > self.MIN_INTERVAL: - self.restartPeriodical(value) + interval = value * 60 + if self.MIN_INTERVAL <= interval != self.interval: + self.core.scheduler.removeJob(self.cb) + self.interval = interval + self.initPeriodical() else: - self.logWarning("Cannot change interval: given value is equal to the current or \ - smaller than %s seconds" % self.MIN_INTERVAL) + self.logDebug("Invalid interval value, kept current") + + + def periodical(self): + self.logDebug(_("Restart failed downloads")) + self.core.api.restartFailed() + + + def setup(self): + self.interval = self.MIN_INTERVAL + def coreReady(self): - self.pluginConfigChanged(plugin="RestartFailed", name="interval", value=self.getConfig("interval")) + 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 ca1122e45..10a1655c2 100644 --- a/module/plugins/hooks/SimplyPremiumCom.py +++ b/module/plugins/hooks/SimplyPremiumCom.py @@ -1,42 +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 SimplyPremiumCom(MultiHook): + __name__ = "SimplyPremiumCom" + __type__ = "hook" + __version__ = "0.03" -class SimplyPremiumCom(MultiHoster): - __name__ = "SimplyPremiumCom" - __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__ = """Simply-Premium.Com hook plugin""" - __author_name__ = ("EvolutionClip") - __author_mail__ = ("evolutionclip@live.de") + + __description__ = """Simply-Premium.com hook plugin""" + __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['host'] for element in json_data['result']] + host_list = [element['regex'] for element in json_data['result']] return host_list diff --git a/module/plugins/hooks/SimplydebridCom.py b/module/plugins/hooks/SimplydebridCom.py index a523d2404..48568f870 100644 --- a/module/plugins/hooks/SimplydebridCom.py +++ b/module/plugins/hooks/SimplydebridCom.py @@ -1,20 +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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class SimplydebridCom(MultiHook): + __name__ = "SimplydebridCom" + __type__ = "hook" + __version__ = "0.02" + + __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 af6039ecd..1becb937a 100644 --- a/module/plugins/hooks/UnSkipOnFail.py +++ b/module/plugins/hooks/UnSkipOnFail.py @@ -1,97 +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. +from module.PyFile import PyFile +from module.plugins.Hook import Hook - 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/>. +class UnSkipOnFail(Hook): + __name__ = "UnSkipOnFail" + __type__ = "hook" + __version__ = "0.05" - @author: hgg -""" -from os.path import basename + __config__ = [("activated", "bool", "Activated", True)] -from module.utils import fs_encode -from module.plugins.Hook import Hook -from module.PyFile import PyFile + __description__ = """Queue skipped duplicates when download fails""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] -class UnSkipOnFail(Hook): - __name__ = 'UnSkipOnFail' - __version__ = '0.01' - __description__ = """When a download fails, restart skipped duplicates""" - __config__ = [("activated", "bool", "Activated", True)] - __author_name__ = "hagg" - __author_mail__ = "" + #@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 4f8f11625..245264d44 100644 --- a/module/plugins/hooks/UnrestrictLi.py +++ b/module/plugins/hooks/UnrestrictLi.py @@ -1,41 +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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class UnrestrictLi(MultiHook): + __name__ = "UnrestrictLi" + __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), ("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 5f88ea6d0..c72699228 100644 --- a/module/plugins/hooks/UpdateManager.py +++ b/module/plugins/hooks/UpdateManager.py @@ -1,121 +1,223 @@ # -*- coding: utf-8 -*- -import sys +from __future__ import with_statement + import re -from os import remove, stat -from os.path import join, isfile -from time import time +import sys + +from operator import itemgetter +from os import path, remove, stat -from module.ConfigParser import IGNORE from module.network.RequestFactory import getURL -from module.plugins.Hook import threaded, Expose, Hook +from module.plugins.Hook import Expose, Hook, threaded +from module.utils import save_join class UpdateManager(Hook): - __name__ = "UpdateManager" - __version__ = "0.16" - __description__ = """Checks for updates""" - __config__ = [("activated", "bool", "Activated", True), - ("interval", "int", "Check interval in minutes", 480), - ("debug", "bool", "Check for plugin changes when in debug mode", False)] - __author_name__ = ("RaNaN", "stickell") - __author_mail__ = ("ranan@pyload.org", "l.stickell@yahoo.it") - - URL = "http://updatemanager.pyload.org" - MIN_TIME = 3 * 60 * 60 # 3h minimum check interval - - @property - def debug(self): - return self.core.debug and self.getConfig("debug") + __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 )] + + __description__ = """ Check for updates """ + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + + + # event_list = ["pluginConfigChanged"] + + 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) + + + def pluginConfigChanged(self, plugin, name, value): + if name == "interval": + interval = value * 60 * 60 + if self.MIN_INTERVAL <= interval != self.interval: + self.core.scheduler.removeJob(self.cb) + self.interval = interval + self.initPeriodical() + else: + self.logDebug("Invalid interval value, kept current") + + elif name == "reloadplugins": + if self.cb2: + self.core.scheduler.removeJob(self.cb2) + if value is True and self.core.debug: + self.periodical2() + + + def coreReady(self): + self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval")) + x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins")) + self.core.scheduler.addJob(10, x, threaded=False) + + + def unload(self): + self.pluginConfigChanged(self.__name__, "reloadplugins", False) + def setup(self): - if self.debug: - self.logDebug("Monitoring file changes") - self.interval = 4 - self.last_check = 0 # timestamp of updatecheck - self.old_periodical = self.periodical - self.periodical = self.checkChanges - self.mtimes = {} # recordes times - else: - self.interval = max(self.getConfig("interval") * 60, self.MIN_TIME) + 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.updated = False - self.reloaded = True - self.version = "None" - self.info = {"pyload": False, "plugins": False} + def periodical2(self): + if not self.updating: + self.autoreloadPlugins() + + self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False) - @threaded - def periodical(self): - updates = self.checkForUpdate() - if updates: - self.checkPlugins(updates) - - if self.updated and not self.reloaded: - self.info["plugins"] = True - self.logInfo(_("*** Plugins have been updated, please restart pyLoad ***")) - elif self.updated and self.reloaded: - self.logInfo(_("Plugins updated and reloaded")) - self.updated = False - elif self.version == "None": - self.logInfo(_("No plugin updates available")) @Expose - def recheckForUpdates(self): - """recheck if updates are available""" - self.periodical() + def autoreloadPlugins(self): + """ reload and reindex all modified plugins """ + modules = filter( + lambda m: m and (m.__name__.startswith("module.plugins.") or + m.__name__.startswith("userplugins.")) and + m.__name__.count(".") >= 2, sys.modules.itervalues() + ) + + reloads = [] - def checkForUpdate(self): - """checks if an update is available, return result""" + for m in modules: + root, type, name = m.__name__.rsplit(".", 2) + id = (type, name) + if type in self.core.pluginManager.plugins: + f = m.__file__.replace(".pyc", ".py") + if not path.isfile(f): + continue + + mtime = stat(f).st_mtime + + if id not in self.mtimes: + self.mtimes[id] = mtime + elif self.mtimes[id] < mtime: + reloads.append(id) + self.mtimes[id] = mtime + + return True if self.core.pluginManager.reloadPlugins(reloads) else False + + + def periodical(self): + if not self.info['pyload'] and not (self.getConfig("nodebugupdate") and self.core.debug): + self.updateThread() + + + def server_request(self): try: - if self.version == "None": # No updated known - version_check = getURL(self.URL, get={'v': self.core.api.getServerVersion()}).splitlines() - self.version = version_check[0] - - # Still no updates, plugins will be checked - if self.version == "None": - self.logInfo(_("No Updates for pyLoad")) - return version_check[1:] - - self.info["pyload"] = True - self.logInfo(_("*** New pyLoad Version %s available ***") % self.version) - self.logInfo(_("*** Get it here: http://pyload.org/download ***")) + return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines() except: - self.logWarning(_("Not able to connect server for updates")) + self.logWarning(_("Unable to contact server to get updates")) - return None # Nothing will be done - def checkPlugins(self, updates): - """ checks for plugins updates""" + @threaded + def updateThread(self): + self.updating = True - # plugins were already updated - if self.info["plugins"]: - return + 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 - reloads = [] - vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)') - url = updates[0] - schema = updates[1].split("|") - if 'BLACKLIST' in updates: + @Expose + def updatePlugins(self): + """ simple wrapper for calling plugin update quickly """ + return self.update(onlyplugin=True) + + + @Expose + def update(self, onlyplugin=False): + """ check for updates """ + data = self.server_request() + + if not data: + exitcode = 0 + + elif data[0] == "None": + self.logInfo(_("No new pyLoad version available")) + updates = data[1:] + exitcode = self._updatePlugins(updates) + + elif onlyplugin: + exitcode = 0 + + else: + newversion = data[0] + self.logInfo(_("*** New pyLoad Version %s available ***") % newversion) + self.logInfo(_("*** Get it here: https://github.com/pyload/pyload/releases ***")) + exitcode = 3 + self.info['pyload'] = True + self.info['version'] = newversion + + return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required; 3 = No plugins updated, new pyLoad version available + + + def _updatePlugins(self, updates): + """ check for plugin updates """ + + if self.info['plugins']: + return False #: plugins were already updated + + exitcode = 0 + updated = [] + + 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, + }) - for plugin in updates: - info = dict(zip(schema, plugin.split("|"))) - filename = info["name"] - prefix = info["type"] - version = info["version"] + for plugin in sorted(upgradable, key=itemgetter("type", "name")): + filename = plugin['name'] + 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: @@ -123,79 +225,86 @@ class UpdateManager(Hook): plugins = getattr(self.core.pluginManager, "%sPlugins" % type) - if name in plugins: - if float(plugins[name]["v"]) >= float(version): - continue + oldver = float(plugins[name]['v']) if name in plugins else None + newver = float(version) - if name in IGNORE or (type, name) in IGNORE: + if not oldver: + msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)" + elif newver > oldver: + msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)" + else: continue - self.logInfo(_("New version of %(type)s|%(name)s : %(version).2f") % { - "type": type, - "name": name, - "version": float(version) - }) - + self.logInfo(_(msg) % {'type' : type, + 'name' : name, + 'oldver': oldver, + 'newver': newver}) try: - content = getURL(url % info) + content = getURL(url % plugin) + m = self.VERSION.search(content) + + if m and m.group(2) == version: + 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.logWarning(_("Error when updating %s") % filename, str(e)) - continue + self.logError(_("Error updating plugin: %s") % filename, str(e)) - m = vre.search(content) - if not m or m.group(2) != version: - self.logWarning(_("Error when updating %s") % name, _("Version mismatch")) - continue + if updated: + reloaded = self.core.pluginManager.reloadPlugins(updated) + if reloaded: + self.logInfo(_("Plugins updated and reloaded")) + exitcode = 1 + else: + self.logInfo(_("*** Plugins have been updated, but need a pyLoad restart to be reloaded ***")) + self.info['plugins'] = True + exitcode = 2 + else: + self.logInfo(_("No plugin updates available")) - f = open(join("userplugins", prefix, filename), "wb") - f.write(content) - f.close() - self.updated = True + return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required - reloads.append((prefix, name)) - if blacklist: - self.executeBlacklist(blacklist) + @Expose + def removePlugins(self, type_plugins): + """ delete plugins from disk """ - self.reloaded = self.core.pluginManager.reloadPlugins(reloads) + if not type_plugins: + return - def executeBlacklist(self, blacklist): - for b in blacklist: - type, name = b.split('|') - if isfile(join("userplugins", type, name)): - self.logInfo(_("Removing blacklisted plugin %(type)s|%(name)s") % { - "type": type, - "name": name - }) - remove(join("userplugins", type, name)) - if isfile(join("userplugins", type, name.replace('.py', '.pyc'))): - remove(join("userplugins", type, name.replace('.py', '.pyc'))) + self.logDebug("Requested deletion of plugins: %s" % type_plugins) - def checkChanges(self): - if self.last_check + max(self.getConfig("interval") * 60, self.MIN_TIME) < time(): - self.old_periodical() - self.last_check = time() + removed = [] - modules = filter( - lambda m: m and (m.__name__.startswith("module.plugins.") or m.__name__.startswith( - "userplugins.")) and m.__name__.count(".") >= 2, sys.modules.itervalues()) + for type, name in type_plugins: + err = False + file = name + ".py" - reloads = [] + for root in ("userplugins", path.join(pypath, "module", "plugins")): - for m in modules: - root, type, name = m.__name__.rsplit(".", 2) - id = (type, name) - if type in self.core.pluginManager.plugins: - f = m.__file__.replace(".pyc", ".py") - if not isfile(f): - continue + filename = save_join(root, type, file) + try: + remove(filename) + except Exception, e: + self.logDebug("Error removing: %s" % path.basename(filename), str(e)) + err = True - mtime = stat(f).st_mtime + filename += "c" + if path.isfile(filename): + try: + if type == "hook": + self.manager.deactivateHook(name) + remove(filename) + except Exception, e: + self.logDebug("Error removing: %s" % path.basename(filename), str(e)) + err = True - if id not in self.mtimes: - self.mtimes[id] = mtime - elif self.mtimes[id] < mtime: - reloads.append(id) - self.mtimes[id] = mtime + if not err: + id = (type, name) + removed.append(id) - self.core.pluginManager.reloadPlugins(reloads) + return removed #: return a list of the plugins successfully removed diff --git a/module/plugins/hooks/Vipleech4uCom.py b/module/plugins/hooks/Vipleech4uCom.py deleted file mode 100644 index b2156b017..000000000 --- a/module/plugins/hooks/Vipleech4uCom.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- -import re - -from module.network.RequestFactory import getURL -from module.plugins.internal.MultiHoster import MultiHoster - - -class Vipleech4uCom(MultiHoster): - __name__ = "Vipleech4uCom" - __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)", "")] - __description__ = """Vipleech4u.com hook plugin""" - __author_name__ = ("Kagenoshin") - __author_mail__ = ("kagenoshin@gmx.ch") - - HOSTER_PATTERN = re.compile(r'align\s*?=\s*?["\']*?left.*?<\s*?strong\s*?>([^<]*?)<', re.I) - - def getHoster(self): - hosters = { - 'depositfiles': ['depositfiles.com', 'dfiles.eu'], - 'uploaded': ['uploaded.to', 'uploaded.net', 'ul.to'], - 'rapidggator': ['rapidgator.net'], # they have a typo it's called rapidgator - 'freakshare': ['freakshare.net', 'freakshare.com'], - 'filefactory': ['filefactory.com'], - 'bitshare': ['bitshare.com'], - 'share-online': ['share-online.biz', 'egoshare.com'], - 'youtube': ['youtube.com'], - 'turbobit': ['turbobit.net', 'unextfiles.com'], - 'firedrive': ['firedrive.com', 'putlocker.com'], - 'filepost': ['filepost.com', 'fp.io'], - 'netload': ['netload.in'], - 'uploadhero': ['uploadhero.com'], - 'ryushare': ['ryushare.com'], - } - - #check if the list is still valid - self.check_for_new_or_removed_hosters(hosters) - - #build list - hoster_list = [] - - for item in hosters.itervalues(): - hoster_list.extend(item) - - return hoster_list - - def check_for_new_or_removed_hosters(self, hosters): - #get the old hosters - old_hosters = hosters.keys() - - #load the current hosters from vipleech4u.com - page = getURL('http://vipleech4u.com/hosts.php') - current_hosters = self.HOSTER_PATTERN.findall(page) - current_hosters = [x.lower() for x in current_hosters] - - #let's look for new hosters - new_hosters = [] - - for hoster in current_hosters: - if not hoster in old_hosters: - new_hosters.append(hoster) - - #let's look for removed hosters - removed_hosters = [] - - for hoster in old_hosters: - if not hoster in current_hosters: - removed_hosters.append(hoster) - - if new_hosters: - self.logDebug('The following new hosters were found on vipleech4u.com: %s' % str(new_hosters)) - - if removed_hosters: - self.logDebug('The following hosters were removed from vipleech4u.com: %s' % str(removed_hosters)) - - if not (new_hosters and removed_hosters): - self.logDebug('The hoster list is still valid.') diff --git a/module/plugins/hooks/WindowsPhoneToastNotify.py b/module/plugins/hooks/WindowsPhoneToastNotify.py index 7c005fcbb..ed305778c 100644 --- a/module/plugins/hooks/WindowsPhoneToastNotify.py +++ b/module/plugins/hooks/WindowsPhoneToastNotify.py @@ -1,40 +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/>. - - @author: RaNaN, Godofdream, zoidberg -""" -import time import httplib +import time + from module.plugins.Hook import Hook class WindowsPhoneToastNotify(Hook): - __name__ = "WindowsPhoneToastNotify" - __version__ = "0.02" - __description__ = """Send push notifications to Windows Phone""" - __author_name__ = "Andy Voigt" - __author_mail__ = "phone-support@hotmail.de" - __config__ = [("activated", "bool", "Activated", False), - ("force", "bool", "Force even if client is connected", False), + __name__ = "WindowsPhoneToastNotify" + __type__ = "hook" + __version__ = "0.03" + + __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""" + __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'> " @@ -42,6 +36,7 @@ class WindowsPhoneToastNotify(Hook): "</wp:Toast> </wp:Notification>") return myxml + def doRequest(self): URL = self.getConfig("pushUrl") request = self.getXmlData() @@ -57,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 19ecc08b6..589143547 100644 --- a/module/plugins/hooks/XFileSharingPro.py +++ b/module/plugins/hooks/XFileSharingPro.py @@ -6,70 +6,126 @@ from module.plugins.Hook import Hook class XFileSharingPro(Hook): - __name__ = "XFileSharingPro" - __version__ = "0.11" - __type__ = "hook" - __config__ = [("activated", "bool", "Activated", True), - ("loadDefault", "bool", "Include default (built-in) hoster list", True), - ("includeList", "str", "Include hosters (comma separated)", ""), - ("excludeList", "str", "Exclude hosters (comma separated)", "")] - __description__ = """XFileSharingPro hook plugin""" - __author_name__ = "zoidberg" - __author_mail__ = "zoidberg@mujmail.cz" + __name__ = "XFileSharingPro" + __type__ = "hook" + __version__ = "0.28" + + __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__ = """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') + + 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(dict['pattern']) + def unload(self): - dict = self.core.pluginManager.hosterPlugins['XFileSharingPro'] - dict["pattern"] = r"^unmatchable$" - dict["re"] = re.compile(r"^unmatchable$") + # 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 5ecf4e153..bbeab4341 100644 --- a/module/plugins/hooks/XMPPInterface.py +++ b/module/plugins/hooks/XMPPInterface.py @@ -1,49 +1,35 @@ # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - @author: RaNaN - @interface-version: 0.2 -""" - 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" - __description__ = """Connect to jabber and let owner perform different tasks""" - __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"), ("info_file", "bool", "Inform about every file finished", False), ("info_pack", "bool", "Inform about every package finished", True), ("captcha", "bool", "Send captcha requests", True)] - __author_name__ = "RaNaN" - __author_mail__ = "RaNaN@pyload.org" + + __description__ = """Connect to jabber and let owner perform different tasks""" + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] + implements(IMessageHandlersProvider) + def __init__(self, core, manager): IRCInterface.__init__(self, core, manager) @@ -72,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"): @@ -84,6 +72,7 @@ class XMPPInterface(IRCInterface, JabberClient): except: pass + def downloadFinished(self, pyfile): try: if self.getConfig("info_file"): @@ -92,45 +81,50 @@ 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. The handlers returned will be called when matching message is received in a client session.""" - return [ - ("normal", self.message), - ] + 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 @@ -174,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) @@ -203,9 +199,11 @@ class XMPPInterface(IRCInterface, JabberClient): stream.send(m) + def beforeReconnecting(self, ip): self.disconnect() + def afterReconnecting(self, ip): self.connect() @@ -218,26 +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), - ] + 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 4dee83ccb..6ca696f38 100644 --- a/module/plugins/hooks/ZeveraCom.py +++ b/module/plugins/hooks/ZeveraCom.py @@ -1,20 +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" - __config__ = [("activated", "bool", "Activated", False), - ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"), +class ZeveraCom(MultiHook): + __name__ = "ZeveraCom" + __type__ = "hook" + __version__ = "0.03" + + __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(",")] |