diff options
Diffstat (limited to 'pyload')
-rw-r--r-- | pyload/PluginManager.py | 48 | ||||
-rw-r--r-- | pyload/api/ConfigApi.py | 15 | ||||
-rw-r--r-- | pyload/config/ConfigManager.py | 7 | ||||
-rw-r--r-- | pyload/config/ConfigParser.py | 80 | ||||
-rw-r--r-- | pyload/config/convert.py | 38 | ||||
-rw-r--r-- | pyload/config/default.py | 150 | ||||
-rw-r--r-- | pyload/interaction/InteractionManager.py | 10 | ||||
-rw-r--r-- | pyload/plugins/Account.py | 6 | ||||
-rw-r--r-- | pyload/plugins/Base.py | 14 | ||||
-rw-r--r-- | pyload/plugins/hoster/BasePlugin.py | 4 | ||||
-rw-r--r-- | pyload/remote/apitypes.py | 34 | ||||
-rw-r--r-- | pyload/remote/apitypes_debug.py | 8 | ||||
-rw-r--r-- | pyload/remote/pyload.thrift | 16 | ||||
-rw-r--r-- | pyload/remote/wsbackend/AsyncHandler.py | 2 | ||||
-rw-r--r-- | pyload/utils/__init__.py | 28 | ||||
-rw-r--r-- | pyload/web/app/scripts/utils/apitypes.js | 2 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/input/inputLoader.js | 2 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/input/inputView.js | 4 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/queryModal.js | 2 |
19 files changed, 259 insertions, 211 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); }, |