summaryrefslogtreecommitdiffstats
path: root/pyload/plugins/internal
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/plugins/internal')
-rw-r--r--pyload/plugins/internal/BasePlugin.py90
-rw-r--r--pyload/plugins/internal/DeadCrypter.py16
-rw-r--r--pyload/plugins/internal/DeadHoster.py24
-rw-r--r--pyload/plugins/internal/SimpleCrypter.py4
-rw-r--r--pyload/plugins/internal/SimpleHoster.py39
-rw-r--r--pyload/plugins/internal/XFSAccount.py51
-rw-r--r--pyload/plugins/internal/XFSHoster.py37
7 files changed, 157 insertions, 104 deletions
diff --git a/pyload/plugins/internal/BasePlugin.py b/pyload/plugins/internal/BasePlugin.py
index dd8540578..f4abc1a15 100644
--- a/pyload/plugins/internal/BasePlugin.py
+++ b/pyload/plugins/internal/BasePlugin.py
@@ -6,20 +6,26 @@ from urllib import unquote
from urlparse import urlparse
from pyload.network.HTTPRequest import BadHeader
+from pyload.plugins.internal.SimpleHoster import create_getInfo
from pyload.plugins.internal.Hoster import Hoster
-from pyload.utils import html_unescape, remove_chars
class BasePlugin(Hoster):
__name__ = "BasePlugin"
__type__ = "hoster"
- __version__ = "0.20"
+ __version__ = "0.23"
__pattern__ = r'^unmatchable$'
__description__ = """Base Plugin when any other didnt fit"""
__license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
+ __authors__ = [("RaNaN", "RaNaN@pyload.org"),
+ ("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ @classmethod
+ def getInfo(cls, url="", html=""): #@TODO: Move to hoster class in 0.4.10
+ return {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3, 'url': url or ""}
def setup(self):
@@ -30,40 +36,45 @@ class BasePlugin(Hoster):
def process(self, pyfile):
"""main function"""
- #: debug part, for api exerciser
- if pyfile.url.startswith("DEBUG_API"):
- self.multiDL = False
- return
+ pyfile.name = self.getInfo(pyfile.url)['name']
if pyfile.url.startswith("http"):
+ for _i in xrange(2):
+ try:
+ self.downloadFile(pyfile)
- try:
- self.downloadFile(pyfile)
- except BadHeader, e:
- if e.code in (401, 403):
- self.logDebug("Auth required")
+ except BadHeader, e:
+ if e.code is 404:
+ self.offline()
- account = self.core.accountManager.getAccountPlugin('Http')
- servers = [x['login'] for x in account.getAllAccounts()]
- server = urlparse(pyfile.url).netloc
+ elif e.code in (401, 403):
+ self.logDebug("Auth required")
- if server in servers:
- self.logDebug("Logging on to %s" % server)
- self.req.addAuth(account.accounts[server]['password'])
- else:
- for pwd in pyfile.package().password.splitlines():
- if ":" in pwd:
- self.req.addAuth(pwd.strip())
- break
- else:
- self.fail(_("Authorization required (username:password)"))
+ account = self.core.accountManager.getAccountPlugin('Http')
+ servers = [x['login'] for x in account.getAllAccounts()]
+ server = urlparse(pyfile.url).netloc
- self.downloadFile(pyfile)
+ if server in servers:
+ self.logDebug("Logging on to %s" % server)
+ self.req.addAuth(account.accounts[server]['password'])
+ else:
+ for pwd in pyfile.package().password.splitlines():
+ if ":" in pwd:
+ self.req.addAuth(pwd.strip())
+ break
+ else:
+ self.fail(_("Authorization required (username:password)"))
+ else:
+ self.fail(e)
else:
- raise
-
+ break
+ else:
+ self.fail(_("No file downloaded")) #@TODO: Move to hoster class (check if self.lastDownload) in 0.4.10
else:
- self.fail(_("No Plugin matched and not a downloadable url"))
+ self.fail(_("No plugin matched"))
+
+ # if self.checkDownload({'empty': re.compile(r"^$")}) is "empty":
+ # self.fail(_("Empty file"))
def downloadFile(self, pyfile):
@@ -78,31 +89,18 @@ class BasePlugin(Hoster):
if 'location' in header:
self.logDebug("Location: " + header['location'])
+
base = re.match(r'https?://[^/]+', url).group(0)
+
if header['location'].startswith("http"):
url = header['location']
+
elif header['location'].startswith("/"):
url = base + unquote(header['location'])
+
else:
url = '%s/%s' % (base, unquote(header['location']))
else:
break
- name = html_unescape(unquote(urlparse(url).path.split("/")[-1]))
-
- if 'content-disposition' in header:
- self.logDebug("Content-Disposition: " + header['content-disposition'])
- m = re.search("filename(?P<type>=|\*=(?P<enc>.+)'')(?P<name>.*)", header['content-disposition'])
- if m:
- disp = m.groupdict()
- self.logDebug(disp)
- if not disp['enc']:
- disp['enc'] = 'utf-8'
- name = remove_chars(disp['name'], "\"';").strip()
- name = unicode(unquote(name), disp['enc'])
-
- if not name:
- name = url
- pyfile.name = name
- self.logDebug("Filename: %s" % pyfile.name)
self.download(url, disposition=True)
diff --git a/pyload/plugins/internal/DeadCrypter.py b/pyload/plugins/internal/DeadCrypter.py
index bf150f3d5..3510e1466 100644
--- a/pyload/plugins/internal/DeadCrypter.py
+++ b/pyload/plugins/internal/DeadCrypter.py
@@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
+from urlparse import urlparse
+
from pyload.plugins.internal.Crypter import Crypter as _Crypter
+from pyload.plugins.internal.SimpleCrypter import create_getInfo
class DeadCrypter(_Crypter):
__name__ = "DeadCrypter"
__type__ = "crypter"
- __version__ = "0.02"
+ __version__ = "0.03"
__pattern__ = r'^unmatchable$'
@@ -15,5 +18,14 @@ class DeadCrypter(_Crypter):
__authors__ = [("stickell", "l.stickell@yahoo.it")]
+ @classmethod
+ def getInfo(cls, url="", html=""):
+ return {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url or ""}
+
+
def setup(self):
- self.offline("Crypter is no longer available")
+ self.pyfile.error = "Crypter is no longer available"
+ self.offline() #@TODO: self.offline("Crypter is no longer available")
+
+
+getInfo = create_getInfo(DeadCrypter)
diff --git a/pyload/plugins/internal/DeadHoster.py b/pyload/plugins/internal/DeadHoster.py
index 036ed3cb6..a7e5093d3 100644
--- a/pyload/plugins/internal/DeadHoster.py
+++ b/pyload/plugins/internal/DeadHoster.py
@@ -1,20 +1,15 @@
# -*- coding: utf-8 -*-
-from pyload.plugins.internal.Hoster import Hoster as _Hoster
-
-
-def create_getInfo(plugin):
+from urlparse import urlparse
- def getInfo(urls):
- yield map(lambda url: ('#N/A: ' + url, 0, 1, url), urls)
-
- return getInfo
+from pyload.plugins.internal.Hoster import Hoster as _Hoster
+from pyload.plugins.internal.SimpleHoster import create_getInfo
class DeadHoster(_Hoster):
__name__ = "DeadHoster"
__type__ = "hoster"
- __version__ = "0.12"
+ __version__ = "0.13"
__pattern__ = r'^unmatchable$'
@@ -23,5 +18,14 @@ class DeadHoster(_Hoster):
__authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
+ @classmethod
+ def getInfo(cls, url="", html=""):
+ return {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url or ""}
+
+
def setup(self):
- self.offline("Hoster is no longer available")
+ self.pyfile.error = "Hoster is no longer available"
+ self.offline() #@TODO: self.offline("Hoster is no longer available")
+
+
+getInfo = create_getInfo(DeadHoster)
diff --git a/pyload/plugins/internal/SimpleCrypter.py b/pyload/plugins/internal/SimpleCrypter.py
index ead5cefba..090cf2e9f 100644
--- a/pyload/plugins/internal/SimpleCrypter.py
+++ b/pyload/plugins/internal/SimpleCrypter.py
@@ -5,14 +5,14 @@ import re
from urlparse import urlparse
from pyload.plugins.internal.Crypter import Crypter
-from pyload.plugins.internal.SimpleHoster import SimpleHoster, replace_patterns, set_cookies
+from pyload.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns, set_cookies
from pyload.utils import fixup
class SimpleCrypter(Crypter, SimpleHoster):
__name__ = "SimpleCrypter"
__type__ = "crypter"
- __version__ = "0.31"
+ __version__ = "0.32"
__pattern__ = r'^unmatchable$'
__config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides core.config['general']['folder_per_package']
diff --git a/pyload/plugins/internal/SimpleHoster.py b/pyload/plugins/internal/SimpleHoster.py
index 4e9db7f73..a33e48bdf 100644
--- a/pyload/plugins/internal/SimpleHoster.py
+++ b/pyload/plugins/internal/SimpleHoster.py
@@ -37,12 +37,13 @@ def parseHtmlTagAttrValue(attr_name, tag):
return m.group(2) if m else None
-def parseHtmlForm(attr_str, html, input_names=None):
- for form in re.finditer(r"(?P<tag><form[^>]*%s[^>]*>)(?P<content>.*?)</?(form|body|html)[^>]*>" % attr_str,
+def parseHtmlForm(attr_str, html, input_names={}):
+ for form in re.finditer(r"(?P<TAG><form[^>]*%s[^>]*>)(?P<CONTENT>.*?)</?(form|body|html)[^>]*>" % attr_str,
html, re.S | re.I):
inputs = {}
- action = parseHtmlTagAttrValue("action", form.group('tag'))
- for inputtag in re.finditer(r'(<(input|textarea)[^>]*>)([^<]*(?=</\2)|)', form.group('content'), re.S | re.I):
+ action = parseHtmlTagAttrValue("action", form.group('TAG'))
+
+ for inputtag in re.finditer(r'(<(input|textarea)[^>]*>)([^<]*(?=</\2)|)', form.group('CONTENT'), re.S | re.I):
name = parseHtmlTagAttrValue("name", inputtag.group(1))
if name:
value = parseHtmlTagAttrValue("value", inputtag.group(1))
@@ -51,7 +52,7 @@ def parseHtmlForm(attr_str, html, input_names=None):
else:
inputs[name] = value
- if isinstance(input_names, dict):
+ if input_names:
# check input attributes
for key, val in input_names.iteritems():
if key in inputs:
@@ -80,6 +81,7 @@ def parseFileInfo(plugin, url="", html=""):
#@TODO: Remove in 0.4.10
+#@NOTE: Every plugin must have own parseInfo classmethod to work with 0.4.10
def create_getInfo(plugin):
return lambda urls: [(info['name'], info['size'], info['status'], info['url']) for info in plugin.parseInfo(urls)]
@@ -90,23 +92,21 @@ def timestamp():
#@TODO: Move to hoster class in 0.4.10
def _getDirectLink(self, url):
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
+ header = self.load(url, ref=True, just_header=True, decode=True)
- html = self.load(url, ref=True, decode=True)
+ if not 'location' in header or not header['location']:
+ return ""
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
+ if header['code'] != 302 or 'content-type' not in header or header['content-type'] != "text/plain":
+ return ""
- if self.getInfo(url, html)['status'] is not 2:
- try:
- return re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I).group(1).rstrip() #@TODO: Remove .rstrip() in 0.4.10
- except:
- pass
+ return header['location']
class SimpleHoster(Hoster):
__name__ = "SimpleHoster"
__type__ = "hoster"
- __version__ = "0.60"
+ __version__ = "0.62"
__pattern__ = r'^unmatchable$'
@@ -177,7 +177,7 @@ class SimpleHoster(Hoster):
@classmethod
def getInfo(cls, url="", html=""):
- info = {'name': url or _("Unknown"), 'size': 0, 'status': 3, 'url': url}
+ info = {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3, 'url': url or ""}
if not html:
if url:
@@ -391,14 +391,15 @@ class SimpleHoster(Hoster):
def handleDirect(self):
- self.link = _getDirectLink(self, self.pyfile.url)
+ link = _getDirectLink(self, self.pyfile.url)
- if self.link:
+ if link:
self.logInfo(_("Direct download link detected"))
+ self.link = link
+
self._updateInfo(self.getInfo(self.pyfile.url))
self.checkNameSize()
-
else:
self.logDebug(_("Direct download link not found"))
@@ -452,7 +453,7 @@ class SimpleHoster(Hoster):
self.retry(max_tries=max_tries, reason=_("Download limit reached"))
- def parseHtmlForm(self, attr_str='', input_names=None):
+ def parseHtmlForm(self, attr_str="", input_names={}):
return parseHtmlForm(attr_str, self.html, input_names)
diff --git a/pyload/plugins/internal/XFSAccount.py b/pyload/plugins/internal/XFSAccount.py
index 1e18c09bd..168c4f903 100644
--- a/pyload/plugins/internal/XFSAccount.py
+++ b/pyload/plugins/internal/XFSAccount.py
@@ -12,7 +12,7 @@ from pyload.plugins.internal.SimpleHoster import parseHtmlForm, set_cookies
class XFSAccount(Account):
__name__ = "XFSAccount"
__type__ = "account"
- __version__ = "0.26"
+ __version__ = "0.30"
__description__ = """XFileSharing account plugin"""
__license__ = "GPLv3"
@@ -32,6 +32,9 @@ class XFSAccount(Account):
TRAFFIC_LEFT_PATTERN = r'>Traffic available today:.*?<b>\s*(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>'
TRAFFIC_LEFT_UNIT = "MB" #: used only if no group <U> was found
+ LEECH_TRAFFIC_PATTERN = r'Leech Traffic left:<b>.*?(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>'
+ LEECH_TRAFFIC_UNIT = "MB" #: used only if no group <U> was found
+
LOGIN_FAIL_PATTERN = r'>(Incorrect Login or Password|Error<)'
@@ -44,9 +47,10 @@ class XFSAccount(Account):
def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = None
- premium = None
+ validuntil = None
+ trafficleft = None
+ leechtraffic = None
+ premium = None
html = req.load(self.HOSTER_URL, get={'op': "my_account"}, decode=True)
@@ -64,17 +68,22 @@ class XFSAccount(Account):
self.logError(e)
else:
+ self.logDebug("Valid until: %s" % validuntil)
+
if validuntil > mktime(gmtime()):
premium = True
+ trafficleft = -1
else:
premium = False
validuntil = None #: registered account type (not premium)
+ else:
+ self.logDebug("VALID_UNTIL_PATTERN not found")
m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
if m:
try:
traffic = m.groupdict()
- size = traffic['S']
+ size = traffic['S']
if "nlimited" in size:
trafficleft = -1
@@ -93,10 +102,36 @@ class XFSAccount(Account):
except Exception, e:
self.logError(e)
else:
- if premium:
- trafficleft = -1
+ self.logDebug("TRAFFIC_LEFT_PATTERN not found")
+
+ m = re.finditer(self.LEECH_TRAFFIC_PATTERN, html)
+ if m:
+ leechtraffic = 0
+ try:
+ for leech in m:
+ size = leech['S']
+
+ if "nlimited" in size:
+ leechtraffic = -1
+ if validuntil is None:
+ validuntil = -1
+ break
+ else:
+ if 'U' in leech:
+ unit = leech['U']
+ elif isinstance(self.LEECH_TRAFFIC_UNIT, basestring):
+ unit = self.LEECH_TRAFFIC_UNIT
+ else:
+ unit = ""
+
+ leechtraffic += self.parseTraffic(size + unit)
+
+ except Exception, e:
+ self.logError(e)
+ else:
+ self.logDebug("LEECH_TRAFFIC_PATTERN not found")
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
+ return {'validuntil': validuntil, 'trafficleft': trafficleft, 'leechtraffic': leechtraffic, 'premium': premium}
def login(self, user, data, req):
diff --git a/pyload/plugins/internal/XFSHoster.py b/pyload/plugins/internal/XFSHoster.py
index 3ae9ee05a..4d3c848b9 100644
--- a/pyload/plugins/internal/XFSHoster.py
+++ b/pyload/plugins/internal/XFSHoster.py
@@ -16,7 +16,7 @@ from pyload.utils import html_unescape
class XFSHoster(SimpleHoster):
__name__ = "XFSHoster"
__type__ = "hoster"
- __version__ = "0.22"
+ __version__ = "0.26"
__pattern__ = r'^unmatchable$'
@@ -30,15 +30,13 @@ class XFSHoster(SimpleHoster):
HOSTER_DOMAIN = None
HOSTER_NAME = None
- URL_REPLACEMENTS = [(r'/(?:embed-)?(\w{12}).*', r'/\1')] #: plus support embedded files
-
TEXT_ENCODING = False
COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
CHECK_DIRECT_LINK = None
- MULTI_HOSTER = False
+ MULTI_HOSTER = True #@NOTE: Should be default to False for safe, but I'm lazy...
INFO_PATTERN = r'<tr><td align=right><b>Filename:</b></td><td nowrap>(?P<N>[^<]+)</td></tr>\s*.*?<small>\((?P<S>[^<]+)\)</small>'
- NAME_PATTERN = r'(>Filename:</b></td><td nowrap>|name="fname" value="|<span class="name">|<[Tt]itle>.*?Download )(?P<N>.+?)(\s*<|")'
+ NAME_PATTERN = r'(>Filename:</b></td><td nowrap>|name="fname" value="|<span class="name">)(?P<N>.+?)(\s*<|")'
SIZE_PATTERN = r'(>Size:</b></td><td>|>File:.*>|<span class="size">)(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)'
OFFLINE_PATTERN = r'>\s*\w+ (Not Found|file (was|has been) removed)'
@@ -48,14 +46,17 @@ class XFSHoster(SimpleHoster):
PREMIUM_ONLY_PATTERN = r'>This file is available for Premium Users only'
ERROR_PATTERN = r'(?:class=["\']err["\'].*?>|<[Cc]enter><b>|>Error</td>|>\(ERROR:)(?:\s*<.+?>\s*)*(.+?)(?:["\']|<|\))'
- OVR_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)'
- LINK_PATTERN = None #: final download url pattern
+ LEECH_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)'
+ LINK_PATTERN = None #: final download url pattern
CAPTCHA_PATTERN = r'(https?://[^"\']+?/captchas?/[^"\']+)'
CAPTCHA_DIV_PATTERN = r'>Enter code.*?<div.*?>(.+?)</div>'
RECAPTCHA_PATTERN = None
SOLVEMEDIA_PATTERN = None
+ FORM_PATTERN = None
+ FORM_INPUTS_MAP = None #: dict passed as input_names to parseHtmlForm
+
def setup(self):
self.chunkLimit = 1
@@ -108,7 +109,7 @@ class XFSHoster(SimpleHoster):
def getDownloadLink(self):
- for i in xrange(1, 5):
+ for i in xrange(1, 6):
self.logDebug("Getting download link: #%d" % i)
self.checkErrors()
@@ -145,7 +146,7 @@ class XFSHoster(SimpleHoster):
#only tested with easybytez.com
self.html = self.load("http://www.%s/" % self.HOSTER_DOMAIN)
- action, inputs = self.parseHtmlForm('')
+ action, inputs = self.parseHtmlForm()
upload_id = "%012d" % int(random() * 10 ** 12)
action += upload_id + "&js_on=1&utype=prem&upload_type=url"
@@ -186,9 +187,9 @@ class XFSHoster(SimpleHoster):
self.fail(stmsg)
#get easybytez.com link for uploaded file
- m = re.search(self.OVR_LINK_PATTERN, self.html)
+ m = re.search(self.LEECH_LINK_PATTERN, self.html)
if m is None:
- self.error(_("OVR_LINK_PATTERN not found"))
+ self.error(_("LEECH_LINK_PATTERN not found"))
header = self.load(m.group(1), just_header=True, decode=True)
@@ -259,10 +260,10 @@ class XFSHoster(SimpleHoster):
def getPostParameters(self):
- if hasattr(self, "FORM_PATTERN"):
- action, inputs = self.parseHtmlForm(self.FORM_PATTERN)
+ if self.FORM_PATTERN or self.FORM_INPUTS_MAP:
+ action, inputs = self.parseHtmlForm(self.FORM_PATTERN or "", self.FORM_INPUTS_MAP or {})
else:
- action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")})
+ action, inputs = self.parseHtmlForm(input_names={'op': re.compile(r'^download')})
if not inputs:
action, inputs = self.parseHtmlForm('F1')
@@ -313,8 +314,8 @@ class XFSHoster(SimpleHoster):
m = re.search(self.CAPTCHA_DIV_PATTERN, self.html, re.S)
if m:
captcha_div = m.group(1)
+ numerals = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div))
self.logDebug(captcha_div)
- numerals = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div))
inputs['code'] = "".join([a[1] for a in sorted(numerals, key=lambda num: int(num[0]))])
self.logDebug("Captcha code: %s" % inputs['code'], numerals)
return 2
@@ -324,9 +325,10 @@ class XFSHoster(SimpleHoster):
captcha_key = re.search(self.RECAPTCHA_PATTERN, self.html).group(1)
except:
captcha_key = recaptcha.detect_key()
+ else:
+ self.logDebug("ReCaptcha key: %s" % captcha_key)
if captcha_key:
- self.logDebug("ReCaptcha key: %s" % captcha_key)
inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge(captcha_key)
return 3
@@ -335,9 +337,10 @@ class XFSHoster(SimpleHoster):
captcha_key = re.search(self.SOLVEMEDIA_PATTERN, self.html).group(1)
except:
captcha_key = solvemedia.detect_key()
+ else:
+ self.logDebug("SolveMedia key: %s" % captcha_key)
if captcha_key:
- self.logDebug("SolveMedia key: %s" % captcha_key)
inputs['adcopy_challenge'], inputs['adcopy_response'] = solvemedia.challenge(captcha_key)
return 4