diff options
author | Walter Purcaro <vuolter@gmail.com> | 2014-11-13 23:47:36 +0100 |
---|---|---|
committer | Walter Purcaro <vuolter@gmail.com> | 2014-11-13 23:47:36 +0100 |
commit | bdfa8104065831bf0eb2f6a022552e8de725ed47 (patch) | |
tree | 4a43864e1bb257c7737a765d62899244b825a859 /pyload/plugins/internal | |
parent | [themes] Tiny code cosmetics (diff) | |
parent | [Plugin] Some fixes by rlindner81 (diff) | |
download | pyload-bdfa8104065831bf0eb2f6a022552e8de725ed47.tar.xz |
Merge branch 'stable' into 0.4.10
Conflicts:
module/plugins/accounts/MegaRapidCz.py
module/plugins/accounts/ShareRapidCom.py
module/plugins/crypter/HotfileComFolder.py
module/plugins/crypter/HotfileFolderCom.py
module/plugins/hoster/Keep2shareCC.py
module/plugins/hoster/Keep2shareCc.py
module/plugins/hoster/MegaCoNz.py
module/plugins/hoster/MegaNz.py
module/plugins/hoster/MegaRapidCz.py
module/plugins/hoster/ShareRapidCom.py
module/plugins/internal/CaptchaService.py
pyload/api/__init__.py
pyload/manager/AccountManager.py
pyload/manager/PluginManager.py
pyload/manager/thread/PluginThread.py
pyload/network/HTTPRequest.py
pyload/network/XDCCRequest.py
pyload/plugins/Plugin.py
pyload/plugins/account/BillionuploadsCom.py
pyload/plugins/account/CramitIn.py
pyload/plugins/account/EasybytezCom.py
pyload/plugins/account/FastshareCz.py
pyload/plugins/account/File4safeCom.py
pyload/plugins/account/FileParadoxIn.py
pyload/plugins/account/FileomCom.py
pyload/plugins/account/FilerNet.py
pyload/plugins/account/FilerioCom.py
pyload/plugins/account/FourSharedCom.py
pyload/plugins/account/Ftp.py
pyload/plugins/account/Http.py
pyload/plugins/account/HugefilesNet.py
pyload/plugins/account/HundredEightyUploadCom.py
pyload/plugins/account/LomafileCom.py
pyload/plugins/account/MovReelCom.py
pyload/plugins/account/MultishareCz.py
pyload/plugins/account/MyfastfileCom.py
pyload/plugins/account/NosuploadCom.py
pyload/plugins/account/NovafileCom.py
pyload/plugins/account/QuickshareCz.py
pyload/plugins/account/RarefileNet.py
pyload/plugins/account/RyushareCom.py
pyload/plugins/account/SecureUploadEu.py
pyload/plugins/account/SendmywayCom.py
pyload/plugins/account/ShareRapidCom.py
pyload/plugins/account/StahnuTo.py
pyload/plugins/account/StreamcloudEu.py
pyload/plugins/account/TusfilesNet.py
pyload/plugins/account/UploadingCom.py
pyload/plugins/account/UptoboxCom.py
pyload/plugins/account/VidPlayNet.py
pyload/plugins/addon/Checksum.py
pyload/plugins/addon/ClickAndLoad.py
pyload/plugins/addon/DeleteFinished.py
pyload/plugins/addon/DownloadScheduler.py
pyload/plugins/addon/ExternalScripts.py
pyload/plugins/addon/ExtractArchive.py
pyload/plugins/addon/HotFolder.py
pyload/plugins/addon/IRCInterface.py
pyload/plugins/addon/MergeFiles.py
pyload/plugins/addon/MultiHome.py
pyload/plugins/addon/RestartFailed.py
pyload/plugins/addon/UnSkipOnFail.py
pyload/plugins/addon/WindowsPhoneToastNotify.py
pyload/plugins/addon/XMPPInterface.py
pyload/plugins/base/Container.py
pyload/plugins/base/Crypter.py
pyload/plugins/base/Hook.py
pyload/plugins/base/OCR.py
pyload/plugins/container/RSDF.py
pyload/plugins/crypter/DuploadOrgFolder.py
pyload/plugins/crypter/EasybytezComFolder.py
pyload/plugins/crypter/FiredriveComFolder.py
pyload/plugins/crypter/HotfileFolderCom.py
pyload/plugins/crypter/LinkSaveIn.py
pyload/plugins/crypter/MultiuploadCom.py
pyload/plugins/crypter/ShareRapidComFolder.py
pyload/plugins/crypter/TusfilesNetFolder.py
pyload/plugins/crypter/UploadableChFolder.py
pyload/plugins/crypter/UploadedToFolder.py
pyload/plugins/hook/Captcha9kw.py
pyload/plugins/hook/MyfastfileCom.py
pyload/plugins/hoster/BillionuploadsCom.py
pyload/plugins/hoster/CramitIn.py
pyload/plugins/hoster/EasybytezCom.py
pyload/plugins/hoster/File4safeCom.py
pyload/plugins/hoster/FileParadoxIn.py
pyload/plugins/hoster/FilefactoryCom.py
pyload/plugins/hoster/FileomCom.py
pyload/plugins/hoster/FilerioCom.py
pyload/plugins/hoster/FileshareInUa.py
pyload/plugins/hoster/FiredriveCom.py
pyload/plugins/hoster/Ftp.py
pyload/plugins/hoster/HugefilesNet.py
pyload/plugins/hoster/HundredEightyUploadCom.py
pyload/plugins/hoster/Keep2shareCC.py
pyload/plugins/hoster/LoadTo.py
pyload/plugins/hoster/LomafileCom.py
pyload/plugins/hoster/MegaNz.py
pyload/plugins/hoster/MegacrypterCom.py
pyload/plugins/hoster/MovReelCom.py
pyload/plugins/hoster/MyvideoDe.py
pyload/plugins/hoster/NosuploadCom.py
pyload/plugins/hoster/NovafileCom.py
pyload/plugins/hoster/PandaPlaNet.py
pyload/plugins/hoster/PremiumTo.py
pyload/plugins/hoster/RarefileNet.py
pyload/plugins/hoster/RyushareCom.py
pyload/plugins/hoster/SecureUploadEu.py
pyload/plugins/hoster/SendmywayCom.py
pyload/plugins/hoster/ShareRapidCom.py
pyload/plugins/hoster/SockshareCom.py
pyload/plugins/hoster/StreamcloudEu.py
pyload/plugins/hoster/TurbobitNet.py
pyload/plugins/hoster/TusfilesNet.py
pyload/plugins/hoster/UptoboxCom.py
pyload/plugins/hoster/VidPlayNet.py
pyload/plugins/hoster/WebshareCz.py
pyload/plugins/internal/DeadCrypter.py
pyload/plugins/internal/DeadHoster.py
pyload/plugins/internal/MultiHoster.py
pyload/plugins/internal/SimpleCrypter.py
pyload/plugins/internal/SimpleHoster.py
pyload/plugins/internal/UpdateManager.py
pyload/plugins/internal/XFSPAccount.py
pyload/plugins/internal/XFSPHoster.py
pyload/utils/__init__.py
pyload/utils/packagetools.py
pyload/webui/app/cnl.py
Diffstat (limited to 'pyload/plugins/internal')
-rw-r--r-- | pyload/plugins/internal/AbstractExtractor.py | 13 | ||||
-rw-r--r-- | pyload/plugins/internal/DeadCrypter.py | 9 | ||||
-rw-r--r-- | pyload/plugins/internal/DeadHoster.py | 9 | ||||
-rw-r--r-- | pyload/plugins/internal/MultiHoster.py | 39 | ||||
-rw-r--r-- | pyload/plugins/internal/SimpleCrypter.py | 135 | ||||
-rw-r--r-- | pyload/plugins/internal/SimpleHoster.py | 289 | ||||
-rw-r--r-- | pyload/plugins/internal/UnRar.py | 13 | ||||
-rw-r--r-- | pyload/plugins/internal/UnZip.py | 8 | ||||
-rw-r--r-- | pyload/plugins/internal/UpdateManager.py | 73 | ||||
-rw-r--r-- | pyload/plugins/internal/XFSPHoster.py | 367 |
10 files changed, 398 insertions, 557 deletions
diff --git a/pyload/plugins/internal/AbstractExtractor.py b/pyload/plugins/internal/AbstractExtractor.py index 5a372fd71..54ea9b348 100644 --- a/pyload/plugins/internal/AbstractExtractor.py +++ b/pyload/plugins/internal/AbstractExtractor.py @@ -13,11 +13,12 @@ class WrongPassword(Exception): class AbtractExtractor: - __name__ = "AbtractExtractor" + __name__ = "AbtractExtractor" __version__ = "0.1" __description__ = """Abtract extractor plugin""" - __authors__ = [("pyLoad Team", "admin@pyload.org")] + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] @staticmethod @@ -27,6 +28,7 @@ class AbtractExtractor: """ return True + @staticmethod def getTargets(files_ids): """ Filter suited targets from list of filename id tuple list @@ -35,6 +37,7 @@ class AbtractExtractor: """ raise NotImplementedError + def __init__(self, m, file, out, fullpath, overwrite, excludefiles, renice): """Initialize extractor for specific file @@ -54,10 +57,12 @@ class AbtractExtractor: self.renice = renice self.files = [] #: Store extracted files here + def init(self): """ Initialize additional data structures """ pass + def checkArchive(self): """Check if password if needed. Raise ArchiveError if integrity is questionable. @@ -67,6 +72,7 @@ class AbtractExtractor: """ return False + def checkPassword(self, password): """ Check if the given password is/might be correct. If it can not be decided at this point return true. @@ -76,6 +82,7 @@ class AbtractExtractor: """ return True + def extract(self, progress, password=None): """Extract the archive. Raise specific errors in case of failure. @@ -88,6 +95,7 @@ class AbtractExtractor: """ raise NotImplementedError + def getDeleteFiles(self): """Return list of files to delete, do *not* delete them here. @@ -95,6 +103,7 @@ class AbtractExtractor: """ raise NotImplementedError + def getExtractedFiles(self): """Populate self.files at some point while extracting""" return self.files diff --git a/pyload/plugins/internal/DeadCrypter.py b/pyload/plugins/internal/DeadCrypter.py index 9a59677fd..83aed6d43 100644 --- a/pyload/plugins/internal/DeadCrypter.py +++ b/pyload/plugins/internal/DeadCrypter.py @@ -4,14 +4,15 @@ from pyload.plugins.base.Crypter import Crypter as _Crypter class DeadCrypter(_Crypter): - __name__ = "DeadCrypter" - __type__ = "crypter" + __name__ = "DeadCrypter" + __type__ = "crypter" __version__ = "0.02" - __pattern__ = None + __pattern__ = r'^unmatchable$' __description__ = """Crypter is no longer available""" - __authors__ = [("stickell", "l.stickell@yahoo.it")] + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it")] def setup(self): diff --git a/pyload/plugins/internal/DeadHoster.py b/pyload/plugins/internal/DeadHoster.py index 349296e47..76a7b5d80 100644 --- a/pyload/plugins/internal/DeadHoster.py +++ b/pyload/plugins/internal/DeadHoster.py @@ -12,14 +12,15 @@ def create_getInfo(plugin): class DeadHoster(_Hoster): - __name__ = "DeadHoster" - __type__ = "hoster" + __name__ = "DeadHoster" + __type__ = "hoster" __version__ = "0.12" - __pattern__ = None + __pattern__ = r'^unmatchable$' __description__ = """Hoster is no longer available""" - __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz")] def setup(self): diff --git a/pyload/plugins/internal/MultiHoster.py b/pyload/plugins/internal/MultiHoster.py index 087edb6af..97cbb4591 100644 --- a/pyload/plugins/internal/MultiHoster.py +++ b/pyload/plugins/internal/MultiHoster.py @@ -7,27 +7,30 @@ from pyload.utils import remove_chars class MultiHoster(Addon): - __name__ = "MultiHoster" - __type__ = "addon" + __name__ = "MultiHoster" + __type__ = "addon" __version__ = "0.20" __description__ = """Generic MultiHoster plugin""" - __authors__ = [("pyLoad Team", "admin@pyload.org")] + __license__ = "GPLv3" + __authors__ = [("pyLoad Team", "admin@pyload.org")] - 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")] - ignored = [] 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")] + HOSTER_EXCLUDED = [] + def setup(self): self.hosters = [] self.supported = [] self.new_supported = [] + def getConfig(self, option, default=''): """getConfig with default value - subclass may not implements all config options""" try: @@ -36,10 +39,11 @@ class MultiHoster(Addon): except KeyError: return default + def getHosterCached(self): if not self.hosters: try: - hosterSet = self.toHosterSet(self.getHoster()) - set(self.ignored) + hosterSet = self.toHosterSet(self.getHoster()) - set(self.HOSTER_EXCLUDED) except Exception, e: self.logError(e) return [] @@ -61,10 +65,11 @@ class MultiHoster(Addon): return self.hosters + def toHosterSet(self, hosters): hosters = set((str(x).strip().lower() for x in hosters)) - for rep in self.replacements: + for rep in self.HOSTER_REPLACEMENTS: if rep[0] in hosters: hosters.remove(rep[0]) hosters.add(rep[1]) @@ -72,6 +77,7 @@ class MultiHoster(Addon): hosters.discard('') return hosters + def getHoster(self): """Load list of supported hoster @@ -79,6 +85,7 @@ class MultiHoster(Addon): """ raise NotImplementedError + def coreReady(self): if self.cb: self.core.scheduler.removeJob(self.cb) @@ -94,9 +101,11 @@ class MultiHoster(Addon): else: self.periodical() + def initPeriodical(self): pass + def periodical(self): """reload hoster list periodically""" self.logInfo(_("Reloading supported hoster list")) @@ -112,12 +121,13 @@ class MultiHoster(Addon): for hoster in old_supported: self.unloadHoster(hoster) + 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.items() if data] + accountList = [name.lower() for name, data in self.core.accountManager.accounts.iteritems() if data] excludedList = [] for hoster in self.getHosterCached(): @@ -152,9 +162,9 @@ 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) + regexp = r'%s|%s' % (klass.__pattern__, regexp) self.logDebug("Regexp", regexp) @@ -162,6 +172,7 @@ class MultiHoster(Addon): dict['pattern'] = regexp dict['re'] = re.compile(regexp) + def unloadHoster(self, hoster): dict = self.core.pluginManager.hosterPlugins[hoster] if "module" in dict: @@ -171,6 +182,7 @@ class MultiHoster(Addon): del dict['new_module'] del dict['new_name'] + def unload(self): """Remove override for all hosters. Scheduler job is removed by AddonManager""" for hoster in self.supported: @@ -182,6 +194,7 @@ class MultiHoster(Addon): dict['pattern'] = getattr(klass, "__pattern__", r'^unmatchable$') dict['re'] = re.compile(dict['pattern']) + def downloadFailed(self, pyfile): """remove plugin override if download fails but not if file is offline/temp.offline""" if pyfile.hasStatus("failed") and self.getConfig("unloadFailing", True): diff --git a/pyload/plugins/internal/SimpleCrypter.py b/pyload/plugins/internal/SimpleCrypter.py index 6c5c9593f..634ec5f12 100644 --- a/pyload/plugins/internal/SimpleCrypter.py +++ b/pyload/plugins/internal/SimpleCrypter.py @@ -2,22 +2,28 @@ import re +from urlparse import urlparse + from pyload.plugins.Crypter import Crypter -from pyload.plugins.internal.SimpleHoster import PluginParseError, replace_patterns, set_cookies +from module.plugins.Plugin import Fail +from module.plugins.internal.SimpleHoster import _error, _wait, parseFileInfo, replace_patterns, set_cookies from pyload.utils import fixup, html_unescape class SimpleCrypter(Crypter): - __name__ = "SimpleCrypter" - __type__ = "crypter" - __version__ = "0.13" + __name__ = "SimpleCrypter" + __type__ = "crypter" + __version__ = "0.28" - __pattern__ = None + __pattern__ = r'^unmatchable$' + __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides core.config['general']['folder_per_package'] + ("subfolder_per_package", "bool", "Create a subfolder for each package", True)] __description__ = """Simple decrypter plugin""" - __authors__ = [("stickell", "l.stickell@yahoo.it"), - ("zoidberg", "zoidberg@mujmail.cz"), - ("Walter Purcaro", "vuolter@gmail.com")] + __license__ = "GPLv3" + __authors__ = [("stickell", "l.stickell@yahoo.it"), + ("zoidberg", "zoidberg@mujmail.cz"), + ("Walter Purcaro", "vuolter@gmail.com")] """ @@ -26,8 +32,8 @@ class SimpleCrypter(Crypter): LINK_PATTERN: group(1) must be a download link or a regex to catch more links example: LINK_PATTERN = r'<div class="link"><a href="(.+?)"' - TITLE_PATTERN: (optional) group(1) should be the folder name or the webpage title - example: TITLE_PATTERN = r'<title>Files of: ([^<]+) folder</title>' + NAME_PATTERN: (optional) folder name or webpage title + example: NAME_PATTERN = r'<title>Files of: (?P<N>[^<]+) folder</title>' OFFLINE_PATTERN: (optional) Checks if the file is yet available online example: OFFLINE_PATTERN = r'File (deleted|not found)' @@ -46,87 +52,103 @@ class SimpleCrypter(Crypter): and its loadPage method: + def loadPage(self, page_n): return the html of the page number page_n """ - TITLE_REPLACEMENTS = [("&#?\w+;", fixup)] - URL_REPLACEMENTS = [] + LINK_PATTERN = None + + NAME_REPLACEMENTS = [("&#?\w+;", fixup)] + URL_REPLACEMENTS = [] TEXT_ENCODING = False #: Set to True or encoding name if encoding in http header is not correct - COOKIES = True #: or False or list of tuples [(domain, name, value)] + COOKIES = True #: or False or list of tuples [(domain, name, value)] LOGIN_ACCOUNT = False LOGIN_PREMIUM = False + #@TODO: remove in 0.4.10 + def init(self): + self.info = {} + + account_name = (self.__name__ + ".py").replace("Folder.py", "").replace(".py", "") + account = self.core.accountManager.getAccountPlugin(account_name) + + if account and account.canUse(): + self.user, data = account.selectAccount() + self.req = account.getAccountRequest(self.user) + self.premium = account.isPremium(self.user) + + self.account = account + + def prepare(self): if self.LOGIN_ACCOUNT and not self.account: - self.fail('Required account not found!') + self.fail(_("Required account not found")) if self.LOGIN_PREMIUM and not self.premium: - self.fail('Required premium account not found!') + self.fail(_("Required premium account not found")) if isinstance(self.COOKIES, list): set_cookies(self.req.cj, self.COOKIES) + self.pyfile.url = replace_patterns(self.pyfile.url, self.URL_REPLACEMENTS) - def decrypt(self, pyfile): - self.prepare() + if self.html is None: + self.html = self.load(self.pyfile.url, decode=not self.TEXT_ENCODING, cookies=bool(self.COOKIES)) - pyfile.url = replace_patterns(pyfile.url, self.URL_REPLACEMENTS) + if isinstance(self.TEXT_ENCODING, basestring): + self.html = unicode(self.html, self.TEXT_ENCODING) - self.html = self.load(pyfile.url, decode=not self.TEXT_ENCODING) - self.checkOnline() + def decrypt(self, pyfile): + self.prepare() - package_name, folder_name = self.getPackageNameAndFolder() + if self.html is None: + self.fail(_("No html retrieved")) - self.package_links = self.getLinks() + if not self.info: + self.getFileInfo() + + self.links = self.getLinks() if hasattr(self, 'PAGES_PATTERN') and hasattr(self, 'loadPage'): self.handleMultiPages() - self.logDebug("Package has %d links" % len(self.package_links)) + self.logDebug("Package has %d links" % len(self.links)) - if self.package_links: - self.packages = [(package_name, self.package_links, folder_name)] - else: - self.fail('Could not extract any links') + if self.links: + self.packages = [(self.info['name'], self.links, self.info['folder'])] - def getLinks(self): - """ - Returns the links extracted from self.html - You should override this only if it's impossible to extract links using only the LINK_PATTERN. - """ - return re.findall(self.LINK_PATTERN, self.html) + def getFileInfo(self): + name, size, status, url = parseFileInfo(self) + if name and name != url: + self.pyfile.name = name + else: + self.pyfile.name = self.info['name'] = urlparse(html_unescape(name)).path.split("/")[-1] - def checkOnline(self): - if hasattr(self, "OFFLINE_PATTERN") and re.search(self.OFFLINE_PATTERN, self.html): + if status is 1: self.offline() - elif hasattr(self, "TEMP_OFFLINE_PATTERN") and re.search(self.TEMP_OFFLINE_PATTERN, self.html): + + elif status is 6: self.tempOffline() + self.info['folder'] = self.pyfile.name - def getPackageNameAndFolder(self): - if hasattr(self, 'TITLE_PATTERN'): - try: - m = re.search(self.TITLE_PATTERN, self.html) - name = replace_patterns(m.group(1).strip(), self.TITLE_REPLACEMENTS) - folder = html_unescape(name) - except: - pass - else: - self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder)) - return name, folder + self.logDebug("FILE NAME: %s" % self.pyfile.name) + return self.info - name = self.pyfile.package().name - folder = self.pyfile.package().folder - self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder)) - return name, folder + def getLinks(self): + """ + Returns the links extracted from self.html + You should override this only if it's impossible to extract links using only the LINK_PATTERN. + """ + return re.findall(self.LINK_PATTERN, self.html) def handleMultiPages(self): @@ -138,8 +160,13 @@ class SimpleCrypter(Crypter): for p in xrange(2, pages + 1): self.html = self.loadPage(p) - self.package_links += self.getLinks() + self.links += self.getLinks() + + + #@TODO: Remove in 0.4.10 + def wait(self, seconds=0, reconnect=None): + return _wait(self, seconds, reconnect) - def parseError(self, msg): - raise PluginParseError(msg) + def error(self, reason="", type="parse"): + return _error(self, reason, type) diff --git a/pyload/plugins/internal/SimpleHoster.py b/pyload/plugins/internal/SimpleHoster.py index bc4cc3c88..b192b45b2 100644 --- a/pyload/plugins/internal/SimpleHoster.py +++ b/pyload/plugins/internal/SimpleHoster.py @@ -5,12 +5,34 @@ import re from time import time from urlparse import urlparse +from pycurl import FOLLOWLOCATION + from pyload.network.CookieJar import CookieJar from pyload.network.RequestFactory import getURL from pyload.plugins.base.Hoster import Hoster +from module.plugins.Plugin import Fail from pyload.utils import fixup, html_unescape, parseFileSize +#@TODO: Remove in 0.4.10 and redirect to self.error instead +def _error(self, reason, type): + if not reason and not type: + type = "unknown" + + msg = _("%s error") % type.strip().capitalize() if type else _("Error") + msg += ": " + reason.strip() if reason else "" + msg += _(" | Plugin may be out of date") + + raise Fail(msg) + + +#@TODO: Remove in 0.4.10 +def _wait(self, seconds, reconnect): + if seconds: + self.setWait(seconds, reconnect) + super(SimpleHoster, self).wait() + + def replace_patterns(string, ruleslist): for r in ruleslist: rf, rt = r @@ -46,7 +68,7 @@ def parseHtmlForm(attr_str, html, input_names=None): if isinstance(input_names, dict): # check input attributes - for key, val in input_names.items(): + for key, val in input_names.iteritems(): if key in inputs: if isinstance(val, basestring) and inputs[key] == val: continue @@ -66,28 +88,32 @@ def parseHtmlForm(attr_str, html, input_names=None): return {}, None # no matching form found -def parseFileInfo(self, url='', html=''): - info = {"name": url, "size": 0, "status": 3} - - if hasattr(self, "pyfile"): +def parseFileInfo(self, url="", html=""): + if not url and hasattr(self, "pyfile"): url = self.pyfile.url - if hasattr(self, "req") and self.req.http.code == '404': - info['status'] = 1 - else: - if not html and hasattr(self, "html"): + info = {'name': url, 'size': 0, 'status': 3} + + if not html: + if url: + return create_getInfo(self)([url]).next() + + elif hasattr(self, "req") and self.req.http.code == '404': + info['status'] = 1 + + elif hasattr(self, "html"): html = self.html - if isinstance(self.TEXT_ENCODING, basestring): - html = unicode(html, self.TEXT_ENCODING) - if hasattr(self, "html"): - self.html = html + if html: if hasattr(self, "OFFLINE_PATTERN") and re.search(self.OFFLINE_PATTERN, html): info['status'] = 1 + elif hasattr(self, "FILE_OFFLINE_PATTERN") and re.search(self.FILE_OFFLINE_PATTERN, html): #@TODO: Remove in 0.4.10 info['status'] = 1 + elif hasattr(self, "TEMP_OFFLINE_PATTERN") and re.search(self.TEMP_OFFLINE_PATTERN, html): info['status'] = 6 + else: online = False try: @@ -95,7 +121,8 @@ def parseFileInfo(self, url='', html=''): except: pass - for pattern in ("FILE_INFO_PATTERN", "FILE_NAME_PATTERN", "FILE_SIZE_PATTERN"): + for pattern in ("INFO_PATTERN", "NAME_PATTERN", "SIZE_PATTERN", + "FILE_INFO_PATTERN", "FILE_NAME_PATTERN", "FILE_SIZE_PATTERN"): #@TODO: Remove in 0.4.10 try: info.update(re.search(getattr(self, pattern), html).groupdict()) online = True @@ -105,18 +132,35 @@ def parseFileInfo(self, url='', html=''): if online: # File online, return name and size info['status'] = 2 + if 'N' in info: - info['name'] = replace_patterns(info['N'].strip(), self.FILE_NAME_REPLACEMENTS) + info['name'] = replace_patterns(info['N'].strip(), + self.FILE_NAME_REPLACEMENTS if hasattr(self, "FILE_NAME_REPLACEMENTS") else self.NAME_REPLACEMENTS) #@TODO: Remove FILE_NAME_REPLACEMENTS check in 0.4.10 + if 'S' in info: size = replace_patterns(info['S'] + info['U'] if 'U' in info else info['S'], - self.FILE_SIZE_REPLACEMENTS) + self.FILE_SIZE_REPLACEMENTS if hasattr(self, "FILE_SIZE_REPLACEMENTS") else self.SIZE_REPLACEMENTS) #@TODO: Remove FILE_SIZE_REPLACEMENTS check in 0.4.10 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 hasattr(self, "file_info"): - self.file_info = info + if hasattr(self, "html") and self.html is None: + self.html = html + + if hasattr(self, "info"): + try: + self.logDebug(_("File info (before update): %s") % self.info) + except: + pass + + self.info.update(info) + + try: + self.logDebug(_("File info (after update): %s") % self.info) + except: + pass return info['name'], info['size'], info['status'], url @@ -125,58 +169,63 @@ def create_getInfo(plugin): def getInfo(urls): for url in urls: - cj = CookieJar(plugin.__name__) - if isinstance(plugin.COOKIES, list): + if hasattr(plugin, "COOKIES") and isinstance(plugin.COOKIES, list): + cj = CookieJar(plugin.__name__) set_cookies(cj, plugin.COOKIES) - file_info = parseFileInfo(plugin, url, getURL(replace_patterns(url, plugin.FILE_URL_REPLACEMENTS), - decode=not plugin.TEXT_ENCODING, cookies=cj)) - yield file_info + else: + cj = None - return getInfo + if hasattr(plugin, "URL_REPLACEMENTS"): + url = replace_patterns(url, plugin.URL_REPLACEMENTS) + elif hasattr(plugin, "FILE_URL_REPLACEMENTS"): #@TODO: Remove in 0.4.10 + url = replace_patterns(url, plugin.FILE_URL_REPLACEMENTS) -def timestamp(): - return int(time() * 1000) + if hasattr(plugin, "TEXT_ENCODING"): + html = getURL(url, cookies=bool(cj), decode=not plugin.TEXT_ENCODING) + if isinstance(plugin.TEXT_ENCODING, basestring): + html = unicode(html, plugin.TEXT_ENCODING) + else: + html = getURL(url, cookies=bool(cj), decode=True) + yield parseFileInfo(plugin, url, html) -class PluginParseError(Exception): + return getInfo - def __init__(self, msg): - Exception.__init__(self) - self.value = 'Parse error (%s) - plugin may be out of date' % msg - def __str__(self): - return repr(self.value) +def timestamp(): + return int(time() * 1000) class SimpleHoster(Hoster): - __name__ = "SimpleHoster" - __type__ = "hoster" - __version__ = "0.38" + __name__ = "SimpleHoster" + __type__ = "hoster" + __version__ = "0.53" - __pattern__ = None + __pattern__ = r'^unmatchable$' __description__ = """Simple hoster plugin""" - __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), - ("stickell", "l.stickell@yahoo.it"), - ("Walter Purcaro", "vuolter@gmail.com")] + __license__ = "GPLv3" + __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), + ("stickell", "l.stickell@yahoo.it"), + ("Walter Purcaro", "vuolter@gmail.com")] """ Following patterns should be defined by each hoster: - FILE_INFO_PATTERN: Name and Size of the file - example: FILE_INFO_PATTERN = r'(?P<N>file_name) (?P<S>file_size) (?P<U>size_unit)' + INFO_PATTERN: (optional) Name and Size of the file + example: INFO_PATTERN = r'(?P<N>file_name) (?P<S>file_size) (?P<U>size_unit)' or - FILE_NAME_PATTERN: Name that will be set for the file - example: FILE_NAME_PATTERN = r'(?P<N>file_name)' - FILE_SIZE_PATTERN: Size that will be checked for the file - example: FILE_SIZE_PATTERN = r'(?P<S>file_size) (?P<U>size_unit)' + NAME_PATTERN: (optional) Name that will be set for the file + example: NAME_PATTERN = r'(?P<N>file_name)' + SIZE_PATTERN: (optional) Size that will be checked for the file + example: SIZE_PATTERN = r'(?P<S>file_size) (?P<U>size_unit)' - OFFLINE_PATTERN: Checks if the file is yet available online + OFFLINE_PATTERN: (optional) Checks if the file is yet available online example: OFFLINE_PATTERN = r'File (deleted|not found)' - TEMP_OFFLINE_PATTERN: Checks if the file is temporarily offline + TEMP_OFFLINE_PATTERN: (optional) Checks if the file is temporarily offline example: TEMP_OFFLINE_PATTERN = r'Server (maintenance|maintainance)' PREMIUM_ONLY_PATTERN: (optional) Checks if the file can be downloaded only with a premium account @@ -192,17 +241,17 @@ class SimpleHoster(Hoster): example: LINK_PREMIUM_PATTERN = r'<div class="link"><a href="(.+?)"' """ - FILE_NAME_REPLACEMENTS = [("&#?\w+;", fixup)] - FILE_SIZE_REPLACEMENTS = [] - FILE_URL_REPLACEMENTS = [] + NAME_REPLACEMENTS = [("&#?\w+;", fixup)] + SIZE_REPLACEMENTS = [] + URL_REPLACEMENTS = [] - TEXT_ENCODING = False #: Set to True or encoding name if encoding in http header is not correct - COOKIES = True #: or False or list of tuples [(domain, name, value)] + TEXT_ENCODING = False #: Set to True or encoding name if encoding 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 def init(self): - self.file_info = {} + self.info = {} def setup(self): @@ -212,107 +261,139 @@ class SimpleHoster(Hoster): def prepare(self): if isinstance(self.COOKIES, list): set_cookies(self.req.cj, self.COOKIES) + self.req.setOption("timeout", 120) + self.pyfile.url = replace_patterns(self.pyfile.url, + self.FILE_URL_REPLACEMENTS if hasattr(self, "FILE_URL_REPLACEMENTS") else self.URL_REPLACEMENTS) #@TODO: Remove FILE_URL_REPLACEMENTS check in 0.4.10 + + if self.premium: + self.logDebug(_("Looking for direct download link...")) + direct_link = self.getDirectLink(self.pyfile.url) + if direct_link: + return direct_link + else: + self.logDebug(_("No direct download link found")) + self.html = None + self.info = {} + + if self.html is None: + self.html = self.load(self.pyfile.url, decode=not self.TEXT_ENCODING, cookies=bool(self.COOKIES)) + + if isinstance(self.TEXT_ENCODING, basestring): + self.html = unicode(self.html, self.TEXT_ENCODING) + def process(self, pyfile): - self.prepare() + direct_link = self.prepare() - pyfile.url = replace_patterns(pyfile.url, self.FILE_URL_REPLACEMENTS) + if isinstance(direct_link, basestring): + self.logInfo(_("Direct download link detected")) + self.download(direct_link, ref=True, cookies=True, disposition=True) - # Due to a 0.4.9 core bug self.load would keep previous cookies even if overridden by cookies parameter. - # Workaround using getURL. Can be reverted in 0.4.10 as the cookies bug has been fixed. - self.html = getURL(pyfile.url, decode=not self.TEXT_ENCODING, cookies=self.COOKIES) - premium_only = hasattr(self, 'PREMIUM_ONLY_PATTERN') and re.search(self.PREMIUM_ONLY_PATTERN, self.html) - if not premium_only: # Usually premium only pages doesn't show the file information - self.getFileInfo() + elif self.html is None: + self.fail(_("No html retrieved")) - if self.premium and (not self.FORCE_CHECK_TRAFFIC or self.checkTrafficLeft()): - self.handlePremium() - elif premium_only: - self.fail("This link require a premium account") else: - # This line is required due to the getURL workaround. Can be removed in 0.4.10 - self.html = self.load(pyfile.url, decode=not self.TEXT_ENCODING) - self.handleFree() + premium_only = hasattr(self, 'PREMIUM_ONLY_PATTERN') and re.search(self.PREMIUM_ONLY_PATTERN, self.html) + if not premium_only and not self.info: #: Usually premium only pages doesn't show any file information + self.getFileInfo() + + if self.premium and (not self.FORCE_CHECK_TRAFFIC or self.checkTrafficLeft()): + self.logDebug("Handle as premium download") + self.handlePremium() + elif premium_only: + self.fail(_("Link require a premium account to be handled")) + else: + self.logDebug("Handle as free download") + self.handleFree() + + + def getDirectLink(self, url): + self.req.http.c.setopt(FOLLOWLOCATION, 0) + + html = self.load(url, ref=True, decode=True) + + self.req.http.c.setopt(FOLLOWLOCATION, 1) + + if parseFileInfo(self, url, html)[2] 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 def getFileInfo(self): - self.logDebug("URL", self.pyfile.url) + name, size, status, url = parseFileInfo(self) - name, size, status = parseFileInfo(self)[:3] + if name and name != url: + self.pyfile.name = name + else: + self.pyfile.name = self.info['name'] = urlparse(html_unescape(name)).path.split("/")[-1] - if status == 1: + if status is 1: self.offline() - elif status == 6: + + elif status is 6: self.tempOffline() - elif status != 2: - self.logDebug(self.file_info) - self.parseError('File info') - if name: - self.pyfile.name = name - else: - self.pyfile.name = html_unescape(urlparse(self.pyfile.url).path.split("/")[-1]) + elif status is not 2: + self.error(_("File info: %s") % self.info) if size: self.pyfile.size = size else: self.logError(_("File size not parsed")) - self.logDebug("FILE NAME: %s FILE SIZE: %s" % (self.pyfile.name, self.pyfile.size)) - return self.file_info + self.logDebug("FILE NAME: %s" % self.pyfile.name, "FILE SIZE: %d" % self.pyfile.size or _("Unknown")) + return self.info def handleFree(self): if not hasattr(self, 'LINK_FREE_PATTERN'): - self.fail("Free download not implemented") + self.fail(_("Free download not implemented")) try: m = re.search(self.LINK_FREE_PATTERN, self.html) if m is None: - self.parseError("Free download link not found") + self.error(_("Free download link not found")) link = m.group(1) except Exception, e: - self.logError(str(e)) + self.fail(str(e)) else: self.download(link, ref=True, cookies=True, disposition=True) def handlePremium(self): if not hasattr(self, 'LINK_PREMIUM_PATTERN'): - self.fail("Premium download not implemented") + self.fail(_("Premium download not implemented")) try: m = re.search(self.LINK_PREMIUM_PATTERN, self.html) if m is None: - self.parseError("Premium download link not found") + self.error(_("Premium download link not found")) link = m.group(1) except Exception, e: - self.logError(str(e)) + self.fail(str(e)) else: self.download(link, ref=True, cookies=True, disposition=True) - def parseError(self, msg): - raise PluginParseError(msg) - - def longWait(self, wait_time=None, max_tries=3): if wait_time and isinstance(wait_time, (int, long, float)): time_str = "%dh %dm" % divmod(wait_time / 60, 60) else: wait_time = 900 - time_str = "(unknown time)" + time_str = _("(unknown time)") max_tries = 100 self.logInfo(_("Download limit reached, reconnect or wait %s") % time_str) self.setWait(wait_time, True) self.wait() - self.retry(max_tries=max_tries, reason="Download limit reached") + self.retry(max_tries=max_tries, reason=_("Download limit reached")) def parseHtmlForm(self, attr_str='', input_names=None): @@ -321,15 +402,21 @@ class SimpleHoster(Hoster): def checkTrafficLeft(self): traffic = self.account.getAccountInfo(self.user, True)['trafficleft'] - if traffic == -1: + + if traffic is None: + return False + elif traffic == -1: return True - size = self.pyfile.size / 1024 - self.logInfo(_("Filesize: %i KiB, Traffic left for user %s: %i KiB") % (size, self.user, traffic)) - return size <= traffic + else: + size = self.pyfile.size / 1024 + self.logInfo(_("Filesize: %i KiB, Traffic left for user %s: %i KiB") % (size, self.user, traffic)) + return size <= traffic #@TODO: Remove in 0.4.10 - def wait(self, seconds=False, reconnect=False): - if seconds: - self.setWait(seconds, reconnect) - super(SimpleHoster, self).wait() + def wait(self, seconds=0, reconnect=None): + return _wait(self, seconds, reconnect) + + + def error(self, reason="", type="parse"): + return _error(self, reason, type) diff --git a/pyload/plugins/internal/UnRar.py b/pyload/plugins/internal/UnRar.py index df7557d0d..31a0d7642 100644 --- a/pyload/plugins/internal/UnRar.py +++ b/pyload/plugins/internal/UnRar.py @@ -21,19 +21,20 @@ def renice(pid, value): class UnRar(AbtractExtractor): - __name__ = "UnRar" + __name__ = "UnRar" __version__ = "0.18" __description__ = """Rar extractor plugin""" - __authors__ = [("RaNaN", "RaNaN@pyload.org")] + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] CMD = "unrar" # there are some more uncovered rar formats - re_version = re.compile(r"(UNRAR 5[\.\d]+(.*?)freeware)") + 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[0-9]+)", 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) @@ -167,9 +168,9 @@ class UnRar(AbtractExtractor): def getDeleteFiles(self): if ".part" in basename(self.file): - return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.IGNORECASE)) + return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.I)) # get files which matches .r* and filter unsuited files out - parts = glob(re.sub(r"(?<=\.r)ar$", "*", self.file, re.IGNORECASE)) + parts = glob(re.sub(r"(?<=\.r)ar$", "*", self.file, re.I)) return filter(lambda x: self.re_partfiles.match(x), parts) diff --git a/pyload/plugins/internal/UnZip.py b/pyload/plugins/internal/UnZip.py index 0fe50198f..413c0699e 100644 --- a/pyload/plugins/internal/UnZip.py +++ b/pyload/plugins/internal/UnZip.py @@ -7,17 +7,19 @@ from pyload.plugins.internal.AbstractExtractor import AbtractExtractor class UnZip(AbtractExtractor): - __name__ = "UnZip" + __name__ = "UnZip" __version__ = "0.1" __description__ = """Zip extractor plugin""" - __authors__ = [("RaNaN", "RaNaN@pyload.org")] + __license__ = "GPLv3" + __authors__ = [("RaNaN", "RaNaN@pyload.org")] @staticmethod def checkDeps(): return sys.version_info[:2] >= (2, 6) + @staticmethod def getTargets(files_ids): result = [] @@ -28,10 +30,12 @@ class UnZip(AbtractExtractor): return result + def extract(self, progress, password=None): z = zipfile.ZipFile(self.file) self.files = z.namelist() z.extractall(self.out) + def getDeleteFiles(self): return [self.file] diff --git a/pyload/plugins/internal/UpdateManager.py b/pyload/plugins/internal/UpdateManager.py index f64a1e573..3f5b34c45 100644 --- a/pyload/plugins/internal/UpdateManager.py +++ b/pyload/plugins/internal/UpdateManager.py @@ -12,9 +12,9 @@ from pyload.utils import safe_join class UpdateManager(Addon): - __name__ = "UpdateManager" - __type__ = "addon" - __version__ = "0.36" + __name__ = "UpdateManager" + __type__ = "addon" + __version__ = "0.39" __config__ = [("activated", "bool", "Activated", True), ("mode", "pyLoad + plugins;plugins only", "Check updates for", "pyLoad + plugins"), @@ -23,15 +23,14 @@ class UpdateManager(Addon): ("nodebugupdate", "bool", "Don't check for updates in debug mode", True)] __description__ = """Check for updates""" - __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] - event_list = ["pluginConfigChanged"] + # event_list = ["pluginConfigChanged"] SERVER_URL = "http://updatemanager.pyload.org" - MIRROR_URL = "" #: empty actually - - MIN_INTERVAL = 3 * 60 * 60 #: 3h minimum check interval (value is in seconds) + MIN_INTERVAL = 6 * 60 * 60 #: 6h minimum check interval (value is in seconds) def pluginConfigChanged(self, plugin, name, value): @@ -43,6 +42,7 @@ class UpdateManager(Addon): self.initPeriodical() else: self.logDebug("Invalid interval value, kept current") + elif name == "reloadplugins": if self.cb2: self.core.scheduler.removeJob(self.cb2) @@ -61,16 +61,17 @@ class UpdateManager(Addon): def setup(self): - self.cb2 = None + self.cb2 = None self.interval = self.MIN_INTERVAL self.updating = False - self.info = {'pyload': False, 'version': None, 'plugins': False} - self.mtimes = {} #: store modification time for each plugin + self.info = {'pyload': False, 'version': None, 'plugins': False} + self.mtimes = {} #: store modification time for each plugin def periodical2(self): if not self.updating: self.autoreloadPlugins() + self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False) @@ -110,21 +111,18 @@ class UpdateManager(Addon): def server_request(self): - request = lambda x: getURL(x, get={'v': self.core.api.getServerVersion()}).splitlines() try: - return request(self.SERVER_URL) + return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines() except: - try: - return request(self.MIRROR_URL) - except: - pass - self.logWarning(_("Unable to contact server to get updates")) + 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: @@ -141,14 +139,18 @@ class UpdateManager(Addon): 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) @@ -156,6 +158,7 @@ class UpdateManager(Addon): 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 @@ -165,11 +168,13 @@ class UpdateManager(Addon): if self.info['plugins']: return False #: plugins were already updated - updated = [] + exitcode = 0 + updated = [] - vre = re.compile(r'__version__.*=.*("|\')([0-9.]+)') + vre = re.compile(r'__version__.*=.*("|\')([\d.]+)') url = updates[0] schema = updates[1].split('|') + if "BLACKLIST" in updates: blacklist = updates[updates.index('BLACKLIST') + 1:] updates = updates[2:updates.index('BLACKLIST')] @@ -177,11 +182,13 @@ class UpdateManager(Addon): blacklist = None updates = updates[2:] - upgradable = sorted(map(lambda x: dict(zip(schema, x.split('|'))), updates), key=itemgetter("type", "name")) + 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'] + prefix = plugin['type'] + version = plugin['version'] if filename.endswith(".pyc"): name = filename[:filename.find("_")] @@ -200,22 +207,20 @@ class UpdateManager(Addon): newver = float(version) if not oldver: - msg = "New [%(type)s] %(name)s (v%(newver)s)" + msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)" elif newver > oldver: - msg = "New version of [%(type)s] %(name)s (v%(oldver)s -> v%(newver)s)" + msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)" else: continue - self.logInfo(_(msg) % { - 'type': type, - 'name': name, - 'oldver': oldver, - 'newver': newver, - }) - + self.logInfo(_(msg) % {'type' : type, + 'name' : name, + 'oldver': oldver, + 'newver': newver}) try: content = getURL(url % plugin) m = vre.search(content) + if m and m.group(2) == version: f = open(safe_join("userplugins", prefix, filename), "wb") f.write(content) @@ -223,8 +228,9 @@ class UpdateManager(Addon): updated.append((prefix, name)) else: raise Exception, _("Version mismatch") + except Exception, e: - self.logError(_("Error updating plugin: %s") % filename, e) + self.logError(_("Error updating plugin %s") % filename, e) if blacklist: blacklisted = map(lambda x: (x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]), blacklist) @@ -253,7 +259,6 @@ class UpdateManager(Addon): 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 diff --git a/pyload/plugins/internal/XFSPHoster.py b/pyload/plugins/internal/XFSPHoster.py index ae8065732..c3f8b6f41 100644 --- a/pyload/plugins/internal/XFSPHoster.py +++ b/pyload/plugins/internal/XFSPHoster.py @@ -2,360 +2,53 @@ import re -from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME -from random import random -from urllib import unquote -from urlparse import urlparse +from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo -from pyload.network.RequestFactory import getURL -from pyload.plugins.internal.CaptchaService import ReCaptcha, SolveMedia -from pyload.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, PluginParseError, replace_patterns -from pyload.utils import html_unescape - -class XFSPHoster(SimpleHoster): - """ - Common base for XFileSharingPro hosters like EasybytezCom, CramitIn, FiledinoCom... - Some hosters may work straight away when added to __pattern__ - However, most of them will NOT work because they are either down or running a customized version - """ - __name__ = "XFSPHoster" - __type__ = "hoster" - __version__ = "0.37" +class XFileSharingPro(XFSHoster): + __name__ = "XFileSharingPro" + __type__ = "hoster" + __version__ = "0.42" __pattern__ = r'^unmatchable$' - __description__ = """XFileSharingPro base hoster plugin""" - __authors__ = [("zoidberg", "zoidberg@mujmail.cz"), - ("stickell", "l.stickell@yahoo.it"), - ("Walter Purcaro", "vuolter@gmail.com")] - + __description__ = """XFileSharingPro dummy hoster plugin for hook""" + __license__ = "GPLv3" + __authors__ = [("Walter Purcaro", "vuolter@gmail.com")] - HOSTER_NAME = None - FILE_URL_REPLACEMENTS = [(r'/embed-(\w{12}).*', r'/\1')] #: support embedded files + def _log(self, type, args): + msg = " | ".join([str(a).strip() for a in args if a]) + logger = getattr(self.log, type) + logger("%s: %s: %s" % (self.__name__, self.HOSTER_NAME, msg or _("%s MARK" % type.upper()))) - COOKIES = [(HOSTER_NAME, "lang", "english")] - FILE_INFO_PATTERN = r'<tr><td align=right><b>Filename:</b></td><td nowrap>(?P<N>[^<]+)</td></tr>\s*.*?<small>\((?P<S>[^<]+)\)</small>' - FILE_NAME_PATTERN = r'<input type="hidden" name="fname" value="(?P<N>[^"]+)"' - FILE_SIZE_PATTERN = r'You have requested .*\((?P<S>[\d\.\,]+) ?(?P<U>\w+)?\)</font>' + def init(self): + super(XFileSharingPro, self).init() - OFFLINE_PATTERN = r'>\s*\w+ (Not Found|file (was|has been) removed)' - TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)' + self.__pattern__ = self.core.pluginManager.hosterPlugins[self.__name__]['pattern'] - WAIT_PATTERN = r'<span id="countdown_str">.*?>(\d+)</span>' + self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group(1).lower() + self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')]) - OVR_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)' - LINK_PATTERN = None #: final download url pattern + account = self.core.accountManager.getAccountPlugin(self.HOSTER_NAME) - CAPTCHA_URL_PATTERN = r'(http://[^"\']+?/captchas?/[^"\']+)' - CAPTCHA_DIV_PATTERN = r'>Enter code.*?<div.*?>(.+?)</div>' - RECAPTCHA_PATTERN = None - SOLVEMEDIA_PATTERN = None + if account and account.canUse(): + self.account = account + elif self.account: + self.account.HOSTER_DOMAIN = self.HOSTER_DOMAIN + else: + return - ERROR_PATTERN = r'class=["\']err["\'][^>]*>(.+?)</' + self.user, data = self.account.selectAccount() + self.req = self.account.getAccountRequest(self.user) + self.premium = self.account.isPremium(self.user) def setup(self): self.chunkLimit = 1 - - if self.__name__ == "XFSPHoster": - self.multiDL = True - self.__pattern__ = self.core.pluginManager.hosterPlugins[self.__name__]['pattern'] - self.HOSTER_NAME = re.match(self.__pattern__, self.pyfile.url).group(1).lower() - self.COOKIES = [(self.HOSTER_NAME, "lang", "english")] - else: - self.resumeDownload = self.multiDL = self.premium - - - def prepare(self): - """ Initialize important variables """ - if not self.HOSTER_NAME: - self.fail("Missing HOSTER_NAME") - - if not self.LINK_PATTERN: - pattr = r'(http://([^/]*?%s|\d+\.\d+\.\d+\.\d+)(:\d+)?(/d/|(?:/files)?/\d+/\w+/)[^"\'<]+)' - self.LINK_PATTERN = pattr % self.HOSTER_NAME - - if isinstance(self.COOKIES, list): - set_cookies(self.req.cj, self.COOKIES) - - self.captcha = None - self.errmsg = None - self.passwords = self.getPassword().splitlines() - - - def process(self, pyfile): - self.prepare() - - pyfile.url = replace_patterns(pyfile.url, self.FILE_URL_REPLACEMENTS) - - if not re.match(self.__pattern__, pyfile.url): - if self.premium: - self.handleOverriden() - else: - self.fail("Only premium users can download from other hosters with %s" % self.HOSTER_NAME) - else: - try: - # Due to a 0.4.9 core bug self.load would use cookies even if - # cookies=False. Workaround using getURL to avoid cookies. - # Can be reverted in 0.4.10 as the cookies bug has been fixed. - self.html = getURL(pyfile.url, decode=True, cookies=self.COOKIES) - self.file_info = self.getFileInfo() - except PluginParseError: - self.file_info = None - - self.location = self.getDirectDownloadLink() - - if not self.file_info: - pyfile.name = html_unescape(unquote(urlparse( - self.location if self.location else pyfile.url).path.split("/")[-1])) - - if self.location: - self.startDownload(self.location) - elif self.premium: - self.handlePremium() - else: - self.handleFree() - - - def getDirectDownloadLink(self): - """ Get download link for premium users with direct download enabled """ - self.req.http.lastURL = self.pyfile.url - - self.req.http.c.setopt(FOLLOWLOCATION, 0) - self.html = self.load(self.pyfile.url, decode=True) - self.header = self.req.http.header - self.req.http.c.setopt(FOLLOWLOCATION, 1) - - location = None - m = re.search(r"Location\s*:\s*(.*)", self.header, re.I) - if m and re.match(self.LINK_PATTERN, m.group(1)): - location = m.group(1).strip() - - return location - - - def handleFree(self): - url = self.getDownloadLink() - self.logDebug("Download URL: %s" % url) - self.startDownload(url) - - - def getDownloadLink(self): - for i in xrange(5): - self.logDebug("Getting download link: #%d" % i) - data = self.getPostParameters() - - self.req.http.c.setopt(FOLLOWLOCATION, 0) - self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True) - self.header = self.req.http.header - self.req.http.c.setopt(FOLLOWLOCATION, 1) - - m = re.search(r"Location\s*:\s*(.*)", self.header, re.I) - if m: - break - - m = re.search(self.LINK_PATTERN, self.html, re.S) - if m: - break - - else: - if self.errmsg and 'captcha' in self.errmsg: - self.fail("No valid captcha code entered") - else: - self.fail("Download link not found") - - return m.group(1) - - - def handlePremium(self): - self.html = self.load(self.pyfile.url, post=self.getPostParameters()) - m = re.search(self.LINK_PATTERN, self.html) - if m is None: - self.parseError('LINK_PATTERN not found') - self.startDownload(m.group(1)) - - - def handleOverriden(self): - #only tested with easybytez.com - self.html = self.load("http://www.%s/" % self.HOSTER_NAME) - action, inputs = self.parseHtmlForm('') - upload_id = "%012d" % int(random() * 10 ** 12) - action += upload_id + "&js_on=1&utype=prem&upload_type=url" - inputs['tos'] = '1' - inputs['url_mass'] = self.pyfile.url - inputs['up1oad_type'] = 'url' - - self.logDebug(self.HOSTER_NAME, action, inputs) - #wait for file to upload to easybytez.com - self.req.http.c.setopt(LOW_SPEED_TIME, 600) - self.html = self.load(action, post=inputs) - - action, inputs = self.parseHtmlForm('F1') - if not inputs: - self.parseError('TEXTAREA not found') - self.logDebug(self.HOSTER_NAME, inputs) - if inputs['st'] == 'OK': - self.html = self.load(action, post=inputs) - elif inputs['st'] == 'Can not leech file': - self.retry(max_tries=20, wait_time=3 * 60, reason=inputs['st']) - else: - self.fail(inputs['st']) - - #get easybytez.com link for uploaded file - m = re.search(self.OVR_LINK_PATTERN, self.html) - if m is None: - self.parseError('OVR_LINK_PATTERN not found') - self.pyfile.url = m.group(1) - header = self.load(self.pyfile.url, just_header=True) - if 'location' in header: # Direct link - self.startDownload(self.pyfile.url) - else: - self.retry() - - - def startDownload(self, link): - link = link.strip() - if self.captcha: - self.correctCaptcha() - self.logDebug("DIRECT LINK: %s" % link) - self.download(link, disposition=True) - - - def checkErrors(self): - m = re.search(self.ERROR_PATTERN, self.html) - if m: - self.errmsg = m.group(1) - self.logWarning(re.sub(r"<.*?>", " ", self.errmsg)) - - if 'wait' in self.errmsg: - wait_time = sum([int(v) * {"hour": 3600, "minute": 60, "second": 1}[u] for v, u in - re.findall(r'(\d+)\s*(hour|minute|second)', self.errmsg)]) - self.wait(wait_time, True) - elif 'captcha' in self.errmsg: - self.invalidCaptcha() - elif 'premium' in self.errmsg and 'require' in self.errmsg: - self.fail("File can be downloaded by premium users only") - elif 'limit' in self.errmsg: - self.wait(1 * 60 * 60, True) - self.retry(25) - elif 'countdown' in self.errmsg or 'Expired' in self.errmsg: - self.retry() - elif 'maintenance' in self.errmsg or 'maintainance' in self.errmsg: - self.tempOffline() - elif 'download files up to' in self.errmsg: - self.fail("File too large for free download") - else: - self.fail(self.errmsg) - - else: - self.errmsg = None - - return self.errmsg - - - def getPostParameters(self): - for _ in xrange(3): - if not self.errmsg: - self.checkErrors() - - if hasattr(self, "FORM_PATTERN"): - action, inputs = self.parseHtmlForm(self.FORM_PATTERN) - else: - action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")}) - - if not inputs: - action, inputs = self.parseHtmlForm('F1') - if not inputs: - if self.errmsg: - self.retry() - else: - self.parseError("Form not found") - - self.logDebug(self.HOSTER_NAME, inputs) - - if 'op' in inputs and inputs['op'] in ("download2", "download3"): - if "password" in inputs: - if self.passwords: - inputs['password'] = self.passwords.pop(0) - else: - self.fail("No or invalid passport") - - if not self.premium: - m = re.search(self.WAIT_PATTERN, self.html) - if m: - wait_time = int(m.group(1)) + 1 - self.setWait(wait_time, False) - else: - wait_time = 0 - - self.captcha = self.handleCaptcha(inputs) - - if wait_time: - self.wait() - - self.errmsg = None - return inputs - - else: - inputs['referer'] = self.pyfile.url - - if self.premium: - inputs['method_premium'] = "Premium Download" - if 'method_free' in inputs: - del inputs['method_free'] - else: - inputs['method_free'] = "Free Download" - if 'method_premium' in inputs: - del inputs['method_premium'] - - self.html = self.load(self.pyfile.url, post=inputs, ref=True) - self.errmsg = None - - else: - self.parseError('FORM: %s' % (inputs['op'] if 'op' in inputs else 'UNKNOWN')) - - - def handleCaptcha(self, inputs): - m = re.search(self.CAPTCHA_URL_PATTERN, self.html) - if m: - captcha_url = m.group(1) - inputs['code'] = self.decryptCaptcha(captcha_url) - return 1 - - m = re.search(self.CAPTCHA_DIV_PATTERN, self.html, re.DOTALL) - if m: - captcha_div = m.group(1) - 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", inputs['code'], numerals) - return 2 - - recaptcha = ReCaptcha(self) - try: - captcha_key = re.search(self.RECAPTCHA_PATTERN, self.html).group(1) - except: - captcha_key = recaptcha.detect_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 - - solvemedia = SolveMedia(self) - try: - captcha_key = re.search(self.SOLVEMEDIA_PATTERN, self.html).group(1) - except: - captcha_key = solvemedia.detect_key() - - if captcha_key: - inputs['adcopy_challenge'], inputs['adcopy_response'] = solvemedia.challenge(captcha_key) - return 4 - - return 0 + self.resumeDownload = self.premium + self.multiDL = True -getInfo = create_getInfo(XFSPHoster) +getInfo = create_getInfo(XFileSharingPro) |