summaryrefslogtreecommitdiffstats
path: root/module/plugins/internal/Hoster.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/plugins/internal/Hoster.py')
-rw-r--r--module/plugins/internal/Hoster.py502
1 files changed, 59 insertions, 443 deletions
diff --git a/module/plugins/internal/Hoster.py b/module/plugins/internal/Hoster.py
index bc340e78f..d1b894c6f 100644
--- a/module/plugins/internal/Hoster.py
+++ b/module/plugins/internal/Hoster.py
@@ -2,19 +2,10 @@
from __future__ import with_statement
-import inspect
-import mimetypes
import os
-import random
-import time
-import urlparse
-
-from module.plugins.internal.Captcha import Captcha
-from module.plugins.internal.Plugin import (Plugin, Abort, Fail, Reconnect, Retry, Skip,
- chunks, decode, encode, exists, fixurl,
- parse_html_form, parse_html_tag_attr_value, parse_name,
- replace_patterns, seconds_to_midnight,
- set_cookie, set_cookies, timestamp)
+
+from module.plugins.internal.Base import Base, check_abort, create_getInfo, getInfo
+from module.plugins.internal.Plugin import Fail, Retry, encode, exists, fixurl, parse_name
from module.utils import fs_decode, fs_encode, save_join as fs_join, save_path as safe_filename
@@ -24,40 +15,17 @@ def parse_fileInfo(klass, url="", html=""):
return encode(info['name']), info['size'], info['status'], info['url']
-#@TODO: Remove in 0.4.10
-def getInfo(urls):
- #: result = [ .. (name, size, status, url) .. ]
- pass
-
-
-#@TODO: Remove in 0.4.10
-def create_getInfo(klass):
- def get_info(urls):
- for url in urls:
- if hasattr(klass, "URL_REPLACEMENTS"):
- url = replace_patterns(url, klass.URL_REPLACEMENTS)
- yield parse_fileInfo(klass, url)
-
- return get_info
-
-
-#@NOTE: `check_abort` decorator
-def check_abort(fn):
-
- def wrapper(self, *args, **kwargs):
- self.check_abort()
- return fn(self, *args, **kwargs)
-
- return wrapper
-
-class Hoster(Plugin):
+class Hoster(Base):
__name__ = "Hoster"
__type__ = "hoster"
- __version__ = "0.31"
+ __version__ = "0.32"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
+ __config__ = [("use_premium" , "bool", "Use premium account if available" , True),
+ ("fallback_premium", "bool", "Fallback to free download if premium fails", True),
+ ("chk_filesize" , "bool", "Check file size" , True)]
__description__ = """Base hoster plugin"""
__license__ = "GPLv3"
@@ -65,107 +33,39 @@ class Hoster(Plugin):
def __init__(self, pyfile):
- self._init(pyfile.m.core)
-
- #: Engage wan reconnection
- self.wantReconnect = False #@TODO: Change to `want_reconnect` in 0.4.10
+ super(Base, self).__init__(pyfile)
#: Enable simultaneous processing of multiple downloads
- self.multiDL = True #@TODO: Change to `multi_dl` in 0.4.10
self.limitDL = 0 #@TODO: Change to `limit_dl` in 0.4.10
- #: time.time() + wait in seconds
- self.wait_until = 0
- self.waiting = False
-
- #: Account handler instance, see :py:class:`Account`
- self.account = None
- self.user = None #@TODO: Remove in 0.4.10
-
- #: Associated pyfile instance, see `PyFile`
- self.pyfile = pyfile
-
- self.thread = None #: Holds thread in future
-
#: Location where the last call to download was saved
- self.last_download = ""
+ self.last_download = None
#: Re match of the last call to `checkDownload`
self.last_check = None
- #: Js engine, see `JsEngine`
- self.js = self.pyload.js
-
- #: Captcha stuff
- self.captcha = Captcha(self)
-
- #: Some plugins store html code here
- self.html = None
-
- #: Dict of the amount of retries already made
- self.retries = {}
- self.force_free = False #@TODO: Recheck in 0.4.10
+ #: Restart flag
+ self.rst_free = False #@TODO: Recheck in 0.4.10
self._setup()
self.init()
- def _log(self, level, plugintype, pluginname, messages):
- log = getattr(self.pyload.log, level)
- msg = u" | ".join(decode(a).strip() for a in messages if a)
- log("%(plugintype)s %(pluginname)s[%(id)s]: %(msg)s"
- % {'plugintype': plugintype.upper(),
- 'pluginname': pluginname,
- 'id' : self.pyfile.id,
- 'msg' : msg})
-
-
- @classmethod
- def get_info(cls, url="", html=""):
- url = fixurl(url, unquote=True)
- return {'name' : parse_name(url),
- 'size' : 0,
- 'status': 3 if url else 8,
- 'url' : url}
-
-
- def init(self):
- """
- Initialize the plugin (in addition to `__init__`)
- """
- pass
-
-
- def setup(self):
- """
- Setup for enviroment and other things, called before downloading (possibly more than one time)
- """
- pass
+ def _setup(self):
+ super(Base, self)._setup()
+ self.last_download = None
+ self.last_check = None
+ self.rst_free = False
- def _setup(self):
- #@TODO: Remove in 0.4.10
- self.html = ""
- self.last_download = ""
- self.pyfile.error = ""
- try:
- self.req.close()
- except Exception:
- pass
-
- if self.account:
- self.req = self.pyload.requestFactory.getRequest(self.__name__, self.account.user)
- self.chunk_limit = -1 #: -1 for unlimited
- self.resume_download = True
- self.premium = self.account.premium
+ def load_account(self):
+ if self.rst_free:
+ self.account = False
+ self.user = None #@TODO: Remove in 0.4.10
else:
- self.req = self.pyload.requestFactory.getRequest(self.__name__)
- self.chunk_limit = 1
- self.resume_download = False
- self.premium = False
-
- return self.setup()
+ super(Base, self).load_account()
+ # self.rst_free = False
def _process(self, thread):
@@ -174,229 +74,44 @@ class Hoster(Plugin):
"""
self.thread = thread
- if self.force_free:
- self.account = False
- else:
- self.load_account() #@TODO: Move to PluginThread in 0.4.10
- self.force_free = False
-
self._setup()
# self.pyload.hookManager.downloadPreparing(self.pyfile) #@TODO: Recheck in 0.4.10
- self.pyfile.setStatus("starting")
-
self.check_abort()
- self.log_debug("PROCESS URL " + self.pyfile.url, "PLUGIN VERSION %s" % self.__version__)
- return self.process(self.pyfile)
-
-
- #: Deprecated method, use `_process` instead (Remove in 0.4.10)
- def preprocessing(self, *args, **kwargs):
- return self._process(*args, **kwargs)
-
-
- def load_account(self):
- if not self.account:
- self.account = self.pyload.accountManager.getAccountPlugin(self.__name__)
-
- if not self.account:
- self.account = False
- self.user = None #@TODO: Remove in 0.4.10
-
- else:
- self.account.choose()
- self.user = self.account.user #@TODO: Remove in 0.4.10
- if self.account.user is None:
- self.account = False
-
-
- def process(self, pyfile):
- """
- The 'main' method of every plugin, you **have to** overwrite it
- """
- raise NotImplementedError
-
-
- def set_reconnect(self, reconnect):
- self.log_debug("RECONNECT %s required" % ("" if reconnect else "not"),
- "Previous wantReconnect: %s" % self.wantReconnect)
- self.wantReconnect = bool(reconnect)
-
-
- def set_wait(self, seconds, reconnect=None):
- """
- Set a specific wait time later used with `wait`
-
- :param seconds: wait time in seconds
- :param reconnect: True if a reconnect would avoid wait time
- """
- wait_time = max(int(seconds), 1)
- wait_until = time.time() + wait_time + 1
-
- self.log_debug("WAIT set to %d seconds" % wait_time,
- "Previous waitUntil: %f" % self.pyfile.waitUntil)
-
- self.pyfile.waitUntil = wait_until
-
- if reconnect is not None:
- self.set_reconnect(reconnect)
-
-
- def wait(self, seconds=None, reconnect=None):
- """
- Waits the time previously set
- """
- pyfile = self.pyfile
-
- if seconds is not None:
- self.set_wait(seconds)
-
- if reconnect is not None:
- self.set_reconnect(reconnect)
-
- self.waiting = True
-
- status = pyfile.status #@NOTE: Recheck in 0.4.10
- pyfile.setStatus("waiting")
-
- self.log_info(_("Waiting %d seconds...") % pyfile.waitUntil - time.time())
-
- if self.wantReconnect:
- self.log_info(_("Requiring reconnection..."))
- if self.account:
- self.log_warning("Ignore reconnection due logged account")
-
- if not self.wantReconnect or self.account:
- while pyfile.waitUntil > time.time():
- self.check_abort()
- time.sleep(2)
-
- else:
- while pyfile.waitUntil > time.time():
- self.check_abort()
- self.thread.m.reconnecting.wait(1)
-
- if self.thread.m.reconnecting.isSet():
- self.waiting = False
- self.wantReconnect = False
- raise Reconnect
-
- time.sleep(2)
-
- self.waiting = False
- pyfile.status = status #@NOTE: Recheck in 0.4.10
-
-
- def skip(self, msg=""):
- """
- Skip and give msg
- """
- raise Skip(encode(msg or self.pyfile.error or self.pyfile.pluginname)) #@TODO: Remove `encode` in 0.4.10
-
-
- #@TODO: Remove in 0.4.10
- def fail(self, msg):
- """
- Fail and give msg
- """
- msg = msg.strip()
-
- if msg:
- self.pyfile.error = msg
- else:
- msg = self.pyfile.error or (self.info['error'] if 'error' in self.info else self.pyfile.getStatusName())
-
- raise Fail(encode(msg)) #@TODO: Remove `encode` in 0.4.10
-
-
- def error(self, msg="", type=_("Parse")):
- type = _("%s error") % type.strip().capitalize() if type else _("Unknown")
- msg = _("%(type)s: %(msg)s | Plugin may be out of date"
- % {'type': type, 'msg': msg or self.pyfile.error})
-
- self.fail(msg)
-
-
- def abort(self, msg=""):
- """
- Abort and give msg
- """
- if msg: #@TODO: Remove in 0.4.10
- self.pyfile.error = encode(msg)
-
- raise Abort
-
-
- #@TODO: Recheck in 0.4.10
- def offline(self, msg=""):
- """
- Fail and indicate file is offline
- """
- self.fail("offline")
-
-
- #@TODO: Recheck in 0.4.10
- def temp_offline(self, msg=""):
- """
- Fail and indicates file ist temporary offline, the core may take consequences
- """
- self.fail("temp. offline")
-
+ self.pyfile.setStatus("starting")
- def retry(self, attemps=5, delay=1, msg=""):
- """
- Retries and begin again from the beginning
+ try:
+ self.log_debug("PROCESS URL " + self.pyfile.url, "PLUGIN VERSION %s" % self.__version__) #@TODO: Remove in 0.4.10
+ self.process(self.pyfile)
- :param attemps: number of maximum retries
- :param delay: time to wait in seconds
- :param msg: msg for retrying, will be passed to fail if attemps value was reached
- """
- id = inspect.currentframe().f_back.f_lineno
- if id not in self.retries:
- self.retries[id] = 0
+ self.check_abort()
- if 0 < attemps <= self.retries[id]:
- self.fail(msg or _("Max retries reached"))
+ self.log_debug("CHECK DOWNLOAD") #@TODO: Recheck in 0.4.10
+ self._check_download()
- self.wait(delay, False)
+ except Fail, e: #@TODO: Move to PluginThread in 0.4.10
+ if self.get_config('fallback_premium', True) and self.premium:
+ self.log_warning(_("Premium download failed"), e)
+ self.restart()
- self.retries[id] += 1
- raise Retry(encode(msg)) #@TODO: Remove `encode` in 0.4.10
+ else:
+ raise Fail(e)
- def restart(self, msg=None, nopremium=False):
+ def restart(self, msg="", premium=False):
if not msg:
- msg = _("Fallback to free download") if nopremium else _("Restart")
+ msg = _("Simple restart") if premium else _("Fallback to free download")
- if nopremium:
+ if not premium:
if self.premium:
- self.force_free = True
+ self.rst_free = True
else:
self.fail("%s | %s" % (msg, _("Download was already free")))
raise Retry(encode(msg)) #@TODO: Remove `encode` in 0.4.10
- def fixurl(self, url, baseurl=None, unquote=None):
- url = fixurl(url)
-
- if not baseurl:
- baseurl = fixurl(self.pyfile.url)
-
- if not urlparse.urlparse(url).scheme:
- url_p = urlparse.urlparse(baseurl)
- baseurl = "%s://%s" % (url_p.scheme, url_p.netloc)
- url = urlparse.urljoin(baseurl, url)
-
- return fixurl(url, unquote)
-
-
- @check_abort
- def load(self, *args, **kwargs):
- return super(Hoster, self).load(*args, **kwargs)
-
-
@check_abort
def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=True):
"""
@@ -415,7 +130,7 @@ class Hoster(Plugin):
self.log_debug("DOWNLOAD URL " + url,
*["%s=%s" % (key, val) for key, val in locals().items() if key not in ("self", "url", "_[1]")])
- url = self.fixurl(url, unquote=True)
+ url = self.fixurl(url)
self.pyfile.name = parse_name(self.pyfile.name) #: Safe check
@@ -475,27 +190,7 @@ class Hoster(Plugin):
self.last_download = filename
- return self.last_download
-
-
- def check_abort(self):
- if not self.pyfile.abort:
- return
-
- if self.pyfile.status is 8:
- self.fail()
-
- elif self.pyfile.status is 4:
- self.skip(self.pyfile.statusname)
-
- elif self.pyfile.status is 1:
- self.offline()
-
- elif self.pyfile.status is 6:
- self.temp_offline()
-
- else:
- self.abort()
+ return filename
def check_filesize(self, file_size, size_tolerance=1024):
@@ -537,7 +232,7 @@ class Hoster(Plugin):
:return: dictionary key of the first rule that matched
"""
do_delete = False
- last_download = fs_encode(self.last_download)
+ last_download = fs_encode(self.last_download) #@TODO: Recheck in 0.4.10
if not self.last_download or not exists(last_download):
self.fail(self.pyfile.error or _("No file downloaded"))
@@ -558,7 +253,7 @@ class Hoster(Plugin):
elif hasattr(rule, "search"):
m = rule.search(content)
- if m:
+ if m is not None:
do_delete = True
self.last_check = m
return name
@@ -575,11 +270,25 @@ class Hoster(Plugin):
self.last_download = "" #: Recheck in 0.4.10
+ def _check_download(self):
+ if self.captcha.task and not self.last_download:
+ self.retry_captcha()
+
+ elif self.check_file({'Empty file': re.compile(r'\A((.|)(\2|\s)*)\Z')},
+ delete=True):
+ self.error(_("Empty file"))
+
+ elif self.get_config('chk_filesize', False) and self.info.get('size'):
+ # 10485760 is 10MB, tolerance is used when comparing displayed size on the hoster website to real size
+ # For example displayed size can be 1.46GB for example, but real size can be 1.4649853GB
+ self.check_filesize(self.info['size'], size_tolerance=10485760)
+
+
def check_traffic(self):
if not self.account:
return True
- traffic = self.account.get_data(refresh=True)['trafficleft']
+ traffic = self.account.get_data('trafficleft')
if traffic is None:
return False
@@ -632,96 +341,3 @@ class Hoster(Plugin):
def checkForSameFiles(self, *args, **kwargs):
if self.pyload.config.get("download", "skip_existing"):
return self.check_filedupe()
-
-
- def direct_link(self, url, follow_location=None):
- link = ""
-
- if follow_location is None:
- redirect = 1
-
- elif type(follow_location) is int:
- redirect = max(follow_location, 1)
-
- else:
- redirect = self.get_config("maxredirs", 10, "UserAgentSwitcher")
-
- for i in xrange(redirect):
- try:
- self.log_debug("Redirect #%d to: %s" % (i, url))
- header = self.load(url, just_header=True)
-
- except Exception: #: Bad bad bad... rewrite this part in 0.4.10
- res = self.load(url,
- just_header=True,
- req=self.pyload.requestFactory.getRequest(self.__name__))
-
- header = {'code': req.code}
- for line in res.splitlines():
- line = line.strip()
- if not line or ":" not in line:
- continue
-
- key, none, value = line.partition(":")
- key = key.lower().strip()
- value = value.strip()
-
- if key in header:
- if type(header[key]) is list:
- header[key].append(value)
- else:
- header[key] = [header[key], value]
- else:
- header[key] = value
-
- if 'content-disposition' in header:
- link = url
-
- elif header.get('location'):
- location = self.fixurl(header['location'], url)
-
- if header.get('code') == 302:
- link = location
-
- if follow_location:
- url = location
- continue
-
- else:
- extension = os.path.splitext(parse_name(url))[-1]
-
- if header.get('content-type'):
- mimetype = header['content-type'].split(';')[0].strip()
-
- elif extension:
- mimetype = mimetypes.guess_type(extension, False)[0] or "application/octet-stream"
-
- else:
- mimetype = ""
-
- if mimetype and (link or 'html' not in mimetype):
- link = url
- else:
- link = ""
-
- break
-
- else:
- try:
- self.log_error(_("Too many redirects"))
-
- except Exception:
- pass
-
- return link
-
-
- def parse_html_form(self, attr_str="", input_names={}):
- return parse_html_form(attr_str, self.html, input_names)
-
-
- def get_password(self):
- """
- Get the password the user provided in the package
- """
- return self.pyfile.package().password or ""