summaryrefslogtreecommitdiffstats
path: root/pyload/plugins/internal
diff options
context:
space:
mode:
authorGravatar Walter Purcaro <vuolter@gmail.com> 2014-11-13 23:47:36 +0100
committerGravatar Walter Purcaro <vuolter@gmail.com> 2014-11-13 23:47:36 +0100
commitbdfa8104065831bf0eb2f6a022552e8de725ed47 (patch)
tree4a43864e1bb257c7737a765d62899244b825a859 /pyload/plugins/internal
parent[themes] Tiny code cosmetics (diff)
parent[Plugin] Some fixes by rlindner81 (diff)
downloadpyload-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.py13
-rw-r--r--pyload/plugins/internal/DeadCrypter.py9
-rw-r--r--pyload/plugins/internal/DeadHoster.py9
-rw-r--r--pyload/plugins/internal/MultiHoster.py39
-rw-r--r--pyload/plugins/internal/SimpleCrypter.py135
-rw-r--r--pyload/plugins/internal/SimpleHoster.py289
-rw-r--r--pyload/plugins/internal/UnRar.py13
-rw-r--r--pyload/plugins/internal/UnZip.py8
-rw-r--r--pyload/plugins/internal/UpdateManager.py73
-rw-r--r--pyload/plugins/internal/XFSPHoster.py367
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)