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