summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--module/plugins/hoster/CzshareCom.py6
-rw-r--r--module/plugins/hoster/FastshareCz.py6
-rw-r--r--module/plugins/hoster/FileserveCom.py4
-rw-r--r--module/plugins/hoster/MegasharesCom.py4
-rw-r--r--module/plugins/hoster/OpenloadIo.py10
-rw-r--r--module/plugins/internal/Base.py493
-rw-r--r--module/plugins/internal/Crypter.py28
-rw-r--r--module/plugins/internal/Hoster.py502
-rw-r--r--module/plugins/internal/MultiCrypter.py5
-rw-r--r--module/plugins/internal/MultiHoster.py12
-rw-r--r--module/plugins/internal/Plugin.py45
-rw-r--r--module/plugins/internal/SimpleCrypter.py5
-rw-r--r--module/plugins/internal/SimpleHoster.py134
-rw-r--r--module/plugins/internal/XFSAccount.py18
-rw-r--r--module/plugins/internal/XFSCrypter.py20
-rw-r--r--module/plugins/internal/XFSHoster.py21
16 files changed, 716 insertions, 597 deletions
diff --git a/module/plugins/hoster/CzshareCom.py b/module/plugins/hoster/CzshareCom.py
index ad2559e13..d678f25fa 100644
--- a/module/plugins/hoster/CzshareCom.py
+++ b/module/plugins/hoster/CzshareCom.py
@@ -12,7 +12,7 @@ from module.utils import parseFileSize as parse_size
class CzshareCom(SimpleHoster):
__name__ = "CzshareCom"
__type__ = "hoster"
- __version__ = "1.03"
+ __version__ = "1.04"
__status__ = "testing"
__pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/(\d+/|download\.php\?).+'
@@ -73,7 +73,7 @@ class CzshareCom(SimpleHoster):
except Exception, e:
self.log_error(e)
- self.restart(nopremium=True)
+ self.restart()
#: Download the file, destination is determined by pyLoad
self.download("http://sdilej.cz/profi_down.php", post=inputs, disposition=True)
@@ -145,7 +145,7 @@ class CzshareCom(SimpleHoster):
self.fail(_("File not available - try later"))
elif check == "credit":
- self.restart(nopremium=True)
+ self.restart()
elif check == "multi-dl":
self.wait(5 * 60, 12, _("Download limit reached"))
diff --git a/module/plugins/hoster/FastshareCz.py b/module/plugins/hoster/FastshareCz.py
index 2919ebce2..b05edfb27 100644
--- a/module/plugins/hoster/FastshareCz.py
+++ b/module/plugins/hoster/FastshareCz.py
@@ -9,7 +9,7 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
class FastshareCz(SimpleHoster):
__name__ = "FastshareCz"
__type__ = "hoster"
- __version__ = "0.34"
+ __version__ = "0.35"
__status__ = "testing"
__pattern__ = r'http://(?:www\.)?fastshare\.cz/\d+/.+'
@@ -42,7 +42,7 @@ class FastshareCz(SimpleHoster):
if self.CREDIT_ERROR in self.html:
errmsg = self.info['error'] = _("Not enough traffic left")
self.log_warning(errmsg)
- self.restart(nopremium=True)
+ self.restart()
self.info.pop('error', None)
@@ -73,7 +73,7 @@ class FastshareCz(SimpleHoster):
self.retry_captcha()
elif check == "credit":
- self.restart(nopremium=True)
+ self.restart()
return super(FastshareCz, self).check_download()
diff --git a/module/plugins/hoster/FileserveCom.py b/module/plugins/hoster/FileserveCom.py
index 903e10349..277d4f2a8 100644
--- a/module/plugins/hoster/FileserveCom.py
+++ b/module/plugins/hoster/FileserveCom.py
@@ -34,7 +34,7 @@ def check_file(plugin, urls):
class FileserveCom(Hoster):
__name__ = "FileserveCom"
__type__ = "hoster"
- __version__ = "0.60"
+ __version__ = "0.61"
__status__ = "testing"
__pattern__ = r'http://(?:www\.)?fileserve\.com/file/(?P<ID>[^/]+)'
@@ -193,7 +193,7 @@ class FileserveCom(Hoster):
elif res['error_code'] in ["305", "500"]:
self.temp_offline()
elif res['error_code'] in ["403", "605"]:
- self.restart(nopremium=True)
+ self.restart()
elif res['error_code'] in ["606", "607", "608"]:
self.offline()
else:
diff --git a/module/plugins/hoster/MegasharesCom.py b/module/plugins/hoster/MegasharesCom.py
index 04fae27b4..b183b882d 100644
--- a/module/plugins/hoster/MegasharesCom.py
+++ b/module/plugins/hoster/MegasharesCom.py
@@ -9,7 +9,7 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
class MegasharesCom(SimpleHoster):
__name__ = "MegasharesCom"
__type__ = "hoster"
- __version__ = "0.31"
+ __version__ = "0.32"
__status__ = "testing"
__pattern__ = r'http://(?:www\.)?(d\d{2}\.)?megashares\.com/((index\.php)?\?d\d{2}=|dl/)\w+'
@@ -70,7 +70,7 @@ class MegasharesCom(SimpleHoster):
if 'Thank you for reactivating your passport' in res:
self.captcha.correct()
- self.restart()
+ self.restart(premium=True)
else:
self.retry_captcha(msg=_("Failed to reactivate passport"))
diff --git a/module/plugins/hoster/OpenloadIo.py b/module/plugins/hoster/OpenloadIo.py
index 1ebc12ad0..6213a9c09 100644
--- a/module/plugins/hoster/OpenloadIo.py
+++ b/module/plugins/hoster/OpenloadIo.py
@@ -10,7 +10,7 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
class OpenloadIo(SimpleHoster):
__name__ = "OpenloadIo"
__type__ = "hoster"
- __version__ = "0.06"
+ __version__ = "0.07"
__status__ = "testing"
__pattern__ = r'https?://(?:www\.)?openload\.(?:co|io)/f/([\w-_]+)'
@@ -23,11 +23,11 @@ class OpenloadIo(SimpleHoster):
# The API reference, that this implementation uses is available at https://openload.co/api
API_URL = 'https://api.openload.co/1'
- FILE_ID_PATTERN = '/f/([\w-_]+)'
+ _FILE_ID_PATTERN = '/f/([\w-_]+)'
- DOWNLOAD_TICKET_URI_PATTERN = '/file/dlticket?file={0}'
- DOWNLOAD_FILE_URI_PATTERN = '/file/dl?file={0}&ticket={1}'
- FILE_INFO_URI_PATTERN = '/file/info?file={0}'
+ _DOWNLOAD_TICKET_URI_PATTERN = '/file/dlticket?file={0}'
+ _DOWNLOAD_FILE_URI_PATTERN = '/file/dl?file={0}&ticket={1}'
+ _FILE_INFO_URI_PATTERN = '/file/info?file={0}'
@classmethod
diff --git a/module/plugins/internal/Base.py b/module/plugins/internal/Base.py
new file mode 100644
index 000000000..4235cf94d
--- /dev/null
+++ b/module/plugins/internal/Base.py
@@ -0,0 +1,493 @@
+# -*- coding: utf-8 -*-
+
+import inspect
+import mimetypes
+import os
+import time
+import urlparse
+
+from module.plugins.internal.Captcha import Captcha
+from module.plugins.internal.Plugin import (Plugin, Abort, Fail, Reconnect, Retry, Skip,
+ decode, encode, fixurl, parse_html_form,
+ parse_name, replace_patterns)
+
+
+#@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 Base(Plugin):
+ __name__ = "Base"
+ __type__ = "base"
+ __version__ = "0.01"
+ __status__ = "testing"
+
+ __pattern__ = r'^unmatchable$'
+ __config__ = [("use_premium", "bool", "Use premium account if available", True)]
+
+ __description__ = """Base plugin for Hoster and Crypter"""
+ __license__ = "GPLv3"
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
+
+
+ def __init__(self, pyfile):
+ self._init(pyfile.m.core)
+
+ #: Engage wan reconnection
+ self.wantReconnect = False #@TODO: Change to `want_reconnect` in 0.4.10
+
+ #: Enable simultaneous processing of multiple downloads
+ self.multiDL = True #@TODO: Change to `multi_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
+
+ #: 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 = {}
+
+
+ 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)
+ info = {'name' : parse_name(url),
+ 'size' : 0,
+ 'status': 3 if url else 8,
+ 'url' : url}
+
+ return info
+
+
+ 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):
+ #@TODO: Remove in 0.4.10
+ self.html = ""
+ self.pyfile.error = ""
+ self.last_html = None
+
+ if self.get_config('use_premium', True):
+ self.load_account() #@TODO: Move to PluginThread in 0.4.10
+ else:
+ self.account = False
+ self.user = None #@TODO: Remove in 0.4.10
+
+ 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
+ else:
+ self.req = self.pyload.requestFactory.getRequest(self.__name__)
+ self.chunk_limit = 1
+ self.resume_download = False
+ self.premium = False
+
+ self.setup()
+
+
+ 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, thread):
+ """
+ Handles important things to do before starting
+ """
+ self.thread = thread
+
+ self._setup()
+
+ # self.pyload.hookManager.downloadPreparing(self.pyfile) #@TODO: Recheck in 0.4.10
+ self.check_abort()
+
+ self.pyfile.setStatus("starting")
+
+ self.log_debug("PROCESS URL " + self.pyfile.url, "PLUGIN VERSION %s" % self.__version__)
+ 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 process(self, pyfile):
+ """
+ The "main" method of every hoster 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")
+
+
+ def retry(self, attemps=5, wait=1, msg=""):
+ """
+ Retries and begin again from the beginning
+
+ :param attemps: number of maximum retries
+ :param wait: time to wait in seconds before retry
+ :param msg: message 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
+
+ if 0 < attemps <= self.retries[id]:
+ self.fail(msg or _("Max retries reached"))
+
+ self.wait(wait, False)
+
+ self.retries[id] += 1
+ raise Retry(encode(msg)) #@TODO: Remove `encode` in 0.4.10
+
+
+ def retry_captcha(self, attemps=10, wait=1, msg=_("Wrong captcha")):
+ self.captcha.invalid()
+ self.retry(attemps, wait, msg)
+
+
+ def fixurl(self, url, baseurl=None, unquote=True):
+ 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)
+
+
+ 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()
+
+
+ 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 ""
+
+
+ def clean(self):
+ """
+ Clean everything and remove references
+ """
+ super(Base, self).clean()
+
+ for attr in ("account", "html", "pyfile", "thread"):
+ if hasattr(self, attr):
+ setattr(self, attr, None)
diff --git a/module/plugins/internal/Crypter.py b/module/plugins/internal/Crypter.py
index ad5bcc74e..069ffb589 100644
--- a/module/plugins/internal/Crypter.py
+++ b/module/plugins/internal/Crypter.py
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
-from module.plugins.internal.Hoster import Hoster, parse_name
+from module.plugins.internal.Base import Base, parse_name
from module.utils import save_path as safe_filename
-class Crypter(Hoster):
+class Crypter(Base):
__name__ = "Crypter"
__type__ = "crypter"
- __version__ = "0.09"
+ __version__ = "0.10"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides pyload.config.get("general", "folder_per_package")
+ __config__ = [("use_premium" , "bool", "Use premium account if available" , True),
+ ("use_subfolder" , "bool", "Save package to subfolder" , True), #: Overrides pyload.config.get("general", "folder_per_package")
("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
__description__ = """Base decrypter plugin"""
@@ -19,11 +20,8 @@ class Crypter(Hoster):
__authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
- html = None #: Last html loaded #@TODO: Move to Hoster
-
-
def __init__(self, pyfile):
- super(Crypter, self).__init__(pyfile)
+ super(Base, self).__init__(pyfile)
#: Put all packages here. It's a list of tuples like: ( name, [list of links], folder )
self.packages = []
@@ -31,6 +29,16 @@ class Crypter(Hoster):
#: List of urls, pyLoad will generate packagenames
self.urls = []
+ self._setup()
+ self.init()
+
+
+ def _setup(self):
+ super(Base, self)._setup()
+
+ self.packages = []
+ self.urls = []
+
def process(self, pyfile):
"""
@@ -48,6 +56,9 @@ class Crypter(Hoster):
def decrypt(self, pyfile):
+ """
+ The "main" method of every crypter plugin, you **have to** overwrite it
+ """
raise NotImplementedError
@@ -77,6 +88,7 @@ class Crypter(Hoster):
"Saved to folder: %s" % folder if folder else "Saved to download folder")
links = map(self.fixurl, links)
+
pid = self.pyload.api.addPackage(name, links, package_queue)
if package_password:
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 ""
diff --git a/module/plugins/internal/MultiCrypter.py b/module/plugins/internal/MultiCrypter.py
index ae8785116..9d4ac3ab9 100644
--- a/module/plugins/internal/MultiCrypter.py
+++ b/module/plugins/internal/MultiCrypter.py
@@ -10,8 +10,9 @@ class MultiCrypter(SimpleCrypter):
__status__ = "testing"
__pattern__ = r'^unmatchable$'
- __config__ = [("use_subfolder" , "bool", "Save package to subfolder" , True),
- ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)]
+ __config__ = [("use_premium" , "bool", "Use premium account if available" , True),
+ ("use_subfolder" , "bool", "Save package to subfolder" , True),
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
__description__ = """Multi decrypter plugin"""
__license__ = "GPLv3"
diff --git a/module/plugins/internal/MultiHoster.py b/module/plugins/internal/MultiHoster.py
index fbfab1ade..d7d3c5ccd 100644
--- a/module/plugins/internal/MultiHoster.py
+++ b/module/plugins/internal/MultiHoster.py
@@ -9,12 +9,14 @@ from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, r
class MultiHoster(SimpleHoster):
__name__ = "MultiHoster"
__type__ = "hoster"
- __version__ = "0.51"
+ __version__ = "0.52"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
- __config__ = [("use_premium" , "bool", "Use premium account if available" , True),
- ("revertfailed", "bool", "Revert to standard download if fails", True)]
+ __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),
+ ("revertfailed" , "bool", "Revert to standard download if fails" , True)]
__description__ = """Multi hoster plugin"""
__license__ = "GPLv3"
@@ -100,7 +102,7 @@ class MultiHoster(SimpleHoster):
except Fail, e: #@TODO: Move to PluginThread in 0.4.10
if self.premium:
self.log_warning(_("Premium download failed"))
- self.restart(nopremium=True)
+ self.restart()
elif self.get_config("revertfailed", True) \
and "new_module" in self.pyload.pluginManager.hosterPlugins[self.__name__]:
@@ -116,7 +118,7 @@ class MultiHoster(SimpleHoster):
hdict['new_module'] = tmp_module
hdict['new_name'] = tmp_name
- self.restart(_("Revert to original hoster plugin"))
+ self.restart(_("Revert to original hoster plugin"), premium=True)
else:
raise Fail(encode(e)) #@TODO: Remove `encode` in 0.4.10
diff --git a/module/plugins/internal/Plugin.py b/module/plugins/internal/Plugin.py
index 6f3e52962..55574aae0 100644
--- a/module/plugins/internal/Plugin.py
+++ b/module/plugins/internal/Plugin.py
@@ -77,11 +77,6 @@ def parse_name(string):
#@TODO: Move to utils in 0.4.10
-def timestamp():
- return int(time.time() * 1000)
-
-
-#@TODO: Move to utils in 0.4.10
def which(program):
"""
Works exactly like the unix command which
@@ -184,7 +179,7 @@ def chunks(iterable, size):
class Plugin(object):
__name__ = "Plugin"
__type__ = "plugin"
- __version__ = "0.38"
+ __version__ = "0.40"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
@@ -206,9 +201,10 @@ class Plugin(object):
def _init(self, core):
- self.pyload = core
- self.info = {} #: Provide information in dict here
- self.req = None #: Browser instance, see `network.Browser`
+ self.pyload = core
+ self.info = {} #: Provide information in dict here
+ self.req = None #: Browser instance, see `network.Browser`
+ self.last_html = None
def init(self):
@@ -355,7 +351,7 @@ class Plugin(object):
self.log_debug("LOAD URL " + url,
*["%s=%s" % (key, val) for key, val in locals().items() if key not in ("self", "url", "_[1]")])
- url = fixurl(url, unquote=True) #: Recheck in 0.4.10
+ url = fixurl(url) #: Recheck in 0.4.10
if req is None:
req = self.req or self.pyload.requestFactory.getRequest(self.__name__)
@@ -364,15 +360,17 @@ class Plugin(object):
if isinstance(cookies, list):
set_cookies(req.cj, cookies)
- res = req.load(url, get, post, ref, bool(cookies), just_header, multipart, decode is True) #@TODO: Fix network multipart in 0.4.10
+ html = req.load(url, get, post, ref, bool(cookies), just_header, multipart, decode is True) #@TODO: Fix network multipart in 0.4.10
#@TODO: Move to network in 0.4.10
if decode:
- res = html_unescape(res)
+ html = html_unescape(html)
#@TODO: Move to network in 0.4.10
if isinstance(decode, basestring):
- res = _decode(res, decode) #@NOTE: Use `utils.decode()` in 0.4.10
+ html = _decode(html, decode) #@NOTE: Use `utils.decode()` in 0.4.10
+
+ self.last_html = html
if self.pyload.debug:
frame = inspect.currentframe()
@@ -383,15 +381,18 @@ class Plugin(object):
with open(framefile, "wb") as f:
del frame #: Delete the frame or it wont be cleaned
- f.write(encode(res))
+ f.write(encode(html))
except IOError, e:
self.log_error(e)
- if just_header:
- #: Parse header
+ if not just_header:
+ return html
+
+ else:
+ #@TODO: Move to network in 0.4.10
header = {'code': req.code}
- for line in res.splitlines():
+ for line in html.splitlines():
line = line.strip()
if not line or ":" not in line:
continue
@@ -407,14 +408,13 @@ class Plugin(object):
header[key] = [header[key], value]
else:
header[key] = value
- res = header
- return res
+ return header
def clean(self):
"""
- Clean everything and remove references
+ Remove references
"""
try:
self.req.clearCookies()
@@ -423,6 +423,5 @@ class Plugin(object):
except Exception:
pass
- for attr in ("account", "html", "pyfile", "req", "thread"):
- if hasattr(self, attr):
- setattr(self, attr, None)
+ else:
+ self.req = None
diff --git a/module/plugins/internal/SimpleCrypter.py b/module/plugins/internal/SimpleCrypter.py
index 53d69b2a5..d4ae85ebe 100644
--- a/module/plugins/internal/SimpleCrypter.py
+++ b/module/plugins/internal/SimpleCrypter.py
@@ -13,8 +13,9 @@ class SimpleCrypter(Crypter, SimpleHoster):
__status__ = "testing"
__pattern__ = r'^unmatchable$'
- __config__ = [("use_subfolder" , "bool", "Save package to subfolder" , True),
- ("subfolder_per_pack", "bool", "Create a subfolder for each package", True)]
+ __config__ = [("use_premium" , "bool", "Use premium account if available" , True),
+ ("use_subfolder" , "bool", "Save package to subfolder" , True),
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
__description__ = """Simple decrypter plugin"""
__license__ = "GPLv3"
diff --git a/module/plugins/internal/SimpleHoster.py b/module/plugins/internal/SimpleHoster.py
index 3bb3ff211..8ba227c92 100644
--- a/module/plugins/internal/SimpleHoster.py
+++ b/module/plugins/internal/SimpleHoster.py
@@ -16,12 +16,12 @@ from module.utils import fixup, fs_encode, parseFileSize as parse_size
class SimpleHoster(Hoster):
__name__ = "SimpleHoster"
__type__ = "hoster"
- __version__ = "1.88"
+ __version__ = "1.89"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
__config__ = [("use_premium" , "bool", "Use premium account if available" , True),
- ("premium_fallback", "bool", "Fallback to free download if premium fails", True),
+ ("fallback_premium", "bool", "Fallback to free download if premium fails", True),
("chk_filesize" , "bool", "Check file size" , True)]
__description__ = """Simple hoster plugin"""
@@ -200,9 +200,6 @@ class SimpleHoster(Hoster):
self.direct_dl = False
self.leech_dl = False
- if not self.get_config('use_premium', True) and self.premium:
- self.restart(nopremium=True)
-
if self.LOGIN_PREMIUM and not self.premium:
self.fail(_("Required premium account not found"))
@@ -243,94 +240,70 @@ class SimpleHoster(Hoster):
def process(self, pyfile):
- try:
- self.prepare()
- self.check_info() #@TODO: Remove in 0.4.10
-
- if self.leech_dl:
- self.log_info(_("Processing as debrid download..."))
- self.handle_multi(pyfile)
+ self.prepare()
+ self.check_info() #@TODO: Remove in 0.4.10
- if not self.link and not was_downloaded():
- self.log_info(_("Failed to leech url"))
-
- else:
- if not self.link and self.direct_dl and not self.last_download:
- self.log_info(_("Looking for direct download link..."))
- self.handle_direct(pyfile)
+ if self.leech_dl:
+ self.log_info(_("Processing as debrid download..."))
+ self.handle_multi(pyfile)
- if self.link or self.last_download:
- self.log_info(_("Direct download link detected"))
- else:
- self.log_info(_("Direct download link not found"))
+ if not self.link and not was_downloaded():
+ self.log_info(_("Failed to leech url"))
- if not self.link and not self.last_download:
- self.preload()
+ else:
+ if not self.link and self.direct_dl and not self.last_download:
+ self.log_info(_("Looking for direct download link..."))
+ self.handle_direct(pyfile)
- if 'status' not in self.info or self.info['status'] is 3: #@TODO: Recheck in 0.4.10
- self.check_info()
+ if self.link or self.last_download:
+ self.log_info(_("Direct download link detected"))
+ else:
+ self.log_info(_("Direct download link not found"))
- if self.premium and (not self.CHECK_TRAFFIC or self.check_traffic()):
- self.log_info(_("Processing as premium download..."))
- self.handle_premium(pyfile)
+ if not self.link and not self.last_download:
+ self.preload()
- elif not self.LOGIN_ACCOUNT or (not self.CHECK_TRAFFIC or self.check_traffic()):
- self.log_info(_("Processing as free download..."))
- self.handle_free(pyfile)
+ if 'status' not in self.info or self.info['status'] is 3: #@TODO: Recheck in 0.4.10
+ self.check_info()
- if not self.last_download:
- self.log_info(_("Downloading file..."))
- self.download(self.link, disposition=self.DISPOSITION)
+ if self.premium and (not self.CHECK_TRAFFIC or self.check_traffic()):
+ self.log_info(_("Processing as premium download..."))
+ self.handle_premium(pyfile)
- self.check_download()
+ elif not self.LOGIN_ACCOUNT or (not self.CHECK_TRAFFIC or self.check_traffic()):
+ self.log_info(_("Processing as free download..."))
+ self.handle_free(pyfile)
- except Fail, e: #@TODO: Move to PluginThread in 0.4.10
- if self.get_config('premium_fallback', True) and self.premium:
- self.log_warning(_("Premium download failed"), e)
- self.restart(nopremium=True)
+ if not self.last_download:
+ self.log_info(_("Downloading file..."))
+ self.download(self.link, disposition=self.DISPOSITION)
- else:
- raise Fail(encode(e)) #@TODO: Remove `encode` in 0.4.10
+ self.check_download()
def check_download(self):
self.log_info(_("Checking downloaded file..."))
+ self.log_debug("Using default check rules...")
+ for r, p in self.FILE_ERRORS:
+ errmsg = self.check_file({r: re.compile(p)})
+ if errmsg is not None:
+ errmsg = errmsg.strip().capitalize()
- if self.captcha.task and not self.last_download:
- self.captcha.invalid()
- self.retry(10, msg=_("Wrong captcha"))
+ try:
+ errmsg += " | " + self.last_check.group(1).strip()
- elif self.check_file({'Empty file': re.compile(r'\A((.|)(\2|\s)*)\Z')},
- delete=True):
- self.error(_("Empty file"))
+ except Exception:
+ pass
+ self.log_warning(_("Check result: ") + errmsg, _("Waiting 1 minute and retry"))
+ self.wantReconnect = True
+ self.retry(wait=60, msg=errmsg)
else:
- if 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)
-
- self.log_debug("Using default check rules...")
- for r, p in self.FILE_ERRORS:
- errmsg = self.check_file({r: re.compile(p)})
- if errmsg is not None:
- errmsg = errmsg.strip().capitalize()
-
- try:
- errmsg += " | " + self.last_check.group(1).strip()
-
- except Exception:
- pass
-
- self.log_warning(_("Check result: ") + errmsg, _("Waiting 1 minute and retry"))
- self.wantReconnect = True
- self.retry(delay=60, msg=errmsg)
- else:
- if self.CHECK_FILE:
- self.log_debug("Using custom check rules...")
- with open(fs_encode(self.last_download), "rb") as f:
- self.html = f.read(1048576) #@TODO: Recheck in 0.4.10
- self.check_errors()
+ if self.CHECK_FILE:
+ self.log_debug("Using custom check rules...")
+ with open(fs_encode(self.last_download), "rb") as f:
+ self.html = f.read(1048576) #@TODO: Recheck in 0.4.10
+ self.check_errors()
self.log_info(_("No errors found"))
@@ -375,7 +348,7 @@ class SimpleHoster(Hoster):
if hasattr(self, 'ERROR_PATTERN'):
m = re.search(self.ERROR_PATTERN, self.html)
- if m:
+ if m is not None:
try:
errmsg = m.group(1).strip()
@@ -399,8 +372,7 @@ class SimpleHoster(Hoster):
self.fail(_("Connection from your current IP address is not allowed"))
elif re.search('captcha|code', errmsg, re.I):
- self.captcha.invalid()
- self.retry(10, msg=_("Wrong captcha"))
+ self.retry_captcha()
elif re.search('countdown|expired', errmsg, re.I):
self.retry(10, 60, _("Link expired"))
@@ -415,18 +387,18 @@ class SimpleHoster(Hoster):
self.offline()
elif re.search('filename', errmsg, re.I):
- self.fail(_("Wrong url"))
+ self.fail(_("Invalid url"))
elif re.search('premium', errmsg, re.I):
self.fail(_("File can be downloaded by premium users only"))
else:
self.wantReconnect = True
- self.retry(delay=60, msg=errmsg)
+ self.retry(wait=60, msg=errmsg)
elif hasattr(self, 'WAIT_PATTERN'):
m = re.search(self.WAIT_PATTERN, self.html)
- if m:
+ if m is not None:
try:
waitmsg = m.group(1).strip()
diff --git a/module/plugins/internal/XFSAccount.py b/module/plugins/internal/XFSAccount.py
index 3b92a191b..7b9410222 100644
--- a/module/plugins/internal/XFSAccount.py
+++ b/module/plugins/internal/XFSAccount.py
@@ -13,7 +13,7 @@ from module.plugins.internal.Plugin import parse_html_form, set_cookie
class XFSAccount(Account):
__name__ = "XFSAccount"
__type__ = "account"
- __version__ = "0.48"
+ __version__ = "0.49"
__status__ = "testing"
__description__ = """XFileSharing account plugin"""
@@ -42,6 +42,16 @@ class XFSAccount(Account):
LOGIN_SKIP_PATTERN = r'op=logout'
+ def set_xfs_cookie(self):
+ if not self.COOKIES:
+ return
+
+ if isinstance(self.COOKIES, list) and (self.PLUGIN_DOMAIN, "lang", "english") not in self.COOKIES:
+ self.COOKIES.insert((self.PLUGIN_DOMAIN, "lang", "english"))
+ else:
+ set_cookie(self.req.cj, self.PLUGIN_DOMAIN, "lang", "english")
+
+
def grab_info(self, user, password, data):
validuntil = None
trafficleft = None
@@ -146,11 +156,7 @@ class XFSAccount(Account):
if not self.PLUGIN_URL:
self.PLUGIN_URL = "http://www.%s/" % self.PLUGIN_DOMAIN
- if self.COOKIES:
- if isinstance(self.COOKIES, list) and (self.PLUGIN_DOMAIN, "lang", "english") not in self.COOKIES:
- self.COOKIES.insert((self.PLUGIN_DOMAIN, "lang", "english"))
- else:
- set_cookie(self.req.cj, self.PLUGIN_DOMAIN, "lang", "english")
+ self.set_xfs_cookie()
if not self.PLUGIN_URL:
self.fail_login(_("Missing PLUGIN_URL"))
diff --git a/module/plugins/internal/XFSCrypter.py b/module/plugins/internal/XFSCrypter.py
index 8047fd03e..69565e22c 100644
--- a/module/plugins/internal/XFSCrypter.py
+++ b/module/plugins/internal/XFSCrypter.py
@@ -7,10 +7,13 @@ from module.plugins.internal.SimpleCrypter import SimpleCrypter, create_getInfo
class XFSCrypter(SimpleCrypter):
__name__ = "XFSCrypter"
__type__ = "crypter"
- __version__ = "0.15"
+ __version__ = "0.16"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
+ __config__ = [("use_premium" , "bool", "Use premium account if available" , True),
+ ("use_subfolder" , "bool", "Save package to subfolder" , True),
+ ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
__description__ = """XFileSharing decrypter plugin"""
__license__ = "GPLv3"
@@ -27,6 +30,15 @@ class XFSCrypter(SimpleCrypter):
OFFLINE_PATTERN = r'>\s*(No such user|\w+ (Not Found|file (was|has been) removed|no longer available)'
TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)'
+ def set_xfs_cookie(self):
+ if not self.COOKIES:
+ return
+
+ if isinstance(self.COOKIES, list) and (self.PLUGIN_DOMAIN, "lang", "english") not in self.COOKIES:
+ self.COOKIES.insert((self.PLUGIN_DOMAIN, "lang", "english"))
+ else:
+ set_cookie(self.req.cj, self.PLUGIN_DOMAIN, "lang", "english")
+
def prepare(self):
if not self.PLUGIN_DOMAIN:
@@ -41,10 +53,6 @@ class XFSCrypter(SimpleCrypter):
else:
self.fail(_("Missing PLUGIN_DOMAIN"))
- if self.COOKIES:
- if isinstance(self.COOKIES, list) and (self.PLUGIN_DOMAIN, "lang", "english") not in self.COOKIES:
- self.COOKIES.insert((self.PLUGIN_DOMAIN, "lang", "english"))
- else:
- set_cookie(self.req.cj, self.PLUGIN_DOMAIN, "lang", "english")
+ self.set_xfs_cookie()
return super(XFSCrypter, self).prepare()
diff --git a/module/plugins/internal/XFSHoster.py b/module/plugins/internal/XFSHoster.py
index 98ed4e016..ce87642f0 100644
--- a/module/plugins/internal/XFSHoster.py
+++ b/module/plugins/internal/XFSHoster.py
@@ -14,10 +14,13 @@ from module.utils import html_unescape
class XFSHoster(SimpleHoster):
__name__ = "XFSHoster"
__type__ = "hoster"
- __version__ = "0.61"
+ __version__ = "0.62"
__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__ = """XFileSharing hoster plugin"""
__license__ = "GPLv3"
@@ -57,6 +60,16 @@ class XFSHoster(SimpleHoster):
self.resume_download = self.multiDL = self.premium
+ def set_xfs_cookie(self):
+ if not self.COOKIES:
+ return
+
+ if isinstance(self.COOKIES, list) and (self.PLUGIN_DOMAIN, "lang", "english") not in self.COOKIES:
+ self.COOKIES.insert((self.PLUGIN_DOMAIN, "lang", "english"))
+ else:
+ set_cookie(self.req.cj, self.PLUGIN_DOMAIN, "lang", "english")
+
+
def prepare(self):
"""
Initialize important variables
@@ -72,11 +85,7 @@ class XFSHoster(SimpleHoster):
else:
self.fail(_("Missing PLUGIN_DOMAIN"))
- if self.COOKIES:
- if isinstance(self.COOKIES, list) and (self.PLUGIN_DOMAIN, "lang", "english") not in self.COOKIES:
- self.COOKIES.insert((self.PLUGIN_DOMAIN, "lang", "english"))
- else:
- set_cookie(self.req.cj, self.PLUGIN_DOMAIN, "lang", "english")
+ self.set_xfs_cookie()
if not self.LINK_PATTERN:
pattern = r'(?:file: "(.+?)"|(https?://(?:www\.)?([^/]*?%s|\d+\.\d+\.\d+\.\d+)(\:\d+)?(/d/|(/files)?/\d+/\w+/).+?)["\'<])'