summaryrefslogtreecommitdiffstats
path: root/module/plugins/internal/Plugin.py
diff options
context:
space:
mode:
Diffstat (limited to 'module/plugins/internal/Plugin.py')
-rw-r--r--module/plugins/internal/Plugin.py237
1 files changed, 157 insertions, 80 deletions
diff --git a/module/plugins/internal/Plugin.py b/module/plugins/internal/Plugin.py
index 7b45c40a8..6d94ca1e4 100644
--- a/module/plugins/internal/Plugin.py
+++ b/module/plugins/internal/Plugin.py
@@ -6,14 +6,17 @@ import datetime
import inspect
import os
import re
+import time
+import traceback
import urllib
+import urlparse
-if os.name != "nt":
+if os.name is not "nt":
import grp
import pwd
from module.plugins.Plugin import Abort, Fail, Reconnect, Retry, SkipDownload as Skip #@TODO: Remove in 0.4.10
-from module.utils import fs_encode, fs_decode, html_unescape, save_join as fs_join
+from module.utils import fs_encode, fs_decode, html_unescape, parseFileSize as parse_size, save_join as fs_join
#@TODO: Move to utils in 0.4.10
@@ -22,7 +25,12 @@ def decode(string, encoding='utf8'):
if type(string) is str:
return string.decode(encoding, "replace")
else:
- return string
+ return unicode(string)
+
+
+#@TODO: Remove in 0.4.10
+def _decode(*args, **kwargs):
+ return decode(*args, **kwargs)
#@TODO: Move to utils in 0.4.10
@@ -31,14 +39,14 @@ def encode(string, encoding='utf8'):
if type(string) is unicode:
return string.encode(encoding, "replace")
else:
- return string
+ return str(string)
#@TODO: Move to utils in 0.4.10
def exists(path):
if os.path.exists(path):
- if os.name == "nt":
- dir, name = os.path.split(path)
+ if os.name is "nt":
+ dir, name = os.path.split(path.rstrip(os.sep))
return name in os.listdir(dir)
else:
return True
@@ -46,9 +54,69 @@ def exists(path):
return False
+def fixurl(url, unquote=None):
+ newurl = urllib.unquote(url)
+
+ if unquote is None:
+ unquote = newurl == url
+
+ newurl = html_unescape(newurl.decode('unicode-escape'))
+ newurl = re.sub(r'[^:]/{2,}', '/', newurl).strip().lstrip('.')
+
+ if not unquote:
+ newurl = urllib.quote(newurl)
+
+ return newurl
+
+
+def parse_name(string):
+ path = fixurl(decode(string), unquote=False)
+ url_p = urlparse.urlparse(path.rstrip('/'))
+ name = (url_p.path.split('/')[-1] or
+ url_p.query.split('=', 1)[::-1][0].split('&', 1)[0] or
+ url_p.netloc.split('.', 1)[0])
+
+ return urllib.unquote(name)
+
+
#@TODO: Move to utils in 0.4.10
-def fixurl(url):
- return html_unescape(urllib.unquote(url.decode('unicode-escape'))).strip().rstrip('/')
+def str2int(string):
+ try:
+ return int(string)
+ except:
+ pass
+
+ ones = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
+ "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
+ "sixteen", "seventeen", "eighteen", "nineteen"]
+ tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
+ "eighty", "ninety"]
+
+ o_tuple = [(w, i) for i, w in enumerate(ones)]
+ t_tuple = [(w, i * 10) for i, w in enumerate(tens)]
+
+ numwords = dict(o_tuple + t_tuple)
+ tokens = re.split(r"[\s-]+", string.lower())
+
+ try:
+ return sum(numwords[word] for word in tokens)
+ except:
+ return 0
+
+
+def parse_time(string):
+ if re.search("da(il)?y|today", string):
+ time = seconds_to_midnight()
+
+ else:
+ this = re.compile("this", re.I)
+ regex = re.compile(r'(\d+|\w+)\s*(hr|hour|min|sec|)', re.I)
+
+ time = sum(1 if this.match(v) else str2int(v) *
+ {'hr': 3600, 'hour': 3600, 'min': 60, 'sec': 1, '': 1}[u.lower()]
+ for v, u in regex.findall(string))
+
+ return time
#@TODO: Move to utils in 0.4.10
@@ -56,22 +124,35 @@ def timestamp():
return int(time.time() * 1000)
-def seconds_to_midnight(gmt=0):
- now = datetime.datetime.utcnow() + datetime.timedelta(hours=gmt)
-
- if now.hour == 0 and now.minute < 10:
- midnight = now
+#@TODO: Move to utils in 0.4.10
+def which(program):
+ """
+ Works exactly like the unix command which
+ Courtesy of http://stackoverflow.com/a/377028/675646
+ """
+ isExe = lambda x: os.path.isfile(x) and os.access(x, os.X_OK)
+
+ fpath, fname = os.path.split(program)
+
+ if fpath:
+ if isExe(program):
+ return program
else:
- midnight = now + datetime.timedelta(days=1)
+ for path in os.environ['PATH'].split(os.pathsep):
+ exe_file = os.path.join(path.strip('"'), program)
+ if isExe(exe_file):
+ return exe_file
+
- td = midnight.replace(hour=0, minute=10, second=0, microsecond=0) - now
+def seconds_to_midnight(utc=None):
+ if utc is None:
+ now = datetime.datetime.today()
+ else:
+ now = datetime.datetime.utcnow() + datetime.timedelta(hours=utc)
- if hasattr(td, 'total_seconds'):
- res = td.total_seconds()
- else: #@NOTE: work-around for python 2.5 and 2.6 missing datetime.timedelta.total_seconds
- res = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
+ midnight = now.replace(hour=0, minute=1, second=0, microsecond=0) + datetime.timedelta(days=1)
- return int(res)
+ return (midnight - now).seconds
def replace_patterns(string, ruleslist):
@@ -112,7 +193,10 @@ def parse_html_form(attr_str, html, input_names={}):
else:
inputs[name] = value
- if input_names:
+ if not input_names:
+ #: No attribute check
+ return action, inputs
+ else:
#: Check input attributes
for key, val in input_names.items():
if key in inputs:
@@ -122,14 +206,12 @@ def parse_html_form(attr_str, html, input_names={}):
continue
elif hasattr(val, "search") and re.match(val, inputs[key]):
continue
- break #: Attibute value does not match
+ else:
+ break #: Attibute value does not match
else:
break #: Attibute name does not match
else:
return action, inputs #: Passed attribute check
- else:
- #: No attribute check
- return action, inputs
return {}, None #: No matching form found
@@ -145,8 +227,8 @@ def chunks(iterable, size):
class Plugin(object):
__name__ = "Plugin"
- __type__ = "hoster"
- __version__ = "0.30"
+ __type__ = "plugin"
+ __version__ = "0.44"
__status__ = "testing"
__pattern__ = r'^unmatchable$'
@@ -154,10 +236,7 @@ class Plugin(object):
__description__ = """Base plugin"""
__license__ = "GPLv3"
- __authors__ = [("RaNaN" , "RaNaN@pyload.org" ),
- ("spoob" , "spoob@pyload.org" ),
- ("mkaay" , "mkaay@mkaay.de" ),
- ("Walter Purcaro", "vuolter@gmail.com")]
+ __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
def __init__(self, core):
@@ -165,10 +244,16 @@ class Plugin(object):
self.init()
+ def __repr__(self):
+ return "<%(type)s %(name)s>" % {'type': self.__type__.capitalize(),
+ 'name': self.__name__}
+
+
def _init(self, core):
- self.pyload = core
- self.info = {} #: Provide information in dict here
- self.req = None
+ 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):
@@ -180,33 +265,37 @@ class Plugin(object):
def _log(self, level, plugintype, pluginname, messages):
log = getattr(self.pyload.log, level)
- msg = encode(" | ".join((a if isinstance(a, basestring) else str(a)).strip() for a in messages if a))
- log("%(plugintype)s %(pluginname)s%(id)s: %(msg)s"
+ msg = u" | ".join(decode(a).strip() for a in messages if a)
+ log("%(plugintype)s %(pluginname)s: %(msg)s"
% {'plugintype': plugintype.upper(),
'pluginname': pluginname,
- 'id' : ("[%s]" % self.pyfile.id) if hasattr(self, 'pyfile') else "",
'msg' : msg})
def log_debug(self, *args):
- if self.pyload.debug:
- return self._log("debug", self.__type__, self.__name__, args)
+ if not self.pyload.debug:
+ return
+ self._log("debug", self.__type__, self.__name__, args)
def log_info(self, *args):
- return self._log("info", self.__type__, self.__name__, args)
+ self._log("info", self.__type__, self.__name__, args)
def log_warning(self, *args):
- return self._log("warning", self.__type__, self.__name__, args)
+ self._log("warning", self.__type__, self.__name__, args)
def log_error(self, *args):
- return self._log("error", self.__type__, self.__name__, args)
+ self._log("error", self.__type__, self.__name__, args)
+ if self.pyload.debug:
+ traceback.print_exc()
def log_critical(self, *args):
return self._log("critical", self.__type__, self.__name__, args)
+ if self.pyload.debug:
+ traceback.print_exc()
def set_permissions(self, path):
@@ -225,7 +314,7 @@ class Plugin(object):
self.log_warning(_("Setting path mode failed"), e)
try:
- if os.name != "nt" and self.pyload.config.get("permission", "change_dl"):
+ if os.name is not "nt" and self.pyload.config.get("permission", "change_dl"):
uid = pwd.getpwnam(self.pyload.config.get("permission", "user"))[2]
gid = grp.getgrnam(self.pyload.config.get("permission", "group"))[2]
os.chown(path, uid, gid)
@@ -240,7 +329,7 @@ class Plugin(object):
return min(self.pyload.config.get("download", "chunks"), self.chunk_limit)
- def set_config(self, option, value):
+ def set_config(self, option, value, plugin=None):
"""
Set config value for current plugin
@@ -248,7 +337,7 @@ class Plugin(object):
:param value:
:return:
"""
- self.pyload.config.setPlugin(self.__name__, option, value)
+ self.pyload.config.setPlugin(plugin or self.__name__, option, value)
def get_config(self, option, default="", plugin=None):
@@ -287,21 +376,10 @@ class Plugin(object):
self.pyload.db.delStorage(self.__name__, key)
- def fail(self, reason):
+ def fail(self, msg):
"""
- Fail and give reason
+ Fail and give msg
"""
- raise Fail(encode(reason)) #@TODO: Remove `encode` in 0.4.10
-
-
- def error(self, reason="", type=_("Parse")):
- if not reason:
- type = _("Unknown")
-
- msg = _("%s error") % type.strip().capitalize() if type else _("Error")
- msg += (": %s" % reason.strip()) if reason else ""
- msg += _(" | Plugin may be out of date")
-
raise Fail(encode(msg)) #@TODO: Remove `encode` in 0.4.10
@@ -318,17 +396,11 @@ class Plugin(object):
:param decode: Wether to decode the output according to http header, should be True in most cases
:return: Loaded content
"""
- if hasattr(self, 'pyfile') and self.pyfile.abort:
- self.abort()
-
- url = fixurl(url)
-
- if not url or not isinstance(url, basestring):
- self.fail(_("No url given"))
-
if self.pyload.debug:
self.log_debug("LOAD URL " + url,
- *["%s=%s" % (key, val) for key, val in locals().items() if key not in ("self", "url")])
+ *["%s=%s" % (key, val) for key, val in locals().items() if key not in ("self", "url", "_[1]")])
+
+ url = fixurl(url) #: Recheck in 0.4.10
if req is None:
req = self.req or self.pyload.requestFactory.getRequest(self.__name__)
@@ -337,15 +409,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)
+ html = _decode(html, decode) #@NOTE: Use `utils.decode()` in 0.4.10
+
+ self.last_html = html
if self.pyload.debug:
frame = inspect.currentframe()
@@ -356,15 +430,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
@@ -380,20 +457,20 @@ 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()
self.req.close()
+
except Exception:
pass
- for a in ("pyfile", "thread", "html", "req"):
- if hasattr(self, a):
- setattr(self, a, None)
+ else:
+ self.req = None