diff options
Diffstat (limited to 'pyload/plugins/internal')
-rw-r--r-- | pyload/plugins/internal/BasePlugin.py | 98 | ||||
-rw-r--r-- | pyload/plugins/internal/DeadCrypter.py | 5 | ||||
-rw-r--r-- | pyload/plugins/internal/DeadHoster.py | 5 | ||||
-rw-r--r-- | pyload/plugins/internal/MultiHoster.py | 44 | ||||
-rw-r--r-- | pyload/plugins/internal/SimpleHoster.py | 146 | ||||
-rw-r--r-- | pyload/plugins/internal/UnRar.py | 14 | ||||
-rw-r--r-- | pyload/plugins/internal/XFSAccount.py | 20 | ||||
-rw-r--r-- | pyload/plugins/internal/XFSHoster.py | 22 |
8 files changed, 207 insertions, 147 deletions
diff --git a/pyload/plugins/internal/BasePlugin.py b/pyload/plugins/internal/BasePlugin.py index f4abc1a15..a9d81d079 100644 --- a/pyload/plugins/internal/BasePlugin.py +++ b/pyload/plugins/internal/BasePlugin.py @@ -3,7 +3,7 @@ import re from urllib import unquote -from urlparse import urlparse +from urlparse import urljoin, urlparse from pyload.network.HTTPRequest import BadHeader from pyload.plugins.internal.SimpleHoster import create_getInfo @@ -13,7 +13,7 @@ from pyload.plugins.internal.Hoster import Hoster class BasePlugin(Hoster): __name__ = "BasePlugin" __type__ = "hoster" - __version__ = "0.23" + __version__ = "0.25" __pattern__ = r'^unmatchable$' @@ -25,7 +25,7 @@ class BasePlugin(Hoster): @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 ""} + return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3 if url else 1, 'url': unquote(url) or ""} def setup(self): @@ -38,69 +38,69 @@ class BasePlugin(Hoster): pyfile.name = self.getInfo(pyfile.url)['name'] - if pyfile.url.startswith("http"): - for _i in xrange(2): - try: - self.downloadFile(pyfile) + if not pyfile.url.startswith("http"): + self.fail(_("No plugin matched")) + + for _i in xrange(5): + try: + self.downloadFile(pyfile) - except BadHeader, e: - if e.code is 404: - self.offline() + except BadHeader, e: + if e.code is 404: + self.offline() - elif e.code in (401, 403): - self.logDebug("Auth required") + elif e.code in (401, 403): + self.logDebug("Auth required", "Received HTTP status code: %d" % e.code) - account = self.core.accountManager.getAccountPlugin('Http') - servers = [x['login'] for x in account.getAllAccounts()] - server = urlparse(pyfile.url).netloc + account = self.core.accountManager.getAccountPlugin('Http') + servers = [x['login'] for x in account.getAllAccounts()] + server = urlparse(pyfile.url).netloc - 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)")) + if server in servers: + self.logDebug("Logging on to %s" % server) + self.req.addAuth(account.accounts[server]['password']) else: - self.fail(e) + for pwd in self.getPassword().splitlines(): + if ":" in pwd: + self.req.addAuth(pwd.strip()) + break + else: + self.fail(_("Authorization required")) else: - break + self.fail(e) else: - self.fail(_("No file downloaded")) #@TODO: Move to hoster class (check if self.lastDownload) in 0.4.10 + break else: - self.fail(_("No plugin matched")) + self.fail(_("No file downloaded")) #@TODO: Move to hoster class in 0.4.10 - # if self.checkDownload({'empty': re.compile(r"^$")}) is "empty": - # self.fail(_("Empty file")) + if self.checkDownload({'empty': re.compile(r"^$")}) is "empty": #@TODO: Move to hoster in 0.4.10 + self.fail(_("Empty file")) def downloadFile(self, pyfile): url = pyfile.url - for _i in xrange(5): - header = self.load(url, just_header=True) - - # self.load does not raise a BadHeader on 404 responses, do it here - if 'code' in header and header['code'] == 404: - raise BadHeader(404) + for i in xrange(1, 7): #@TODO: retrieve the pycurl.MAXREDIRS value set by req + header = self.load(url, ref=True, cookies=True, just_header=True, decode=True) - if 'location' in header: - self.logDebug("Location: " + header['location']) - - base = re.match(r'https?://[^/]+', url).group(0) + if 'location' not in header or not header['location']: + if 'code' in header and header['code'] not in (200, 201, 203, 206): + self.logDebug("Received HTTP status code: %d" % header['code']) + self.fail(_("File not found")) + else: + break - if header['location'].startswith("http"): - url = header['location'] + location = header['location'] - elif header['location'].startswith("/"): - url = base + unquote(header['location']) + self.logDebug("Redirect #%d to: %s" % (i, location)) - else: - url = '%s/%s' % (base, unquote(header['location'])) + if urlparse(location).scheme: + url = location else: - break + p = urlparse(url) + base = "%s://%s" % (p.scheme, p.netloc) + url = urljoin(base, location) + else: + self.fail(_("Too many redirects")) - self.download(url, disposition=True) + self.download(unquote(url), disposition=True) diff --git a/pyload/plugins/internal/DeadCrypter.py b/pyload/plugins/internal/DeadCrypter.py index 3510e1466..81b68e00a 100644 --- a/pyload/plugins/internal/DeadCrypter.py +++ b/pyload/plugins/internal/DeadCrypter.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from urllib import unquote from urlparse import urlparse from pyload.plugins.internal.Crypter import Crypter as _Crypter @@ -9,7 +10,7 @@ from pyload.plugins.internal.SimpleCrypter import create_getInfo class DeadCrypter(_Crypter): __name__ = "DeadCrypter" __type__ = "crypter" - __version__ = "0.03" + __version__ = "0.04" __pattern__ = r'^unmatchable$' @@ -20,7 +21,7 @@ class DeadCrypter(_Crypter): @classmethod def getInfo(cls, url="", html=""): - return {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url or ""} + return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url} def setup(self): diff --git a/pyload/plugins/internal/DeadHoster.py b/pyload/plugins/internal/DeadHoster.py index a7e5093d3..f066883c6 100644 --- a/pyload/plugins/internal/DeadHoster.py +++ b/pyload/plugins/internal/DeadHoster.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from urllib import unquote from urlparse import urlparse from pyload.plugins.internal.Hoster import Hoster as _Hoster @@ -9,7 +10,7 @@ from pyload.plugins.internal.SimpleHoster import create_getInfo class DeadHoster(_Hoster): __name__ = "DeadHoster" __type__ = "hoster" - __version__ = "0.13" + __version__ = "0.14" __pattern__ = r'^unmatchable$' @@ -20,7 +21,7 @@ class DeadHoster(_Hoster): @classmethod def getInfo(cls, url="", html=""): - return {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url or ""} + return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url} def setup(self): diff --git a/pyload/plugins/internal/MultiHoster.py b/pyload/plugins/internal/MultiHoster.py index 4eb4a6f31..4d466d350 100644 --- a/pyload/plugins/internal/MultiHoster.py +++ b/pyload/plugins/internal/MultiHoster.py @@ -16,18 +16,21 @@ class MultiHoster(Addon): __authors__ = [("pyLoad Team", "admin@pyload.org")] - interval = 24 * 60 * 60 #: reload hosters daily - - HOSTER_REPLACEMENTS = [("2shared.com", "twoshared.com"), ("4shared.com", "fourshared.com"), ("cloudnator.com", "shragle.com"), - ("ifile.it", "filecloud.io"), ("easy-share.com", "crocko.com"), ("freakshare.net", "freakshare.com"), - ("hellshare.com", "hellshare.cz"), ("share-rapid.cz", "sharerapid.com"), ("sharerapid.cz", "sharerapid.com"), - ("ul.to", "uploaded.to"), ("uploaded.net", "uploaded.to"), ("1fichier.com", "onefichier.com")] + interval = 12 * 60 * 60 #: reload hosters every 12h + + HOSTER_REPLACEMENTS = [("1fichier.com", "onefichier.com"), ("2shared.com", "twoshared.com"), + ("4shared.com", "fourshared.com"), ("cloudnator.com", "shragle.com"), + ("easy-share.com", "crocko.com"), ("freakshare.net", "freakshare.com"), + ("hellshare.com", "hellshare.cz"), ("ifile.it", "filecloud.io"), + ("putlocker.com", "firedrive.com"), ("share-rapid.cz", "multishare.cz"), + ("sharerapid.cz", "multishare.cz"), ("ul.to", "uploaded.to"), + ("uploaded.net", "uploaded.to")] HOSTER_EXCLUDED = [] def setup(self): - self.hosters = [] - self.supported = [] + self.hosters = [] + self.supported = [] self.new_supported = [] @@ -110,8 +113,10 @@ class MultiHoster(Addon): """reload hoster list periodically""" self.logInfo(_("Reloading supported hoster list")) - old_supported = self.supported - self.supported, self.new_supported, self.hosters = [], [], [] + old_supported = self.supported + self.supported = [] + self.new_supported = [] + self.hosters = [] self.overridePlugins() @@ -123,11 +128,8 @@ class MultiHoster(Addon): def overridePlugins(self): - pluginMap = {} - for name in self.core.pluginManager.hosterPlugins.keys(): - pluginMap[name.lower()] = name - - accountList = [name.lower() for name, data in self.core.accountManager.accounts.iteritems() if data] + pluginMap = dict((name.lower(), name) for name in self.core.pluginManager.hosterPlugins.keys()) + accountList = [name.lower() for name, data in self.core.accountManager.accounts.iteritems() if data] excludedList = [] for hoster in self.getHosterCached(): @@ -146,14 +148,14 @@ class MultiHoster(Addon): return module = self.core.pluginManager.getPlugin(self.__type__, self.__name__) - klass = getattr(module, self.__name__) + klass = getattr(module, self.__name__) # inject plugin plugin self.logDebug("Overwritten Hosters", ", ".join(sorted(self.supported))) for hoster in self.supported: dict = self.core.pluginManager.hosterPlugins[hoster] dict['new_module'] = module - dict['new_name'] = self.__name__ + dict['new_name'] = self.__name__ if excludedList: self.logInfo(_("The following hosters were not overwritten - account exists"), ", ".join(sorted(excludedList))) @@ -162,7 +164,7 @@ class MultiHoster(Addon): self.logDebug("New Hosters", ", ".join(sorted(self.new_supported))) # create new regexp - regexp = r'.*(%s).*' % "|".join([x.replace(".", "\\.") for x in self.new_supported]) + regexp = r'.*(%s).*' % "|".join([x.replace(".", "\.") for x in self.new_supported]) if hasattr(klass, "__pattern__") and isinstance(klass.__pattern__, basestring) and '://' in klass.__pattern__: regexp = r'%s|%s' % (klass.__pattern__, regexp) @@ -170,7 +172,7 @@ class MultiHoster(Addon): dict = self.core.pluginManager.hosterPlugins[self.__name__] dict['pattern'] = regexp - dict['re'] = re.compile(regexp) + dict['re'] = re.compile(regexp) def unloadHoster(self, hoster): @@ -190,9 +192,9 @@ class MultiHoster(Addon): # reset pattern klass = getattr(self.core.pluginManager.getPlugin(self.__type__, self.__name__), self.__name__) - dict = self.core.pluginManager.hosterPlugins[self.__name__] + dict = self.core.pluginManager.hosterPlugins[self.__name__] dict['pattern'] = getattr(klass, "__pattern__", r'^unmatchable$') - dict['re'] = re.compile(dict['pattern']) + dict['re'] = re.compile(dict['pattern']) def downloadFailed(self, pyfile): diff --git a/pyload/plugins/internal/SimpleHoster.py b/pyload/plugins/internal/SimpleHoster.py index 922361b30..ce1ddd2b6 100644 --- a/pyload/plugins/internal/SimpleHoster.py +++ b/pyload/plugins/internal/SimpleHoster.py @@ -3,9 +3,8 @@ import re from time import time -from urlparse import urlparse - -from pycurl import FOLLOWLOCATION +from urllib import unquote +from urlparse import urljoin, urlparse from pyload.datatype.PyFile import statusMap as _statusMap from pyload.network.CookieJar import CookieJar @@ -91,22 +90,37 @@ def timestamp(): #@TODO: Move to hoster class in 0.4.10 -def _getDirectLink(self, url): +def _isDirectLink(self, url, resumable=True): header = self.load(url, ref=True, just_header=True, decode=True) if not 'location' in header or not header['location']: return "" - if header['code'] != 302 or 'content-type' not in header or header['content-type'] != "text/plain": - return "" + location = header['location'] + + resumable = False #@NOTE: Testing... + + if resumable: #: sometimes http code may be wrong... + if 'location' in self.load(location, ref=True, cookies=True, just_header=True, decode=True): + return "" + else: + if not 'code' in header or header['code'] != 302: + return "" + + if urlparse(location).scheme: + link = location + else: + p = urlparse(url) + base = "%s://%s" % (p.scheme, p.netloc) + link = urljoin(base, location) - return header['location'] + return link class SimpleHoster(Hoster): __name__ = "SimpleHoster" __type__ = "hoster" - __version__ = "0.62" + __version__ = "0.71" __pattern__ = r'^unmatchable$' @@ -128,6 +142,9 @@ class SimpleHoster(Hoster): SIZE_PATTERN: (optional) Size that will be checked for the file example: SIZE_PATTERN = r'(?P<S>file_size) (?P<U>size_unit)' + HASHSUM_PATTERN: (optional) Hash code and type of the file + example: HASHSUM_PATTERN = r'(?P<H>hash_code) (?P<T>MD5)' + OFFLINE_PATTERN: (optional) Check if the file is yet available online example: OFFLINE_PATTERN = r'File (deleted|not found)' @@ -163,9 +180,9 @@ class SimpleHoster(Hoster): TEXT_ENCODING = False #: Set to True or encoding name if encoding value in http header is not correct COOKIES = True #: or False or list of tuples [(domain, name, value)] FORCE_CHECK_TRAFFIC = False #: Set to True to force checking traffic left for premium account - CHECK_DIRECT_LINK = None #: when None self-set to True if self.account else False - MULTI_HOSTER = False #: Set to True to leech other hoster link - CONTENT_DISPOSITION = False #: Set to True to replace file name with content-disposition value in http header + CHECK_DIRECT_LINK = None #: Set to True to check for direct link, set to None to do it only if self.account is True + MULTI_HOSTER = False #: Set to True to leech other hoster link (according its multihoster hook if available) + CONTENT_DISPOSITION = False #: Set to True to replace file name with content-disposition value from http header @classmethod @@ -177,14 +194,32 @@ class SimpleHoster(Hoster): @classmethod def getInfo(cls, url="", html=""): - info = {'name': urlparse(url).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3, 'url': url or ""} + info = {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3, 'url': url} if not html: - if url: - html = getURL(url, cookies=cls.COOKIES, decode=not cls.TEXT_ENCODING) - if isinstance(cls.TEXT_ENCODING, basestring): - html = unicode(html, cls.TEXT_ENCODING) - else: + try: + if not url: + info['error'] = "missing url" + info['status'] = 1 + raise + + try: + html = getURL(url, cookies=cls.COOKIES, decode=not cls.TEXT_ENCODING) + + if isinstance(cls.TEXT_ENCODING, basestring): + html = unicode(html, cls.TEXT_ENCODING) + + except BadHeader, e: + info['error'] = "%d: %s" % (e.code, e.content) + + if e.code is 404: + info['status'] = 1 + raise + + if e.code is 503: + info['status'] = 6 + raise + except: return info online = False @@ -197,33 +232,43 @@ class SimpleHoster(Hoster): else: try: - info.update(re.match(cls.__pattern__, url).groupdict()) + info['pattern'] = re.match(cls.__pattern__, url).groupdict() #: pattern groups will be saved here, please save api stuff to info['api'] except Exception: pass - for pattern in ("INFO_PATTERN", "NAME_PATTERN", "SIZE_PATTERN"): + for pattern in ("INFO_PATTERN", "NAME_PATTERN", "SIZE_PATTERN", "HASHSUM_PATTERN"): try: attr = getattr(cls, pattern) - info.update(re.search(attr, html).groupdict()) + dict = re.search(attr, html).groupdict() + + if all(True for k in dict if k not in info['pattern']): + info['pattern'].update(dict) + except AttributeError: continue + else: online = True if online: info['status'] = 2 - if 'N' in info: - info['name'] = replace_patterns(info['N'].strip(), cls.NAME_REPLACEMENTS) + if 'N' in info['pattern']: + info['name'] = replace_patterns(unquote(info['pattern']['N'].strip()), cls.NAME_REPLACEMENTS) - if 'S' in info: - size = replace_patterns(info['S'] + info['U'] if 'U' in info else info['S'], cls.SIZE_REPLACEMENTS) + if 'S' in info['pattern']: + size = replace_patterns(info['pattern']['S'] + info['pattern']['U'] if 'U' in info else info['pattern']['S'], + cls.SIZE_REPLACEMENTS) info['size'] = parseFileSize(size) elif isinstance(info['size'], basestring): unit = info['units'] if 'units' in info else None info['size'] = parseFileSize(info['size'], unit) + if 'H' in info['pattern']: + hashtype = info['pattern']['T'] if 'T' in info['pattern'] else "hash" + info[hashtype] = info['pattern']['H'] + return info @@ -243,8 +288,8 @@ class SimpleHoster(Hoster): set_cookies(self.req.cj, self.COOKIES) if (self.MULTI_HOSTER - and self.__pattern__ != self.core.pluginManager.hosterPlugins[self.__name__]['pattern'] - and re.match(self.__pattern__, self.pyfile.url) is None): + and (self.__pattern__ != self.core.pluginManager.hosterPlugins[self.__name__]['pattern'] + or re.match(self.__pattern__, self.pyfile.url) is None)): self.logInfo("Multi hoster detected") @@ -288,8 +333,7 @@ class SimpleHoster(Hoster): premium_only = 'error' in self.info and self.info['error'] == "premium-only" - info = self.getInfo(pyfile.url, self.html) - self._updateInfo(info) + self._updateInfo(self.getInfo(pyfile.url, self.html)) self.checkNameSize() @@ -308,18 +352,28 @@ class SimpleHoster(Hoster): self.logDebug("Handled as free download") self.handleFree() - if self.link: - self.download(self.link, disposition=self.CONTENT_DISPOSITION) + self.downloadLink(self.link) + self.checkFile() + + + def downloadLink(self, link): + if not link: + return + + self.download(link, disposition=self.CONTENT_DISPOSITION) + + + def checkFile(self): + if self.checkDownload({'empty': re.compile(r"^$")}) is "empty": #@TODO: Move to hoster in 0.4.10 + self.fail(_("Empty file")) def checkErrors(self): - if hasattr(self, 'WAIT_PATTERN'): - m = re.search(self.WAIT_PATTERN, self.html) + if hasattr(self, 'ERROR_PATTERN'): + m = re.search(self.ERROR_PATTERN, self.html) if m: - wait_time = sum([int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1}[u.lower()] for v, u in - re.findall(r'(\d+)\s*(hr|hour|min|sec)', m, re.I)]) - self.wait(wait_time, False) - return + errmsg = self.info['error'] = m.group(1) + self.error(errmsg) if hasattr(self, 'PREMIUM_ONLY_PATTERN'): m = re.search(self.PREMIUM_ONLY_PATTERN, self.html) @@ -327,11 +381,13 @@ class SimpleHoster(Hoster): self.info['error'] = "premium-only" return - if hasattr(self, 'ERROR_PATTERN'): - m = re.search(self.ERROR_PATTERN, self.html) + if hasattr(self, 'WAIT_PATTERN'): + m = re.search(self.WAIT_PATTERN, self.html) if m: - e = self.info['error'] = m.group(1) - self.error(e) + wait_time = sum([int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1}[u.lower()] for v, u in + re.findall(r'(\d+)\s*(hr|hour|min|sec)', m, re.I)]) + self.wait(wait_time, False) + return self.info.pop('error', None) @@ -381,7 +437,9 @@ class SimpleHoster(Hoster): #: Deprecated def getFileInfo(self): - return self.checkInfo() + self.info = {} + self.checkInfo() + return self.info def _updateInfo(self, info): @@ -391,7 +449,7 @@ class SimpleHoster(Hoster): def handleDirect(self): - link = _getDirectLink(self, self.pyfile.url) + link = _isDirectLink(self, self.pyfile.url, self.resumeDownload) if link: self.logInfo(_("Direct download link detected")) @@ -420,7 +478,7 @@ class SimpleHoster(Hoster): self.link = m.group(1) except Exception, e: - self.fail(str(e)) + self.fail(e) def handlePremium(self): @@ -435,7 +493,7 @@ class SimpleHoster(Hoster): self.link = m.group(1) except Exception, e: - self.fail(str(e)) + self.fail(e) def longWait(self, wait_time=None, max_tries=3): diff --git a/pyload/plugins/internal/UnRar.py b/pyload/plugins/internal/UnRar.py index ebfe53829..90216222b 100644 --- a/pyload/plugins/internal/UnRar.py +++ b/pyload/plugins/internal/UnRar.py @@ -22,7 +22,7 @@ def renice(pid, value): class UnRar(AbtractExtractor): __name__ = "UnRar" - __version__ = "0.18" + __version__ = "0.19" __description__ = """Rar extractor plugin""" __license__ = "GPLv3" @@ -32,12 +32,12 @@ class UnRar(AbtractExtractor): CMD = "unrar" # there are some more uncovered rar formats - re_version = re.compile(r"(UNRAR 5[\d.]+(.*?)freeware)") - re_splitfile = re.compile(r"(.*)\.part(\d+)\.rar$", re.I) - re_partfiles = re.compile(r".*\.(rar|r\d+)", re.I) - re_filelist = re.compile(r"(.+)\s+(\d+)\s+(\d+)\s+") - re_filelist5 = re.compile(r"(.+)\s+(\d+)\s+\d\d-\d\d-\d\d\s+\d\d:\d\d\s+(.+)") - re_wrongpwd = re.compile("(Corrupt file or wrong password|password incorrect)", re.I) + re_version = re.compile(r'UNRAR ([\w .]+?) freeware') + re_splitfile = re.compile(r'(.*)\.part(\d+)\.rar$', re.I) + re_partfiles = re.compile(r'.*\.(rar|r\d+)', re.I) + re_filelist = re.compile(r'(.+)\s+(\d+)\s+(\d+)\s+') + re_filelist5 = re.compile(r'(.+)\s+(\d+)\s+\d\d-\d\d-\d\d\s+\d\d:\d\d\s+(.+)') + re_wrongpwd = re.compile(r'(Corrupt file or wrong password|password incorrect)', re.I) @staticmethod diff --git a/pyload/plugins/internal/XFSAccount.py b/pyload/plugins/internal/XFSAccount.py index 168c4f903..a330d2ff4 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.30" + __version__ = "0.32" __description__ = """XFileSharing account plugin""" __license__ = "GPLv3" @@ -27,15 +27,15 @@ class XFSAccount(Account): PREMIUM_PATTERN = r'\(Premium only\)' - VALID_UNTIL_PATTERN = r'>Premium.[Aa]ccount expire:.*?(\d{1,2} [\w^_]+ \d{4})' + VALID_UNTIL_PATTERN = r'Premium.[Aa]ccount expire:.*?(\d{1,2} [\w^_]+ \d{4})' - TRAFFIC_LEFT_PATTERN = r'>Traffic available today:.*?<b>\s*(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>' + 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<)' + LOGIN_FAIL_PATTERN = r'>\s*(Incorrect Login or Password|Error<)' def init(self): @@ -104,12 +104,12 @@ class XFSAccount(Account): else: self.logDebug("TRAFFIC_LEFT_PATTERN not found") - m = re.finditer(self.LEECH_TRAFFIC_PATTERN, html) - if m: + leech = [m.groupdict() for m in re.finditer(self.LEECH_TRAFFIC_PATTERN, html)] + if leech: leechtraffic = 0 try: - for leech in m: - size = leech['S'] + for traffic in leech: + size = traffic['S'] if "nlimited" in size: leechtraffic = -1 @@ -117,8 +117,8 @@ class XFSAccount(Account): validuntil = -1 break else: - if 'U' in leech: - unit = leech['U'] + if 'U' in traffic: + unit = traffic['U'] elif isinstance(self.LEECH_TRAFFIC_UNIT, basestring): unit = self.LEECH_TRAFFIC_UNIT else: diff --git a/pyload/plugins/internal/XFSHoster.py b/pyload/plugins/internal/XFSHoster.py index 061012059..a4e7339c5 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.26" + __version__ = "0.27" __pattern__ = r'^unmatchable$' @@ -35,7 +35,6 @@ class XFSHoster(SimpleHoster): CHECK_DIRECT_LINK = None 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">)(?P<N>.+?)(\s*<|")' SIZE_PATTERN = r'(>Size:</b></td><td>|>File:.*>|<span class="size">)(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)' @@ -49,10 +48,10 @@ class XFSHoster(SimpleHoster): 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 + CAPTCHA_PATTERN = r'(https?://[^"\']+?/captchas?/[^"\']+)' + CAPTCHA_BLOCK_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 @@ -234,10 +233,10 @@ class XFSHoster(SimpleHoster): retries = 3 else: delay = 1 * 60 * 60 - retries = 25 + retries = 24 - self.wait(delay, True) - self.retry(retries, reason=_("Download limit exceeded")) + self.wantReconnect = True + self.retry(retries, delay, _("Download limit exceeded")) elif 'countdown' in self.errmsg or 'Expired' in self.errmsg: self.retry(reason=_("Link expired")) @@ -249,6 +248,7 @@ class XFSHoster(SimpleHoster): self.fail(_("File too large for free download")) else: + self.wantReconnect = True self.retry(wait_time=60, reason=self.errmsg) if self.errmsg: @@ -256,8 +256,6 @@ class XFSHoster(SimpleHoster): else: self.info.pop('error', None) - return self.errmsg - def getPostParameters(self): if self.FORM_PATTERN or self.FORM_INPUTS_MAP: @@ -311,7 +309,7 @@ class XFSHoster(SimpleHoster): inputs['code'] = self.decryptCaptcha(captcha_url) return 1 - m = re.search(self.CAPTCHA_DIV_PATTERN, self.html, re.S) + m = re.search(self.CAPTCHA_BLOCK_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)) |