summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pyload/PluginManager.py48
-rw-r--r--pyload/api/ConfigApi.py15
-rw-r--r--pyload/config/ConfigManager.py7
-rw-r--r--pyload/config/ConfigParser.py80
-rw-r--r--pyload/config/convert.py38
-rw-r--r--pyload/config/default.py150
-rw-r--r--pyload/interaction/InteractionManager.py10
-rw-r--r--pyload/plugins/Account.py6
-rw-r--r--pyload/plugins/Base.py14
-rw-r--r--pyload/plugins/hoster/BasePlugin.py4
-rw-r--r--pyload/remote/apitypes.py34
-rw-r--r--pyload/remote/apitypes_debug.py8
-rw-r--r--pyload/remote/pyload.thrift16
-rw-r--r--pyload/remote/wsbackend/AsyncHandler.py2
-rw-r--r--pyload/utils/__init__.py28
-rw-r--r--pyload/web/app/scripts/utils/apitypes.js2
-rw-r--r--pyload/web/app/scripts/views/input/inputLoader.js2
-rw-r--r--pyload/web/app/scripts/views/input/inputView.js4
-rw-r--r--pyload/web/app/scripts/views/queryModal.js2
-rw-r--r--tests/manager/test_configManager.py11
20 files changed, 265 insertions, 216 deletions
diff --git a/pyload/PluginManager.py b/pyload/PluginManager.py
index dab5be063..149d04fc2 100644
--- a/pyload/PluginManager.py
+++ b/pyload/PluginManager.py
@@ -22,6 +22,7 @@ from os import listdir, makedirs
from os.path import isfile, join, exists, abspath, basename
from sys import version_info
from time import time
+from collections import defaultdict
from pyload.lib.SafeEval import const_eval as literal_eval
from pyload.plugins.Base import Base
@@ -34,10 +35,20 @@ from new_collections import namedtuple
IGNORE = (
"FreakshareNet", "SpeedManager", "ArchiveTo", "ShareCx", ('addons', 'UnRar'),
'EasyShareCom', 'FlyshareCz'
- )
+)
PluginTuple = namedtuple("PluginTuple", "version re deps category user path")
+class BaseAttributes(defaultdict):
+ """ Dictionary that loads defaults values from Base object """
+ def __missing__(self, key):
+ attr = "__%s__" % key
+ if not hasattr(Base, attr):
+ return defaultdict.__missing__(self, key)
+
+ return getattr(Base, attr)
+
+
class PluginManager:
ROOT = "pyload.plugins."
LOCALROOT = "localplugins."
@@ -45,7 +56,7 @@ class PluginManager:
BUILTIN = re.compile(r'__(?P<attr>[a-z0-9_]+)__\s*=\s?(True|False|None|[0-9x.]+)', re.I)
SINGLE = re.compile(r'__(?P<attr>[a-z0-9_]+)__\s*=\s*(?:r|u|_)?((?:(?<!")"(?!")|\'|\().*(?:(?<!")"(?!")|\'|\)))',
- re.I)
+ re.I)
# note the nongreedy character: that means we can not embed list and dicts
MULTI = re.compile(r'__(?P<attr>[a-z0-9_]+)__\s*=\s*((?:\{|\[|"{3}).*?(?:"""|\}|\]))', re.DOTALL | re.M | re.I)
@@ -102,7 +113,7 @@ class PluginManager:
for f in listdir(pfolder):
if (isfile(join(pfolder, f)) and f.endswith(".py") or f.endswith("_25.pyc") or f.endswith(
- "_26.pyc") or f.endswith("_27.pyc")) and not f.startswith("_"):
+ "_26.pyc") or f.endswith("_27.pyc")) and not f.startswith("_"):
if f.endswith("_25.pyc") and version_info[0:2] != (2, 5):
continue
elif f.endswith("_26.pyc") and version_info[0:2] != (2, 6):
@@ -130,7 +141,7 @@ class PluginManager:
content = data.read()
data.close()
- attrs = {}
+ attrs = BaseAttributes()
for m in self.BUILTIN.findall(content) + self.SINGLE.findall(content) + self.MULTI.findall(content):
#replace gettext function and eval result
try:
@@ -179,10 +190,11 @@ class PluginManager:
except:
self.logDebug(folder, name, "Invalid regexp pattern '%s'" % attrs["pattern"])
plugin_re = self.NO_MATCH
- else: plugin_re = self.NO_MATCH
+ else:
+ plugin_re = self.NO_MATCH
- deps = attrs.get("dependencies", None)
- category = attrs.get("category", None) if folder == "addons" else None
+ deps = attrs["dependencies"]
+ category = attrs["category"] if folder == "addons" else ""
# create plugin tuple
plugin = PluginTuple(version, plugin_re, deps, category, bool(home), filename)
@@ -191,29 +203,29 @@ class PluginManager:
if folder == "internal":
return plugin
- if folder == "addons" and "config" not in attrs and not attrs.get("internal", False):
+ if folder == "addons" and "config" not in attrs and not attrs["internal"]:
attrs["config"] = (["activated", "bool", "Activated", False],)
if "config" in attrs and attrs["config"] is not None:
config = attrs["config"]
- desc = attrs.get("description", "")
- long_desc = attrs.get("long_description", "")
+ desc = attrs["description"]
+ expl = attrs["explanation"]
# Convert tuples to list
config = [list(x) for x in config]
- if folder == "addons" and not attrs.get("internal", False):
+ if folder == "addons" and not attrs["internal"]:
for item in config:
if item[0] == "activated": break
else: # activated flag missing
config.insert(0, ("activated", "bool", "Activated", False))
# Everything that is no addon and user_context=True, is added to dict
- if folder != "addons" or attrs.get("user_context", False):
+ if folder != "addons" or attrs["user_context"]:
self.user_context[name] = True
try:
- self.core.config.addConfigSection(name, name, desc, long_desc, config)
+ self.core.config.addConfigSection(name, name, desc, expl, config)
except:
self.logDebug(folder, name, "Invalid config %s" % config)
@@ -312,8 +324,10 @@ class PluginManager:
def find_module(self, fullname, path=None):
#redirecting imports if necesarry
if fullname.startswith(self.ROOT) or fullname.startswith(self.LOCALROOT): #separate pyload plugins
- if fullname.startswith(self.LOCALROOT): user = 1
- else: user = 0 #used as bool and int
+ if fullname.startswith(self.LOCALROOT):
+ user = 1
+ else:
+ user = 0 #used as bool and int
split = fullname.split(".")
if len(split) != 4 - user: return
@@ -347,13 +361,13 @@ class PluginManager:
self.log.debug("Old import reference detected, use %s" % name)
replace = False
-
if replace:
if self.ROOT in name:
newname = name.replace(self.ROOT, self.LOCALROOT)
else:
newname = name.replace(self.LOCALROOT, self.ROOT)
- else: newname = name
+ else:
+ newname = name
base, plugin = newname.rsplit(".", 1)
diff --git a/pyload/api/ConfigApi.py b/pyload/api/ConfigApi.py
index 82cfdd418..2adc0c565 100644
--- a/pyload/api/ConfigApi.py
+++ b/pyload/api/ConfigApi.py
@@ -8,9 +8,9 @@ from ApiComponent import ApiComponent
# helper function to create a ConfigHolder
def toConfigHolder(section, config, values):
- holder = ConfigHolder(section, config.name, config.description, config.long_desc)
- holder.items = [ConfigItem(option, x.name, x.description, x.type, to_string(x.default),
- to_string(values.get(option, x.default))) for option, x in
+ holder = ConfigHolder(section, config.label, config.description, config.explanation)
+ holder.items = [ConfigItem(option, x.label, x.description, x.input,
+ to_string(values.get(option, x.input.default_value))) for option, x in
config.config.iteritems()]
return holder
@@ -56,7 +56,7 @@ class ConfigApi(ApiComponent):
:rtype: list of PluginInfo
"""
- return [ConfigInfo(section, config.name, config.description, False, False)
+ return [ConfigInfo(section, config.label, config.description, False, False)
for section, config, values in self.core.config.iterCoreSections()]
@RequirePerm(Permission.Plugins)
@@ -74,11 +74,12 @@ class ConfigApi(ApiComponent):
# skip unmodified and inactive addons
if not values and name not in active: continue
- item = ConfigInfo(name, config.name, config.description,
+ item = ConfigInfo(name, config.label, config.description,
self.core.pluginManager.getCategory(name),
self.core.pluginManager.isUserPlugin(name),
+ # TODO: won't work probably
values.get("activated", None if "activated" not in config.config else config.config[
- "activated"].default))
+ "activated"].input.default_value))
data.append(item)
return data
@@ -90,7 +91,7 @@ class ConfigApi(ApiComponent):
:rtype: list of PluginInfo
"""
# TODO: filter user_context / addons when not allowed
- plugins = [ConfigInfo(name, config.name, config.description,
+ plugins = [ConfigInfo(name, config.label, config.description,
self.core.pluginManager.getCategory(name),
self.core.pluginManager.isUserPlugin(name))
for name, config, values in self.core.config.iterSections(self.primaryUID)]
diff --git a/pyload/config/ConfigManager.py b/pyload/config/ConfigManager.py
index f9dc3795b..33bd151c3 100644
--- a/pyload/config/ConfigManager.py
+++ b/pyload/config/ConfigManager.py
@@ -4,10 +4,11 @@
from new_collections import OrderedDict
from pyload.Api import InvalidConfigSection
-from pyload.utils import from_string, json
+from pyload.utils import json
from ConfigParser import ConfigParser
+from convert import to_input, from_string
def convertKeyError(func):
""" converts KeyError into InvalidConfigSection """
@@ -64,7 +65,7 @@ class ConfigManager(ConfigParser):
except KeyError:
pass # Returns default value later
- return self.config[section].config[option].default
+ return self.config[section].config[option].input.default_value
def loadValues(self, user, section):
if (user, section) not in self.values:
@@ -86,7 +87,7 @@ class ConfigManager(ConfigParser):
changed = self.parser.set(section, option, value, sync)
else:
data = self.config[section].config[option]
- value = from_string(value, data.type)
+ value = from_string(value, data.input.type)
old_value = self.get(section, option)
# Values will always be saved to db, sync is ignored
diff --git a/pyload/config/ConfigParser.py b/pyload/config/ConfigParser.py
index fd22e93b9..bda3f7bd4 100644
--- a/pyload/config/ConfigParser.py
+++ b/pyload/config/ConfigParser.py
@@ -6,14 +6,16 @@ from os.path import exists
from gettext import gettext
from new_collections import namedtuple, OrderedDict
-from pyload.utils import from_string
+
+from pyload.Api import Input, InputType
from pyload.utils.fs import chmod
from default import make_config
+from convert import to_input, from_string
CONF_VERSION = 2
-SectionTuple = namedtuple("SectionTuple", "name description long_desc config")
-ConfigData = namedtuple("ConfigData", "name type description default")
+SectionTuple = namedtuple("SectionTuple", "label description explanation config")
+ConfigData = namedtuple("ConfigData", "label description input")
class ConfigParser:
"""
@@ -41,31 +43,21 @@ class ConfigParser:
def checkVersion(self):
"""Determines if config needs to be deleted"""
- e = None
- # workaround conflict, with GUI (which also accesses the config) so try read in 3 times
- for i in range(0, 3):
- try:
- if exists(self.CONFIG):
- f = open(self.CONFIG, "rb")
- v = f.readline()
- f.close()
- v = v[v.find(":") + 1:].strip()
-
- if not v or int(v) < CONF_VERSION:
- f = open(self.CONFIG, "wb")
- f.write("version: " + str(CONF_VERSION))
- f.close()
- print "Old version of %s deleted" % self.CONFIG
- else:
- f = open(self.CONFIG, "wb")
- f.write("version:" + str(CONF_VERSION))
- f.close()
-
- except Exception, ex:
- e = ex
- sleep(0.3)
- if e: raise e
-
+ if exists(self.CONFIG):
+ f = open(self.CONFIG, "rb")
+ v = f.readline()
+ f.close()
+ v = v[v.find(":") + 1:].strip()
+
+ if not v or int(v) < CONF_VERSION:
+ f = open(self.CONFIG, "wb")
+ f.write("version: " + str(CONF_VERSION))
+ f.close()
+ print "Old version of %s deleted" % self.CONFIG
+ else:
+ f = open(self.CONFIG, "wb")
+ f.write("version:" + str(CONF_VERSION))
+ f.close()
def parseValues(self, filename):
"""read config values from file"""
@@ -139,13 +131,13 @@ class ConfigParser:
try:
return self.values[section][option]
except KeyError:
- return self.config[section].config[option].default
+ return self.config[section].config[option].input.default_value
def set(self, section, option, value, sync=True):
"""set value"""
data = self.config[section].config[option]
- value = from_string(value, data.type)
+ value = from_string(value, data.input.type)
old_value = self.get(section, option)
# only save when different values
@@ -172,22 +164,34 @@ class ConfigParser:
""" Retrieves single config as tuple (section, values) """
return self.config[section], self.values[section] if section in self.values else {}
- def addConfigSection(self, section, name, desc, long_desc, config):
+ def addConfigSection(self, section, label, desc, expl, config):
"""Adds a section to the config. `config` is a list of config tuples as used in plugin api defined as:
The order of the config elements is preserved with OrderedDict
"""
d = OrderedDict()
for entry in config:
- if len(entry) == 5:
- conf_name, type, conf_desc, conf_verbose, default = entry
- else: # config options without description
- conf_name, type, conf_desc, default = entry
- conf_verbose = ""
+ if len(entry) != 4:
+ raise ValueError("Config entry must be of length 4")
+
+ # Values can have different roles depending on the two config formats
+ conf_name, type_label, label_desc, default_input = entry
+
+ # name, label, desc, input
+ if isinstance(default_input, Input):
+ input = default_input
+ conf_label = type_label
+ conf_desc = label_desc
+ # name, type, label, default
+ else:
+ input = Input(to_input(type_label))
+ input.default_value = from_string(default_input, input.type)
+ conf_label = label_desc
+ conf_desc = ""
- d[conf_name] = ConfigData(gettext(conf_desc), type, gettext(conf_verbose), from_string(default, type))
+ d[conf_name] = ConfigData(gettext(conf_label), gettext(conf_desc), input)
- data = SectionTuple(gettext(name), gettext(desc), gettext(long_desc), d)
+ data = SectionTuple(gettext(label), gettext(desc), gettext(expl), d)
self.config[section] = data
class Section:
diff --git a/pyload/config/convert.py b/pyload/config/convert.py
new file mode 100644
index 000000000..f25f6a7ba
--- /dev/null
+++ b/pyload/config/convert.py
@@ -0,0 +1,38 @@
+
+from pyload.Api import Input, InputType
+from pyload.utils import decode, to_bool
+
+# Maps old config formats to new values
+input_dict = {
+ "int": InputType.Int,
+ "bool": InputType.Bool,
+ "time": InputType.Time,
+ "file": InputType.File,
+ "folder": InputType.Folder
+}
+
+
+def to_input(typ):
+ """ Converts old config format to input type"""
+ return input_dict.get(typ, InputType.Text)
+
+
+def from_string(value, typ=None):
+ """ cast value to given type, unicode for strings """
+
+ # value is no string
+ if not isinstance(value, basestring):
+ return value
+
+ value = decode(value)
+
+ if typ == InputType.Int:
+ return int(value)
+ elif typ == InputType.Bool:
+ return to_bool(value)
+ elif typ == InputType.Time:
+ if not value: value = "0:00"
+ if not ":" in value: value += ":00"
+ return value
+ else:
+ return value \ No newline at end of file
diff --git a/pyload/config/default.py b/pyload/config/default.py
index 8388d32df..9fad814d3 100644
--- a/pyload/config/default.py
+++ b/pyload/config/default.py
@@ -12,96 +12,96 @@ def make_config(config):
_ = lambda x: x
config.addConfigSection("remote", _("Remote"), _("Description"), _("Long description"),
- [
- ("activated", "bool", _("Activated"), _("Tooltip"), True),
- ("port", "int", _("Port"), _("Tooltip"), 7227),
- ("listenaddr", "ip", _("Adress"), _("Tooltip"), "0.0.0.0"),
- ])
+ [
+ ("activated", "bool", _("Activated"), True),
+ ("port", "int", _("Port"), 7227),
+ ("listenaddr", "ip", _("Adress"), "0.0.0.0"),
+ ])
config.addConfigSection("log", _("Log"), _("Description"), _("Long description"),
- [
- ("log_size", "int", _("Size in kb"), _("Tooltip"), 100),
- ("log_folder", "folder", _("Folder"), _("Tooltip"), "Logs"),
- ("file_log", "bool", _("File Log"), _("Tooltip"), True),
- ("log_count", "int", _("Count"), _("Tooltip"), 5),
- ("log_rotate", "bool", _("Log Rotate"), _("Tooltip"), True),
- ])
+ [
+ ("log_size", "int", _("Size in kb"), 100),
+ ("log_folder", "folder", _("Folder"), "Logs"),
+ ("file_log", "bool", _("File Log"), True),
+ ("log_count", "int", _("Count"), 5),
+ ("log_rotate", "bool", _("Log Rotate"), True),
+ ])
config.addConfigSection("permission", _("Permissions"), _("Description"), _("Long description"),
- [
- ("group", "str", _("Groupname"), _("Tooltip"), "users"),
- ("change_dl", "bool", _("Change Group and User of Downloads"), _("Tooltip"), False),
- ("change_file", "bool", _("Change file mode of downloads"), _("Tooltip"), False),
- ("user", "str", _("Username"), _("Tooltip"), "user"),
- ("file", "str", _("Filemode for Downloads"), _("Tooltip"), "0644"),
- ("change_group", "bool", _("Change group of running process"), _("Tooltip"), False),
- ("folder", "str", _("Folder Permission mode"), _("Tooltip"), "0755"),
- ("change_user", "bool", _("Change user of running process"), _("Tooltip"), False),
- ])
+ [
+ ("group", "str", _("Groupname"), "users"),
+ ("change_dl", "bool", _("Change Group and User of Downloads"), False),
+ ("change_file", "bool", _("Change file mode of downloads"), False),
+ ("user", "str", _("Username"), "user"),
+ ("file", "str", _("Filemode for Downloads"), "0644"),
+ ("change_group", "bool", _("Change group of running process"), False),
+ ("folder", "str", _("Folder Permission mode"), "0755"),
+ ("change_user", "bool", _("Change user of running process"), False),
+ ])
config.addConfigSection("general", _("General"), _("Description"), _("Long description"),
- [
- ("language", "en;de;fr;it;es;nl;sv;ru;pl;cs;sr;pt_BR", _("Language"), _("Tooltip"), "en"),
- ("download_folder", "folder", _("Download Folder"), _("Tooltip"), "Downloads"),
- ("checksum", "bool", _("Use Checksum"), _("Tooltip"), False),
- ("folder_per_package", "bool", _("Create folder for each package"), _("Tooltip"), True),
- ("debug_mode", "bool", _("Debug Mode"), _("Tooltip"), False),
- ("min_free_space", "int", _("Min Free Space (MB)"), _("Tooltip"), 200),
- ("renice", "int", _("CPU Priority"), _("Tooltip"), 0),
- ])
+ [
+ ("language", "en;de;fr;it;es;nl;sv;ru;pl;cs;sr;pt_BR", _("Language"), "en"),
+ ("download_folder", "folder", _("Download Folder"), "Downloads"),
+ ("checksum", "bool", _("Use Checksum"), False),
+ ("folder_per_package", "bool", _("Create folder for each package"), True),
+ ("debug_mode", "bool", _("Debug Mode"), False),
+ ("min_free_space", "int", _("Min Free Space (MB)"), 200),
+ ("renice", "int", _("CPU Priority"), 0),
+ ])
config.addConfigSection("ssl", _("SSL"), _("Description"), _("Long description"),
- [
- ("cert", "file", _("SSL Certificate"), _("Tooltip"), "ssl.crt"),
- ("activated", "bool", _("Activated"), _("Tooltip"), False),
- ("key", "file", _("SSL Key"), _("Tooltip"), "ssl.key"),
- ])
+ [
+ ("cert", "file", _("SSL Certificate"), "ssl.crt"),
+ ("activated", "bool", _("Activated"), False),
+ ("key", "file", _("SSL Key"), "ssl.key"),
+ ])
config.addConfigSection("webinterface", _("Webinterface"), _("Description"), _("Long description"),
- [
- ("template", "str", _("Template"), _("Tooltip"), "default"),
- ("activated", "bool", _("Activated"), _("Tooltip"), True),
- ("prefix", "str", _("Path Prefix"), _("Tooltip"), ""),
- ("server", "auto;threaded;fallback;fastcgi", _("Server"), _("Tooltip"), "auto"),
- ("force_server", "str", _("Favor specific server"), _("Tooltip"), ""),
- ("host", "ip", _("IP"), _("Tooltip"), "0.0.0.0"),
- ("https", "bool", _("Use HTTPS"), _("Tooltip"), False),
- ("port", "int", _("Port"), _("Tooltip"), 8001),
- ("develop", "str", _("Development mode"), _("Tooltip"), False),
- ])
+ [
+ ("template", "str", _("Template"), "default"),
+ ("activated", "bool", _("Activated"), True),
+ ("prefix", "str", _("Path Prefix"), ""),
+ ("server", "auto;threaded;fallback;fastcgi", _("Server"), "auto"),
+ ("force_server", "str", _("Favor specific server"), ""),
+ ("host", "ip", _("IP"), "0.0.0.0"),
+ ("https", "bool", _("Use HTTPS"), False),
+ ("port", "int", _("Port"), 8001),
+ ("develop", "str", _("Development mode"), False),
+ ])
config.addConfigSection("proxy", _("Proxy"), _("Description"), _("Long description"),
- [
- ("username", "str", _("Username"), _("Tooltip"), ""),
- ("proxy", "bool", _("Use Proxy"), _("Tooltip"), False),
- ("address", "str", _("Address"), _("Tooltip"), "localhost"),
- ("password", "password", _("Password"), _("Tooltip"), ""),
- ("type", "http;socks4;socks5", _("Protocol"), _("Tooltip"), "http"),
- ("port", "int", _("Port"), _("Tooltip"), 7070),
- ])
+ [
+ ("username", "str", _("Username"), ""),
+ ("proxy", "bool", _("Use Proxy"), False),
+ ("address", "str", _("Address"), "localhost"),
+ ("password", "password", _("Password"), ""),
+ ("type", "http;socks4;socks5", _("Protocol"), "http"),
+ ("port", "int", _("Port"), 7070),
+ ])
config.addConfigSection("reconnect", _("Reconnect"), _("Description"), _("Long description"),
- [
- ("endTime", "time", _("End"), _("Tooltip"), "0:00"),
- ("activated", "bool", _("Use Reconnect"), _("Tooltip"), False),
- ("method", "str", _("Method"), _("Tooltip"), "./reconnect.sh"),
- ("startTime", "time", _("Start"), _("Tooltip"), "0:00"),
- ])
+ [
+ ("endTime", "time", _("End"), "0:00"),
+ ("activated", "bool", _("Use Reconnect"), False),
+ ("method", "str", _("Method"), "./reconnect.sh"),
+ ("startTime", "time", _("Start"), "0:00"),
+ ])
config.addConfigSection("download", _("Download"), _("Description"), _("Long description"),
- [
- ("max_downloads", "int", _("Max Parallel Downloads"), _("Tooltip"), 3),
- ("limit_speed", "bool", _("Limit Download Speed"), _("Tooltip"), False),
- ("interface", "str", _("Download interface to bind (ip or Name)"), _("Tooltip"), ""),
- ("skip_existing", "bool", _("Skip already existing files"), _("Tooltip"), False),
- ("max_speed", "int", _("Max Download Speed in kb/s"), _("Tooltip"), -1),
- ("ipv6", "bool", _("Allow IPv6"), _("Tooltip"), False),
- ("chunks", "int", _("Max connections for one download"), _("Tooltip"), 3),
- ("restart_failed", "bool", _("Restart failed downloads on startup"), _("Tooltip"), False),
- ])
+ [
+ ("max_downloads", "int", _("Max Parallel Downloads"), 3),
+ ("limit_speed", "bool", _("Limit Download Speed"), False),
+ ("interface", "str", _("Download interface to bind (ip or Name)"), ""),
+ ("skip_existing", "bool", _("Skip already existing files"), False),
+ ("max_speed", "int", _("Max Download Speed in kb/s"), -1),
+ ("ipv6", "bool", _("Allow IPv6"), False),
+ ("chunks", "int", _("Max connections for one download"), 3),
+ ("restart_failed", "bool", _("Restart failed downloads on startup"), False),
+ ])
config.addConfigSection("downloadTime", _("Download Time"), _("Description"), _("Long description"),
- [
- ("start", "time", _("Start"), _("Tooltip"), "0:00"),
- ("end", "time", _("End"), _("Tooltip"), "0:00"),
- ])
+ [
+ ("start", "time", _("Start"), "0:00"),
+ ("end", "time", _("End"), "0:00"),
+ ])
diff --git a/pyload/interaction/InteractionManager.py b/pyload/interaction/InteractionManager.py
index 9c5449b31..36d457323 100644
--- a/pyload/interaction/InteractionManager.py
+++ b/pyload/interaction/InteractionManager.py
@@ -71,21 +71,21 @@ class InteractionManager:
:param plugin: plugin name
:return: :class:`InteractionTask`
"""
- task = InteractionTask(self.ids, IA.Notification, Input(InputType.Text, content), "", title, desc, plugin,
+ task = InteractionTask(self.ids, IA.Notification, Input(InputType.Text, None, content), title, desc, plugin,
owner=owner)
self.ids += 1
self.queueTask(task)
return task
@lock
- def createQueryTask(self, input, desc, default="", plugin="", owner=None):
+ def createQueryTask(self, input, desc, plugin="", owner=None):
# input type was given, create a input widget
if type(input) == int:
input = Input(input)
if not isinstance(input, Input):
raise TypeError("'Input' class expected not '%s'" % type(input))
- task = InteractionTask(self.ids, IA.Query, input, default, _("Query"), desc, plugin, owner=owner)
+ task = InteractionTask(self.ids, IA.Query, input, _("Query"), desc, plugin, owner=owner)
self.ids += 1
self.queueTask(task)
return task
@@ -104,11 +104,11 @@ class InteractionManager:
elif type == 'positional':
type = InputType.Click
- input = Input(type, [standard_b64encode(img), format, filename])
+ input = Input(type, data=[standard_b64encode(img), format, filename])
#todo: title desc plugin
task = InteractionTask(self.ids, IA.Captcha, input,
- None, _("Captcha request"), _("Please solve the captcha."), plugin, owner=owner)
+ _("Captcha request"), _("Please solve the captcha."), plugin, owner=owner)
self.ids += 1
self.queueTask(task)
diff --git a/pyload/plugins/Account.py b/pyload/plugins/Account.py
index 2a85e2b44..0bf201c1a 100644
--- a/pyload/plugins/Account.py
+++ b/pyload/plugins/Account.py
@@ -4,7 +4,7 @@ from time import time
from traceback import print_exc
from threading import RLock
-from pyload.utils import compare_time, format_size, parseFileSize, lock, from_string
+from pyload.utils import compare_time, format_size, parseFileSize, lock, to_bool
from pyload.Api import AccountInfo
from pyload.network.CookieJar import CookieJar
@@ -50,7 +50,7 @@ class Account(Base):
Base.__init__(self, manager.core)
if "activated" in options:
- self.activated = from_string(options["activated"], "bool")
+ self.activated = to_bool(options["activated"])
else:
self.activated = Account.activated
@@ -141,7 +141,7 @@ class Account(Base):
self.valid = True #set valid, so the login will be retried
if "activated" in options:
- self.activated = from_string(options["avtivated"], "bool")
+ self.activated = True if options["activated"] == "True" else False
if password:
self.password = password
diff --git a/pyload/plugins/Base.py b/pyload/plugins/Base.py
index 47369e21f..308a38726 100644
--- a/pyload/plugins/Base.py
+++ b/pyload/plugins/Base.py
@@ -50,12 +50,12 @@ class Base(object):
#: When True this addon can be enabled by every user
__user_context__ = False
#: Config definition: list of (name, type, label, default_value) or
- #: (name, type, label, short_description, default_value)
- __config__ = list()
+ #: (name, label, desc, Input(...))
+ __config__ = tuple()
#: Short description, one liner
- __label__ = ""
+ __description__ = ""
#: More detailed text
- __description__ = """"""
+ __explanation__ = """"""
#: List of needed modules
__dependencies__ = tuple()
#: Used to assign a category for addon plugins
@@ -66,6 +66,8 @@ class Base(object):
__icon__ = ""
#: Alternative, link to png icon
__icon_url__ = ""
+ #: Domain name of the service
+ __domain__ = ""
#: Url with general information/support/discussion
__url__ = ""
#: Url to term of content, user is accepting these when using the plugin
@@ -106,6 +108,10 @@ class Base(object):
#: last interaction task
self.task = None
+ def __getitem__(self, item):
+ """ Retrieves meta data attribute """
+ return getattr(Base, "__%s__" % item)
+
def logInfo(self, *args, **kwargs):
""" Print args to log at specific level
diff --git a/pyload/plugins/hoster/BasePlugin.py b/pyload/plugins/hoster/BasePlugin.py
index 7070fafde..552e7bc73 100644
--- a/pyload/plugins/hoster/BasePlugin.py
+++ b/pyload/plugins/hoster/BasePlugin.py
@@ -13,7 +13,7 @@ class BasePlugin(Hoster):
__type__ = "hoster"
__pattern__ = r"^unmatchable$"
__version__ = "0.17"
- __description__ = """Base Plugin when any other didnt fit"""
+ __description__ = """Base Plugin when any other didn't match"""
__author_name__ = ("RaNaN")
__author_mail__ = ("RaNaN@pyload.org")
@@ -31,7 +31,7 @@ class BasePlugin(Hoster):
#TODO: remove debug
if pyfile.url.lower().startswith("debug"):
- self.decryptCaptcha("http://download.pyload.org/pie.png", imgtype="png")
+ self.decryptCaptcha("http://forum.pyload.org/lib/tpl/pyload/images/pyload-logo-edited3.5-new-font-small.png", imgtype="png")
self.download("http://download.pyload.org/random100.bin")
return
#
diff --git a/pyload/remote/apitypes.py b/pyload/remote/apitypes.py
index 196491083..d487599c3 100644
--- a/pyload/remote/apitypes.py
+++ b/pyload/remote/apitypes.py
@@ -51,12 +51,13 @@ class InputType:
Folder = 4
Textbox = 5
Password = 6
- Bool = 7
- Click = 8
- Select = 9
- Multiple = 10
- List = 11
- Table = 12
+ Time = 7
+ Bool = 8
+ Click = 9
+ Select = 10
+ Multiple = 11
+ List = 12
+ Table = 13
class Interaction:
All = 0
@@ -127,13 +128,13 @@ class AddonService(BaseObject):
self.media = media
class ConfigHolder(BaseObject):
- __slots__ = ['name', 'label', 'description', 'long_description', 'items', 'info']
+ __slots__ = ['name', 'label', 'description', 'explanation', 'items', 'info']
- def __init__(self, name=None, label=None, description=None, long_description=None, items=None, info=None):
+ def __init__(self, name=None, label=None, description=None, explanation=None, items=None, info=None):
self.name = name
self.label = label
self.description = description
- self.long_description = long_description
+ self.explanation = explanation
self.items = items
self.info = info
@@ -149,14 +150,13 @@ class ConfigInfo(BaseObject):
self.activated = activated
class ConfigItem(BaseObject):
- __slots__ = ['name', 'label', 'description', 'input', 'default_value', 'value']
+ __slots__ = ['name', 'label', 'description', 'input', 'value']
- def __init__(self, name=None, label=None, description=None, input=None, default_value=None, value=None):
+ def __init__(self, name=None, label=None, description=None, input=None, value=None):
self.name = name
self.label = label
self.description = description
self.input = input
- self.default_value = default_value
self.value = value
class DownloadInfo(BaseObject):
@@ -211,20 +211,20 @@ class Forbidden(ExceptionObject):
pass
class Input(BaseObject):
- __slots__ = ['type', 'data']
+ __slots__ = ['type', 'default_value', 'data']
- def __init__(self, type=None, data=None):
+ def __init__(self, type=None, default_value=None, data=None):
self.type = type
+ self.default_value = default_value
self.data = data
class InteractionTask(BaseObject):
- __slots__ = ['iid', 'type', 'input', 'default_value', 'title', 'description', 'plugin']
+ __slots__ = ['iid', 'type', 'input', 'title', 'description', 'plugin']
- def __init__(self, iid=None, type=None, input=None, default_value=None, title=None, description=None, plugin=None):
+ def __init__(self, iid=None, type=None, input=None, title=None, description=None, plugin=None):
self.iid = iid
self.type = type
self.input = input
- self.default_value = default_value
self.title = title
self.description = description
self.plugin = plugin
diff --git a/pyload/remote/apitypes_debug.py b/pyload/remote/apitypes_debug.py
index 96673cc99..7c62a6277 100644
--- a/pyload/remote/apitypes_debug.py
+++ b/pyload/remote/apitypes_debug.py
@@ -23,17 +23,17 @@ classes = {
'AddonService' : [basestring, basestring, (list, basestring), (None, int)],
'ConfigHolder' : [basestring, basestring, basestring, basestring, (list, ConfigItem), (None, (list, AddonInfo))],
'ConfigInfo' : [basestring, basestring, basestring, basestring, bool, (None, bool)],
- 'ConfigItem' : [basestring, basestring, basestring, Input, basestring, basestring],
+ 'ConfigItem' : [basestring, basestring, basestring, Input, basestring],
'DownloadInfo' : [basestring, basestring, basestring, int, basestring, basestring],
'DownloadProgress' : [int, int, int, int],
'EventInfo' : [basestring, (list, basestring)],
'FileDoesNotExists' : [int],
'FileInfo' : [int, basestring, int, int, int, int, int, int, int, (None, DownloadInfo)],
- 'Input' : [int, (None, basestring)],
- 'InteractionTask' : [int, int, Input, (None, basestring), basestring, basestring, basestring],
+ 'Input' : [int, (None, basestring), (None, basestring)],
+ 'InteractionTask' : [int, int, Input, basestring, basestring, basestring],
'InvalidConfigSection' : [basestring],
'LinkStatus' : [basestring, basestring, basestring, int, int, basestring],
- 'OnlineCheck' : [int, (None, (dict, basestring, LinkStatus))],
+ 'OnlineCheck' : [int, (dict, basestring, LinkStatus)],
'PackageDoesNotExists' : [int],
'PackageInfo' : [int, basestring, basestring, int, int, basestring, basestring, basestring, int, (list, basestring), int, bool, int, PackageStats, (list, int), (list, int)],
'PackageStats' : [int, int, int, int],
diff --git a/pyload/remote/pyload.thrift b/pyload/remote/pyload.thrift
index 57d7e0a0a..f218896ef 100644
--- a/pyload/remote/pyload.thrift
+++ b/pyload/remote/pyload.thrift
@@ -77,6 +77,7 @@ enum InputType {
Folder,
Textbox,
Password,
+ Time,
Bool, // confirm like, yes or no dialog
Click, // for positional captchas
Select, // select from list
@@ -113,7 +114,8 @@ enum Role {
struct Input {
1: InputType type,
- 2: optional JSONString data,
+ 2: optional JSONString default_value,
+ 3: optional JSONString data,
}
struct DownloadProgress {
@@ -215,10 +217,9 @@ struct InteractionTask {
1: InteractionID iid,
2: Interaction type,
3: Input input,
- 4: optional JSONString default_value,
- 5: string title,
- 6: string description,
- 7: PluginName plugin,
+ 4: string title,
+ 5: string description,
+ 6: PluginName plugin,
}
struct AddonService {
@@ -239,15 +240,14 @@ struct ConfigItem {
2: string label,
3: string description,
4: Input input,
- 5: JSONString default_value,
- 6: JSONString value,
+ 5: JSONString value,
}
struct ConfigHolder {
1: string name, // for plugin this is the PluginName
2: string label,
3: string description,
- 4: string long_description,
+ 4: string explanation,
5: list<ConfigItem> items,
6: optional list<AddonInfo> info,
}
diff --git a/pyload/remote/wsbackend/AsyncHandler.py b/pyload/remote/wsbackend/AsyncHandler.py
index f1c584b7d..b936de898 100644
--- a/pyload/remote/wsbackend/AsyncHandler.py
+++ b/pyload/remote/wsbackend/AsyncHandler.py
@@ -43,7 +43,7 @@ class AsyncHandler(AbstractHandler):
PATH = "/async"
COMMAND = "start"
- PROGRESS_INTERVAL = 2
+ PROGRESS_INTERVAL = 1.5
EVENT_PATTERN = re.compile(r"^(package|file|interaction)", re.I)
INTERACTION = Interaction.All
diff --git a/pyload/utils/__init__.py b/pyload/utils/__init__.py
index da7b81af7..c9c24ac40 100644
--- a/pyload/utils/__init__.py
+++ b/pyload/utils/__init__.py
@@ -42,13 +42,13 @@ def remove_chars(string, repl):
def get_console_encoding(enc):
- if os.name == "nt":
+ if os.name == "nt":
if enc == "cp65001": # aka UTF-8
print "WARNING: Windows codepage 65001 is not supported."
enc = "cp850"
else:
enc = "utf8"
-
+
return enc
def compare_time(start, end):
@@ -199,6 +199,10 @@ def accumulate(it, inv_map=None):
def to_string(value):
return str(value) if not isinstance(value, basestring) else value
+def to_bool(value):
+ if not isinstance(value, basestring): return value
+ return True if value.lower() in ("1", "true", "on", "an", "yes") else False
+
def to_int(string, default=0):
""" return int from string or default """
try:
@@ -206,26 +210,6 @@ def to_int(string, default=0):
except ValueError:
return default
-def from_string(value, typ=None):
- """ cast value to given type, unicode for strings """
-
- # value is no string
- if not isinstance(value, basestring):
- return value
-
- value = decode(value)
-
- if typ == "int":
- return int(value)
- elif typ == "bool":
- return True if value.lower() in ("1", "true", "on", "an", "yes") else False
- elif typ == "time":
- if not value: value = "0:00"
- if not ":" in value: value += ":00"
- return value
- else:
- return value
-
def get_index(l, value):
""" .index method that also works on tuple and python 2.5 """
for pos, t in enumerate(l):
diff --git a/pyload/web/app/scripts/utils/apitypes.js b/pyload/web/app/scripts/utils/apitypes.js
index cbbc9064f..d3b984923 100644
--- a/pyload/web/app/scripts/utils/apitypes.js
+++ b/pyload/web/app/scripts/utils/apitypes.js
@@ -6,7 +6,7 @@ define([], function() {
DownloadState: {'Failed': 3, 'All': 0, 'Unmanaged': 4, 'Finished': 1, 'Unfinished': 2},
DownloadStatus: {'Downloading': 10, 'NA': 0, 'Processing': 14, 'Waiting': 9, 'Decrypting': 13, 'Paused': 4, 'Failed': 7, 'Finished': 5, 'Skipped': 6, 'Unknown': 16, 'Aborted': 12, 'Online': 2, 'TempOffline': 11, 'Offline': 1, 'Custom': 15, 'Starting': 8, 'Queued': 3},
FileStatus: {'Remote': 2, 'Ok': 0, 'Missing': 1},
- InputType: {'Multiple': 10, 'Int': 2, 'NA': 0, 'List': 11, 'Bool': 7, 'File': 3, 'Text': 1, 'Table': 12, 'Folder': 4, 'Password': 6, 'Click': 8, 'Select': 9, 'Textbox': 5},
+ InputType: {'Multiple': 11, 'Int': 2, 'NA': 0, 'Time': 7, 'List': 12, 'Bool': 8, 'File': 3, 'Text': 1, 'Table': 13, 'Folder': 4, 'Password': 6, 'Click': 9, 'Select': 10, 'Textbox': 5},
Interaction: {'Captcha': 2, 'All': 0, 'Query': 4, 'Notification': 1},
MediaType: {'All': 0, 'Audio': 2, 'Image': 4, 'Other': 1, 'Video': 8, 'Document': 16, 'Archive': 32},
PackageStatus: {'Paused': 1, 'Remote': 3, 'Folder': 2, 'Ok': 0},
diff --git a/pyload/web/app/scripts/views/input/inputLoader.js b/pyload/web/app/scripts/views/input/inputLoader.js
index 11665abb4..04d591d30 100644
--- a/pyload/web/app/scripts/views/input/inputLoader.js
+++ b/pyload/web/app/scripts/views/input/inputLoader.js
@@ -2,7 +2,7 @@ define(['./textInput'], function(textInput) {
'use strict';
// selects appropriate input element
- return function(input, value, default_value, description) {
+ return function(input) {
return textInput;
};
}); \ No newline at end of file
diff --git a/pyload/web/app/scripts/views/input/inputView.js b/pyload/web/app/scripts/views/input/inputView.js
index 1fbe5042d..1860fcaf1 100644
--- a/pyload/web/app/scripts/views/input/inputView.js
+++ b/pyload/web/app/scripts/views/input/inputView.js
@@ -8,16 +8,16 @@ define(['jquery', 'backbone', 'underscore'], function($, Backbone, _) {
input: null,
value: null,
- default_value: null,
description: null,
+ default_value: null,
// enables tooltips
tooltip: true,
initialize: function(options) {
this.input = options.input;
+ this.default_value = this.input.default_value;
this.value = options.value;
- this.default_value = options.default_value;
this.description = options.description;
},
diff --git a/pyload/web/app/scripts/views/queryModal.js b/pyload/web/app/scripts/views/queryModal.js
index c748e1657..ce624814a 100644
--- a/pyload/web/app/scripts/views/queryModal.js
+++ b/pyload/web/app/scripts/views/queryModal.js
@@ -39,7 +39,7 @@ define(['jquery', 'underscore', 'app', 'views/abstract/modalView', './input/inpu
// instantiate the input
var input = this.model.get('input');
var InputView = load_input(input);
- this.input = new InputView(input);
+ this.input = new InputView({input: input});
// only renders after wards
this.$('#inputField').append(this.input.render().el);
},
diff --git a/tests/manager/test_configManager.py b/tests/manager/test_configManager.py
index 97b43dd66..ca572bf20 100644
--- a/tests/manager/test_configManager.py
+++ b/tests/manager/test_configManager.py
@@ -5,15 +5,16 @@ from collections import defaultdict
from nose.tools import raises
-from tests.helper.Stubs import Core
+from tests.helper.Stubs import Core, adminUser, normalUser
from pyload.Api import InvalidConfigSection
from pyload.database import DatabaseBackend
from pyload.config.ConfigParser import ConfigParser
from pyload.config.ConfigManager import ConfigManager
+from pyload.utils import primary_uid
-adminUser = None
-normalUser = 1
+adminUser = primary_uid(adminUser)
+normalUser = primary_uid(normalUser)
class TestConfigManager(TestCase):
@@ -36,7 +37,7 @@ class TestConfigManager(TestCase):
def addConfig(self):
self.config.addConfigSection("plugin", "Name", "desc", "something",
- [("value", "str", "label", "desc", "default")])
+ [("value", "str", "label", "default")])
def test_db(self):
@@ -115,7 +116,7 @@ class TestConfigManager(TestCase):
def test_get_section(self):
self.addConfig()
- assert self.config.getSection("plugin")[0].name == "Name"
+ self.assertEqual(self.config.getSection("plugin")[0].label, "Name")
# TODO: more save tests are needed
def test_saveValues(self):