[UI] Replace optparse with argparse for cmd arguments handling
optparse is deprecation and succeeded by argparse. See https://www.python.org/dev/peps/pep-0389
This commit is contained in:
parent
aa82efd4f1
commit
7b54a2a1ee
|
@ -162,6 +162,28 @@ def osx_check():
|
|||
return platform.system() == "Darwin"
|
||||
|
||||
|
||||
def linux_check():
|
||||
"""
|
||||
Checks if the current platform is Linux
|
||||
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
return platform.system() == "Linux"
|
||||
|
||||
|
||||
def get_os_version():
|
||||
if windows_check():
|
||||
return platform.win32_ver()
|
||||
elif osx_check():
|
||||
return platform.mac_ver()
|
||||
elif linux_check():
|
||||
return platform.linux_distribution()
|
||||
else:
|
||||
return (platform.release(), )
|
||||
|
||||
|
||||
def get_pixmap(fname):
|
||||
"""
|
||||
Provides easy access to files in the deluge/ui/data/pixmaps folder within the Deluge egg
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import deluge.common
|
||||
import deluge.configmanager
|
||||
from deluge.log import setup_logger
|
||||
|
||||
|
||||
def version_callback(option, opt_str, value, parser):
|
||||
print(os.path.basename(sys.argv[0]) + ": " + deluge.common.get_version())
|
||||
try:
|
||||
from deluge._libtorrent import lt
|
||||
print("libtorrent: %s" % lt.version)
|
||||
except ImportError:
|
||||
pass
|
||||
print("Python: %s" % platform.python_version())
|
||||
for version in (platform.linux_distribution(), platform.win32_ver(), platform.mac_ver(), ("n/a",)):
|
||||
if filter(None, version): # pylint: disable=bad-builtin
|
||||
print("OS: %s %s" % (platform.system(), " ".join(version)))
|
||||
break
|
||||
raise SystemExit
|
||||
|
||||
|
||||
class CommonOptionParser(optparse.OptionParser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
optparse.OptionParser.__init__(self, *args, **kwargs)
|
||||
self.common_group = optparse.OptionGroup(self, _("Common Options"))
|
||||
self.common_group.add_option("-v", "--version", action="callback", callback=version_callback,
|
||||
help="Show program's version number and exit")
|
||||
self.common_group.add_option("-c", "--config", dest="config", action="store", type="str",
|
||||
help="Set the config folder location")
|
||||
self.common_group.add_option("-l", "--logfile", dest="logfile", action="store", type="str",
|
||||
help="Output to designated logfile instead of stdout")
|
||||
self.common_group.add_option("-L", "--loglevel", dest="loglevel", action="store", type="str",
|
||||
help="Set the log level: none, info, warning, error, critical, debug")
|
||||
self.common_group.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
|
||||
help="Sets the log level to 'none', this is the same as `-L none`")
|
||||
self.common_group.add_option("-r", "--rotate-logs", action="store_true", default=False,
|
||||
help="Rotate logfiles.")
|
||||
self.add_option_group(self.common_group)
|
||||
|
||||
def parse_args(self, *args):
|
||||
options, args = optparse.OptionParser.parse_args(self, *args)
|
||||
|
||||
# Setup the logger
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
if options.loglevel:
|
||||
options.loglevel = options.loglevel.lower()
|
||||
|
||||
logfile_mode = 'w'
|
||||
if options.rotate_logs:
|
||||
logfile_mode = 'a'
|
||||
|
||||
# Setup the logger
|
||||
setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode)
|
||||
|
||||
if options.config:
|
||||
if not deluge.configmanager.set_config_dir(options.config):
|
||||
log = logging.getLogger(__name__)
|
||||
log.error("There was an error setting the config dir! Exiting..")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not os.path.exists(deluge.common.get_default_config_dir()):
|
||||
os.makedirs(deluge.common.get_default_config_dir())
|
||||
|
||||
return options, args
|
|
@ -15,7 +15,6 @@
|
|||
"""Main starting point for Deluge. Contains the main() entry point."""
|
||||
from __future__ import print_function
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
from logging import FileHandler, getLogger
|
||||
|
@ -38,25 +37,27 @@ def start_ui():
|
|||
|
||||
# Setup the argument parser
|
||||
parser = CommonOptionParser()
|
||||
group = optparse.OptionGroup(parser, _("Default Options"))
|
||||
group = parser.add_argument_group(_("Default Options"))
|
||||
|
||||
ui_entrypoints = dict([(entrypoint.name, entrypoint.load())
|
||||
for entrypoint in pkg_resources.iter_entry_points('deluge.ui')])
|
||||
|
||||
cmd_help = ['The UI that you wish to launch. The UI choices are:']
|
||||
max_len = 0
|
||||
for k, v in ui_entrypoints.iteritems():
|
||||
cmdline = getattr(v, 'cmdline', "")
|
||||
max_len = max(max_len, len(cmdline))
|
||||
|
||||
cmd_help.extend(["%s -- %s" % (k, getattr(v, 'cmdline', "")) for k, v in ui_entrypoints.iteritems()])
|
||||
|
||||
parser.add_option("-u", "--ui", dest="ui",
|
||||
choices=ui_entrypoints.keys(), help="\n\t ".join(cmd_help), action="store")
|
||||
group.add_option("-a", "--args", dest="args",
|
||||
help="Arguments to pass to UI, -a '--option args'", action="store", type="str")
|
||||
group.add_option("-s", "--set-default-ui", dest="default_ui",
|
||||
help="Sets the default UI to be run when no UI is specified", action="store", type="str")
|
||||
parser.add_argument("-u", "--ui", action="store",
|
||||
choices=ui_entrypoints.keys(), help="\n* ".join(cmd_help))
|
||||
group.add_argument("-a", "--args", action="store",
|
||||
help='Arguments to pass to the UI. Multiple args must be quoted, e.g. -a "--option args"')
|
||||
group.add_argument("-s", "--set-default-ui", dest="default_ui", choices=ui_entrypoints.keys(),
|
||||
help="Sets the default UI to be run when no UI is specified", action="store")
|
||||
|
||||
parser.add_option_group(group)
|
||||
|
||||
# Get the options and args from the OptionParser
|
||||
(options, args) = parser.parse_args(deluge.common.unicode_argv()[1:])
|
||||
options = parser.parse_args(deluge.common.unicode_argv()[1:])
|
||||
|
||||
config = deluge.configmanager.ConfigManager("ui.conf", DEFAULT_PREFS)
|
||||
log = getLogger(__name__)
|
||||
|
@ -69,7 +70,6 @@ def start_ui():
|
|||
|
||||
log.info("Deluge ui %s", deluge.common.get_version())
|
||||
log.debug("options: %s", options)
|
||||
log.debug("args: %s", args)
|
||||
|
||||
selected_ui = options.ui if options.ui else config["default_ui"]
|
||||
|
||||
|
@ -81,15 +81,13 @@ def start_ui():
|
|||
if options.args:
|
||||
import shlex
|
||||
client_args.extend(shlex.split(options.args))
|
||||
client_args.extend(args)
|
||||
|
||||
try:
|
||||
ui = ui_entrypoints[selected_ui](skip_common=True)
|
||||
ui = ui_entrypoints[selected_ui](parser=parser)
|
||||
except KeyError as ex:
|
||||
log.error("Unable to find the requested UI: '%s'. Please select a different UI with the '-u' option"
|
||||
" or alternatively use the '-s' option to select a different default UI.", selected_ui)
|
||||
except ImportError as ex:
|
||||
import sys
|
||||
import traceback
|
||||
error_type, error_value, tb = sys.exc_info()
|
||||
stack = traceback.extract_tb(tb)
|
||||
|
@ -106,6 +104,31 @@ def start_ui():
|
|||
ui.start(client_args)
|
||||
|
||||
|
||||
def add_daemon_options(parser):
|
||||
group = parser.add_argument_group('Daemon Options')
|
||||
group.add_argument("-p", "--port", metavar="<port>", action="store", type=int,
|
||||
help="The port the daemon will listen on")
|
||||
group.add_argument("-i", "--interface", metavar="<iface>", dest="listen_interface",
|
||||
help="Interface daemon will listen for bittorrent connections on, "
|
||||
"this should be an IP address", action="store")
|
||||
group.add_argument("-u", "--ui-interface", metavar="<iface>", action="store",
|
||||
help="Interface daemon will listen for UI connections on, "
|
||||
"this should be an IP address")
|
||||
if not deluge.common.windows_check():
|
||||
group.add_argument("-d", "--do-not-daemonize", dest="donot",
|
||||
help="Do not daemonize", action="store_true", default=False)
|
||||
group.add_argument("-P", "--pidfile", metavar="<pidfile>",
|
||||
help="Use pidfile to store process id", action="store")
|
||||
if not deluge.common.windows_check():
|
||||
group.add_argument("-U", "--user", metavar="<user>", action="store",
|
||||
help="User to switch to. Only use it when starting as root")
|
||||
group.add_argument("-g", "--group", metavar="<group>", action="store",
|
||||
help="Group to switch to. Only use it when starting as root")
|
||||
group.add_argument("--read-only-config-keys",
|
||||
help="List of comma-separated config keys that will not be modified by set_config RPC.",
|
||||
action="store", type=str, default="")
|
||||
|
||||
|
||||
def start_daemon(skip_start=False):
|
||||
"""
|
||||
Entry point for daemon script
|
||||
|
@ -124,38 +147,10 @@ def start_daemon(skip_start=False):
|
|||
warnings.filterwarnings('ignore', category=DeprecationWarning, module='twisted')
|
||||
|
||||
# Setup the argument parser
|
||||
parser = CommonOptionParser(usage="%prog [options] [actions]")
|
||||
parser = CommonOptionParser()
|
||||
add_daemon_options(parser)
|
||||
|
||||
group = optparse.OptionGroup(parser, _("Daemon Options"))
|
||||
group.add_option("-p", "--port", dest="port",
|
||||
help="Port daemon will listen on", action="store", type="int")
|
||||
group.add_option("-i", "--interface", dest="listen_interface",
|
||||
help="Interface daemon will listen for bittorrent connections on, "
|
||||
"this should be an IP address", metavar="IFACE",
|
||||
action="store", type="str")
|
||||
group.add_option("-u", "--ui-interface", dest="ui_interface",
|
||||
help="Interface daemon will listen for UI connections on, this should be "
|
||||
"an IP address", metavar="IFACE", action="store", type="str")
|
||||
if not (deluge.common.windows_check() or deluge.common.osx_check()):
|
||||
group.add_option("-d", "--do-not-daemonize", dest="donot",
|
||||
help="Do not daemonize", action="store_true", default=False)
|
||||
group.add_option("-P", "--pidfile", dest="pidfile",
|
||||
help="Use pidfile to store process id", action="store", type="str")
|
||||
if not deluge.common.windows_check():
|
||||
group.add_option("-U", "--user", dest="user",
|
||||
help="User to switch to. Only use it when starting as root", action="store", type="str")
|
||||
group.add_option("-g", "--group", dest="group",
|
||||
help="Group to switch to. Only use it when starting as root", action="store", type="str")
|
||||
group.add_option("--read-only-config-keys",
|
||||
help="List of comma-separated config keys that will not be modified by set_config RPC.",
|
||||
action="store", type="str", default="")
|
||||
group.add_option("--profile", dest="profile", action="store_true", default=False,
|
||||
help="Profiles the daemon")
|
||||
|
||||
parser.add_option_group(group)
|
||||
|
||||
# Get the options and args from the OptionParser
|
||||
(options, args) = parser.parse_args()
|
||||
options = parser.parse_args()
|
||||
|
||||
# Check for any daemons running with this same config
|
||||
from deluge.core.daemon import check_running_daemon
|
||||
|
|
|
@ -10,42 +10,31 @@ from __future__ import print_function
|
|||
|
||||
import os
|
||||
import sys
|
||||
from argparse import ArgumentParser
|
||||
from datetime import datetime
|
||||
from optparse import OptionParser
|
||||
|
||||
import deluge.common
|
||||
|
||||
parser = OptionParser()
|
||||
parser.add_option("-n", "--name", dest="name", help="plugin name")
|
||||
parser.add_option("-m", "--module-name", dest="module", help="plugin name")
|
||||
parser.add_option("-p", "--basepath", dest="path", help="base path")
|
||||
parser.add_option("-a", "--author-name", dest="author_name", help="author name,for the GPL header")
|
||||
parser.add_option("-e", "--author-email", dest="author_email", help="author email,for the GPL header")
|
||||
parser.add_option("-u", "--url", dest="url", help="Homepage URL")
|
||||
parser.add_option("-c", "--config", dest="configdir", help="location of deluge configuration")
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("-n", "--name", metavar="<plugin name>", required=True, help="Plugin name")
|
||||
parser.add_argument("-m", "--module-name", metavar="<module name>", help="Module name")
|
||||
parser.add_argument("-p", "--basepath", metavar="<path>", required=True, help="Base path")
|
||||
parser.add_argument("-a", "--author-name", metavar="<author name>", required=True,
|
||||
help="Author name,for the GPL header")
|
||||
parser.add_argument("-e", "--author-email", metavar="<author email>", required=True,
|
||||
help="Author email,for the GPL header")
|
||||
parser.add_argument("-u", "--url", metavar="<URL>", help="Homepage URL")
|
||||
parser.add_argument("-c", "--config", metavar="<Config dir>", dest="configdir", help="Location of deluge configuration")
|
||||
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
options = parser.parse_args()
|
||||
|
||||
|
||||
def create_plugin():
|
||||
if not options.name:
|
||||
print("--name is mandatory , use -h for more info")
|
||||
return
|
||||
if not options.path:
|
||||
print("--basepath is mandatory , use -h for more info")
|
||||
return
|
||||
if not options.author_email:
|
||||
print("--author-email is mandatory , use -h for more info")
|
||||
return
|
||||
if not options.author_email:
|
||||
print("--author-name is mandatory , use -h for more info")
|
||||
return
|
||||
|
||||
if not options.url:
|
||||
options.url = ""
|
||||
|
||||
if not os.path.exists(options.path):
|
||||
if not os.path.exists(options.basepath):
|
||||
print("basepath does not exist")
|
||||
return
|
||||
|
||||
|
@ -57,9 +46,9 @@ def create_plugin():
|
|||
real_name = options.name
|
||||
name = real_name.replace(" ", "_")
|
||||
safe_name = name.lower()
|
||||
if options.module:
|
||||
safe_name = options.module.lower()
|
||||
plugin_base = os.path.realpath(os.path.join(options.path, name))
|
||||
if options.module_name:
|
||||
safe_name = options.module_name.lower()
|
||||
plugin_base = os.path.realpath(os.path.join(options.basepath, name))
|
||||
deluge_namespace = os.path.join(plugin_base, "deluge")
|
||||
plugins_namespace = os.path.join(deluge_namespace, "plugins")
|
||||
src = os.path.join(plugins_namespace, safe_name)
|
||||
|
@ -121,16 +110,16 @@ def create_plugin():
|
|||
CORE = """
|
||||
import logging
|
||||
from deluge.plugins.pluginbase import CorePluginBase
|
||||
import deluge.component as component
|
||||
import deluge.configmanager
|
||||
from deluge.core.rpcserver import export
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
"test":"NiNiNi"
|
||||
"test": "NiNiNi"
|
||||
}
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Core(CorePluginBase):
|
||||
def enable(self):
|
||||
self.config = deluge.configmanager.ConfigManager("%(safe_name)s.conf", DEFAULT_PREFS)
|
||||
|
@ -157,22 +146,25 @@ class Core(CorePluginBase):
|
|||
INIT = """
|
||||
from deluge.plugins.init import PluginInitBase
|
||||
|
||||
|
||||
class CorePlugin(PluginInitBase):
|
||||
def __init__(self, plugin_name):
|
||||
from core import Core as _plugin_cls
|
||||
self._plugin_cls = _plugin_cls
|
||||
from core import Core as PluginClass
|
||||
self._plugin_cls = PluginClass
|
||||
super(CorePlugin, self).__init__(plugin_name)
|
||||
|
||||
|
||||
class GtkUIPlugin(PluginInitBase):
|
||||
def __init__(self, plugin_name):
|
||||
from gtkui import GtkUI as _plugin_cls
|
||||
self._plugin_cls = _plugin_cls
|
||||
from gtkui import GtkUI as PluginClass
|
||||
self._plugin_cls = PluginClass
|
||||
super(GtkUIPlugin, self).__init__(plugin_name)
|
||||
|
||||
|
||||
class WebUIPlugin(PluginInitBase):
|
||||
def __init__(self, plugin_name):
|
||||
from webui import WebUI as _plugin_cls
|
||||
self._plugin_cls = _plugin_cls
|
||||
from webui import WebUI as PluginClass
|
||||
self._plugin_cls = PluginClass
|
||||
super(WebUIPlugin, self).__init__(plugin_name)
|
||||
"""
|
||||
|
||||
|
@ -201,8 +193,8 @@ setup(
|
|||
long_description=__long_description__ if __long_description__ else __description__,
|
||||
|
||||
packages=find_packages(),
|
||||
namespace_packages = ["deluge", "deluge.plugins"],
|
||||
package_data = __pkg_data__,
|
||||
namespace_packages=["deluge", "deluge.plugins"],
|
||||
package_data=__pkg_data__,
|
||||
|
||||
entry_points=\"\"\"
|
||||
[deluge.plugin.core]
|
||||
|
@ -218,7 +210,8 @@ setup(
|
|||
COMMON = """
|
||||
|
||||
def get_resource(filename):
|
||||
import pkg_resources, os
|
||||
import pkg_resources
|
||||
import os
|
||||
return pkg_resources.resource_filename("deluge.plugins.%(safe_name)s",
|
||||
os.path.join("data", filename))
|
||||
"""
|
||||
|
@ -230,12 +223,12 @@ import logging
|
|||
from deluge.ui.client import client
|
||||
from deluge.plugins.pluginbase import GtkPluginBase
|
||||
import deluge.component as component
|
||||
import deluge.common
|
||||
|
||||
from common import get_resource
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GtkUI(GtkPluginBase):
|
||||
def enable(self):
|
||||
self.glade = gtk.glade.XML(get_resource("config.glade"))
|
||||
|
@ -252,7 +245,7 @@ class GtkUI(GtkPluginBase):
|
|||
def on_apply_prefs(self):
|
||||
log.debug("applying prefs for %(name)s")
|
||||
config = {
|
||||
"test":self.glade.get_widget("txt_test").get_text()
|
||||
"test": self.glade.get_widget("txt_test").get_text()
|
||||
}
|
||||
client.%(safe_name)s.set_config(config)
|
||||
|
||||
|
@ -296,13 +289,13 @@ GLADE = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|||
WEBUI = """
|
||||
import logging
|
||||
from deluge.ui.client import client
|
||||
from deluge import component
|
||||
from deluge.plugins.pluginbase import WebPluginBase
|
||||
|
||||
from common import get_resource
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebUI(WebPluginBase):
|
||||
|
||||
scripts = [get_resource("%(safe_name)s.js")]
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import deluge.common
|
||||
import deluge.configmanager
|
||||
import deluge.log
|
||||
from deluge.log import setup_logger
|
||||
|
||||
|
||||
def get_version():
|
||||
version_str = "%s\n" % (deluge.common.get_version())
|
||||
try:
|
||||
from deluge._libtorrent import lt
|
||||
version_str += "libtorrent: %s\n" % lt.version
|
||||
except ImportError:
|
||||
pass
|
||||
version_str += "Python: %s\n" % platform.python_version()
|
||||
version_str += "OS: %s %s\n" % (platform.system(), " ".join(deluge.common.get_os_version()))
|
||||
return version_str
|
||||
|
||||
|
||||
class DelugeTextHelpFormatter(argparse.RawDescriptionHelpFormatter):
|
||||
"""Help message formatter which retains formatting of all help text.
|
||||
"""
|
||||
|
||||
def _split_lines(self, text, width):
|
||||
"""
|
||||
Do not remove whitespaces in string but still wrap text to max width.
|
||||
Instead of passing the entire text to textwrap.wrap, split and pass each
|
||||
line instead. This way list formatting is not mangled by textwrap.wrap.
|
||||
"""
|
||||
wrapped_lines = []
|
||||
for l in text.splitlines():
|
||||
wrapped_lines.extend(textwrap.wrap(l, width))
|
||||
return wrapped_lines
|
||||
|
||||
def _format_action_invocation(self, action):
|
||||
"""
|
||||
Combines the options with comma and displays the argument
|
||||
value only once instead of after both options.
|
||||
Instead of: -s <arg>, --long-opt <arg>
|
||||
Show : -s, --long-opt <arg>
|
||||
|
||||
"""
|
||||
if not action.option_strings:
|
||||
metavar, = self._metavar_formatter(action, action.dest)(1)
|
||||
return metavar
|
||||
else:
|
||||
parts = []
|
||||
# if the Optional doesn't take a value, format is:
|
||||
# -s, --long
|
||||
if action.nargs == 0:
|
||||
parts.extend(action.option_strings)
|
||||
|
||||
# if the Optional takes a value, format is:
|
||||
# -s, --long ARGS
|
||||
else:
|
||||
default = action.dest.upper()
|
||||
args_string = self._format_args(action, default)
|
||||
opt = ", ".join(action.option_strings)
|
||||
parts.append('%s %s' % (opt, args_string))
|
||||
return ', '.join(parts)
|
||||
|
||||
|
||||
class HelpAction(argparse._HelpAction):
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
if hasattr(parser, "subparser"):
|
||||
subparser = getattr(parser, "subparser")
|
||||
# If -h on a subparser is given, the subparser will exit after help message
|
||||
subparser.parse_args()
|
||||
parser.print_help()
|
||||
parser.exit()
|
||||
|
||||
|
||||
class BaseArgParser(argparse.ArgumentParser):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if "formatter_class" not in kwargs:
|
||||
kwargs["formatter_class"] = lambda prog: DelugeTextHelpFormatter(prog, max_help_position=33, width=90)
|
||||
super(BaseArgParser, self).__init__(*args, add_help=False, **kwargs)
|
||||
|
||||
self.group = self.add_argument_group('Common Options')
|
||||
self.group.add_argument('--version', action='version', version='%(prog)s ' + get_version(),
|
||||
help="Show program's version number and exit")
|
||||
self.group.add_argument("-c", "--config", action="store", metavar='<config>',
|
||||
help="Set the config directory path")
|
||||
self.group.add_argument("-l", "--logfile", action="store", metavar='<logfile>',
|
||||
help="Output to designated logfile instead of stdout")
|
||||
self.group.add_argument("-L", "--loglevel", action="store", choices=deluge.log.levels, metavar='<level>',
|
||||
help="Set the log level: %s" % ", ".join(deluge.log.levels))
|
||||
self.group.add_argument("-q", "--quiet", action="store_true", default=False,
|
||||
help="Sets the log level to 'none', this is the same as `-L none`")
|
||||
self.group.add_argument("-r", "--rotate-logs", action="store_true", default=False,
|
||||
help="Rotate logfiles.")
|
||||
self.group.add_argument("-h", "--help", action=HelpAction, help='Show this help message and exit')
|
||||
|
||||
def parse_args(self, *args):
|
||||
options, remaining = super(BaseArgParser, self).parse_known_args(*args)
|
||||
options.remaining = remaining
|
||||
|
||||
# Setup the logger
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
if options.loglevel:
|
||||
options.loglevel = options.loglevel.lower()
|
||||
|
||||
logfile_mode = 'w'
|
||||
if options.rotate_logs:
|
||||
logfile_mode = 'a'
|
||||
|
||||
# Setup the logger
|
||||
setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode)
|
||||
|
||||
if options.config:
|
||||
if not deluge.configmanager.set_config_dir(options.config):
|
||||
log = logging.getLogger(__name__)
|
||||
log.error("There was an error setting the config dir! Exiting..")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not os.path.exists(deluge.common.get_default_config_dir()):
|
||||
os.makedirs(deluge.common.get_default_config_dir())
|
||||
|
||||
return options
|
|
@ -379,14 +379,14 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
|
||||
def authenticate(self, username, password):
|
||||
log.debug("%s.authenticate: %s", self.__class__.__name__, username)
|
||||
self.login_deferred = defer.Deferred()
|
||||
login_deferred = defer.Deferred()
|
||||
d = self.call("daemon.login", username, password,
|
||||
client_version=deluge.common.get_version())
|
||||
d.addCallback(self.__on_login, username)
|
||||
d.addErrback(self.__on_login_fail)
|
||||
return self.login_deferred
|
||||
d.addCallbacks(self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred],
|
||||
errbackArgs=[login_deferred])
|
||||
return login_deferred
|
||||
|
||||
def __on_login(self, result, username):
|
||||
def __on_login(self, result, username, login_deferred):
|
||||
log.debug("__on_login called: %s %s", username, result)
|
||||
self.username = username
|
||||
self.authentication_level = result
|
||||
|
@ -399,11 +399,10 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
self.__on_auth_levels_mappings
|
||||
)
|
||||
|
||||
self.login_deferred.callback(result)
|
||||
login_deferred.callback(result)
|
||||
|
||||
def __on_login_fail(self, result):
|
||||
log.debug("_on_login_fail(): %s", result.value)
|
||||
self.login_deferred.errback(result)
|
||||
def __on_login_fail(self, result, login_deferred):
|
||||
login_deferred.errback(result)
|
||||
|
||||
def __on_auth_levels_mappings(self, result):
|
||||
auth_levels_mapping, auth_levels_mapping_reverse = result
|
||||
|
@ -549,7 +548,12 @@ class Client(object):
|
|||
|
||||
d = self._daemon_proxy.connect(host, port)
|
||||
|
||||
def on_connected(daemon_version):
|
||||
log.debug("on_connected. Daemon version: %s", daemon_version)
|
||||
return daemon_version
|
||||
|
||||
def on_connect_fail(reason):
|
||||
log.debug("on_connect_fail: %s", reason)
|
||||
self.disconnect()
|
||||
return reason
|
||||
|
||||
|
@ -561,11 +565,6 @@ class Client(object):
|
|||
log.debug("Failed to authenticate: %s", reason.value)
|
||||
return reason
|
||||
|
||||
def on_connected(daemon_version):
|
||||
log.debug("Client.connect.on_connected. Daemon version: %s",
|
||||
daemon_version)
|
||||
return daemon_version
|
||||
|
||||
def authenticate(daemon_version, username, password):
|
||||
if not username and host in ("127.0.0.1", "localhost"):
|
||||
# No username provided and it's localhost, so attempt to get credentials from auth file.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
UI_PATH = __path__[0]
|
||||
UI_PATH = __path__[0] # NOQA Ignore 'E402 module level import not at top of file'
|
||||
|
||||
from deluge.ui.console.console import Console
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import re
|
||||
|
||||
from deluge.ui.console.modes import format_utils
|
||||
|
||||
try:
|
||||
|
@ -228,3 +230,39 @@ def parse_color_string(s, encoding="UTF-8"):
|
|||
# There was no color scheme so we add it with a 0 for white on black
|
||||
ret = [(0, s)]
|
||||
return ret
|
||||
|
||||
|
||||
class ConsoleColorFormatter(object):
|
||||
"""
|
||||
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
|
||||
"""
|
||||
|
||||
replace_dict = {
|
||||
"<torrent-id>": "{!green!}%s{!input!}",
|
||||
"<torrent>": "{!green!}%s{!input!}",
|
||||
"<command>": "{!green!}%s{!input!}",
|
||||
|
||||
"<state>": "{!yellow!}%s{!input!}",
|
||||
"\\.\\.\\.": "{!yellow!}%s{!input!}",
|
||||
"\\s\\*\\s": "{!blue!}%s{!input!}",
|
||||
"(?<![\\-a-z])(-[a-zA-Z0-9])": "{!red!}%s{!input!}",
|
||||
# "(\-[a-zA-Z0-9])": "{!red!}%s{!input!}",
|
||||
"--[_\\-a-zA-Z0-9]+": "{!green!}%s{!input!}",
|
||||
"(\\[|\\])": "{!info!}%s{!input!}",
|
||||
|
||||
"<tab>": "{!white!}%s{!input!}",
|
||||
"[_A-Z]{3,}": "{!cyan!}%s{!input!}",
|
||||
"<key>": "{!cyan!}%s{!input!}",
|
||||
"<value>": "{!cyan!}%s{!input!}",
|
||||
"usage:": "{!info!}%s{!input!}",
|
||||
"<download-folder>": "{!yellow!}%s{!input!}",
|
||||
"<torrent-file>": "{!green!}%s{!input!}"
|
||||
|
||||
}
|
||||
|
||||
def format_colors(self, string):
|
||||
def r(repl):
|
||||
return lambda s: repl % s.group()
|
||||
for key, replacement in self.replace_dict.items():
|
||||
string = re.sub(key, r(replacement), string)
|
||||
return string
|
||||
|
|
|
@ -13,43 +13,45 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.error import DelugeError
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console.colors import strip_colors
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Commander:
|
||||
class Commander(object):
|
||||
|
||||
def __init__(self, cmds, interactive=False):
|
||||
self._commands = cmds
|
||||
self.console = component.get("ConsoleUI")
|
||||
self.interactive = interactive
|
||||
|
||||
def write(self, line):
|
||||
print(strip_colors(line))
|
||||
|
||||
def do_command(self, cmd):
|
||||
def do_command(self, cmd_line):
|
||||
"""
|
||||
Processes a command.
|
||||
|
||||
:param cmd: str, the command string
|
||||
|
||||
"""
|
||||
if not cmd:
|
||||
if not cmd_line:
|
||||
return
|
||||
cmd, _, line = cmd.partition(" ")
|
||||
cmd, _, line = cmd_line.partition(" ")
|
||||
try:
|
||||
parser = self._commands[cmd].create_parser()
|
||||
except KeyError:
|
||||
self.write("{!error!}Unknown command: %s" % cmd)
|
||||
return
|
||||
args = self._commands[cmd].split(line)
|
||||
|
||||
try:
|
||||
args = [cmd] + self._commands[cmd].split(line)
|
||||
except ValueError as ex:
|
||||
self.write("{!error!}Error parsing command: %s" % ex)
|
||||
return
|
||||
|
||||
# Do a little hack here to print 'command --help' properly
|
||||
parser._print_help = parser.print_help
|
||||
|
@ -73,14 +75,20 @@ class Commander:
|
|||
return
|
||||
|
||||
try:
|
||||
options, args = parser.parse_args(args)
|
||||
options = parser.parse_args(args=args)
|
||||
except TypeError as ex:
|
||||
self.write("{!error!}Error parsing options: %s" % ex)
|
||||
import traceback
|
||||
self.write("%s" % traceback.format_exc())
|
||||
return
|
||||
except Exception as ex:
|
||||
self.write("{!error!} %s" % ex)
|
||||
parser.print_help()
|
||||
return
|
||||
|
||||
if not getattr(options, "_exit", False):
|
||||
if not getattr(parser, "_exit", False):
|
||||
try:
|
||||
ret = self._commands[cmd].handle(*args, **options.__dict__)
|
||||
ret = self._commands[cmd].handle(options)
|
||||
except Exception as ex:
|
||||
self.write("{!error!} %s" % ex)
|
||||
log.exception(ex)
|
||||
|
@ -89,50 +97,3 @@ class Commander:
|
|||
return defer.succeed(True)
|
||||
else:
|
||||
return ret
|
||||
|
||||
def exec_args(self, args, host, port, username, password):
|
||||
commands = []
|
||||
if args:
|
||||
# Multiple commands split by ";"
|
||||
commands = [arg.strip() for arg in args.split(";")]
|
||||
|
||||
def on_connect(result):
|
||||
def on_started(result):
|
||||
def on_started(result):
|
||||
def do_command(result, cmd):
|
||||
return self.do_command(cmd)
|
||||
d = defer.succeed(None)
|
||||
for command in commands:
|
||||
if command in ("quit", "exit"):
|
||||
break
|
||||
d.addCallback(do_command, command)
|
||||
d.addCallback(do_command, "quit")
|
||||
|
||||
# We need to wait for the rpcs in start() to finish before processing
|
||||
# any of the commands.
|
||||
self.console.started_deferred.addCallback(on_started)
|
||||
component.start().addCallback(on_started)
|
||||
|
||||
def on_connect_fail(reason):
|
||||
if reason.check(DelugeError):
|
||||
rm = reason.value.message
|
||||
else:
|
||||
rm = reason.getErrorMessage()
|
||||
if host:
|
||||
print("Could not connect to daemon: %s:%s\n %s" % (host, port, rm))
|
||||
else:
|
||||
print("Could not connect to localhost daemon\n %s" % rm)
|
||||
self.do_command("quit")
|
||||
|
||||
if host:
|
||||
d = client.connect(host, port, username, password)
|
||||
else:
|
||||
d = client.connect()
|
||||
if not self.interactive:
|
||||
if commands[0].startswith("connect"):
|
||||
d = self.do_command(commands.pop(0))
|
||||
elif "help" in commands:
|
||||
self.do_command("help")
|
||||
sys.exit(0)
|
||||
d.addCallback(on_connect)
|
||||
d.addErrback(on_connect_fail)
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
import base64
|
||||
import os
|
||||
from optparse import make_option
|
||||
from urllib import url2pathname
|
||||
from urlparse import urlparse
|
||||
|
||||
|
@ -23,20 +22,19 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Add a torrent"""
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('-p', '--path', dest='path', help='download folder for torrent'),
|
||||
)
|
||||
"""Add torrents"""
|
||||
|
||||
usage = "Usage: add [-p <download-folder>] <torrent-file> [<torrent-file> ...]\n"\
|
||||
" <torrent-file> arguments can be file paths, URLs or magnet uris"
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("-p", "--path", dest="path", help="download folder for torrent")
|
||||
parser.add_argument("torrents", metavar="<torrent>", nargs="+",
|
||||
help="One or more torrent files, URLs or magnet URIs")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
t_options = {}
|
||||
if options["path"]:
|
||||
t_options["download_location"] = os.path.expanduser(options["path"])
|
||||
if options.path:
|
||||
t_options["download_location"] = os.path.expanduser(options.path)
|
||||
|
||||
def on_success(result):
|
||||
if not result:
|
||||
|
@ -49,22 +47,22 @@ class Command(BaseCommand):
|
|||
|
||||
# Keep a list of deferreds to make a DeferredList
|
||||
deferreds = []
|
||||
for arg in args:
|
||||
if not arg.strip():
|
||||
for torrent in options.torrents:
|
||||
if not torrent.strip():
|
||||
continue
|
||||
if deluge.common.is_url(arg):
|
||||
self.console.write("{!info!}Attempting to add torrent from url: %s" % arg)
|
||||
deferreds.append(client.core.add_torrent_url(arg, t_options).addCallback(on_success).addErrback(
|
||||
if deluge.common.is_url(torrent):
|
||||
self.console.write("{!info!}Attempting to add torrent from url: %s" % torrent)
|
||||
deferreds.append(client.core.add_torrent_url(torrent, t_options).addCallback(on_success).addErrback(
|
||||
on_fail))
|
||||
elif deluge.common.is_magnet(arg):
|
||||
self.console.write("{!info!}Attempting to add torrent from magnet uri: %s" % arg)
|
||||
deferreds.append(client.core.add_torrent_magnet(arg, t_options).addCallback(on_success).addErrback(
|
||||
elif deluge.common.is_magnet(torrent):
|
||||
self.console.write("{!info!}Attempting to add torrent from magnet uri: %s" % torrent)
|
||||
deferreds.append(client.core.add_torrent_magnet(torrent, t_options).addCallback(on_success).addErrback(
|
||||
on_fail))
|
||||
else:
|
||||
# Just a file
|
||||
if urlparse(arg).scheme == "file":
|
||||
arg = url2pathname(urlparse(arg).path)
|
||||
path = os.path.abspath(os.path.expanduser(arg))
|
||||
if urlparse(torrent).scheme == "file":
|
||||
torrent = url2pathname(urlparse(torrent).path)
|
||||
path = os.path.abspath(os.path.expanduser(torrent))
|
||||
if not os.path.exists(path):
|
||||
self.console.write("{!error!}%s doesn't exist!" % path)
|
||||
continue
|
||||
|
|
|
@ -14,9 +14,8 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
class Command(BaseCommand):
|
||||
"""Show information about the disk cache"""
|
||||
usage = "Usage: cache"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
def on_cache_status(status):
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
import cStringIO
|
||||
import logging
|
||||
import tokenize
|
||||
from optparse import make_option
|
||||
|
||||
import deluge.component as component
|
||||
import deluge.ui.console.colors as colors
|
||||
|
@ -66,25 +65,28 @@ def simple_eval(source):
|
|||
class Command(BaseCommand):
|
||||
"""Show and set configuration values"""
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
|
||||
)
|
||||
usage = """Usage: config [key1 [key2 ...]]"
|
||||
config --set key value"""
|
||||
usage = "config [--set <key> <value>] [<key> [<key>...] ]"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
set_group = parser.add_argument_group("setting a value")
|
||||
set_group.add_argument("-s", "--set", action="store", metavar="<key>", help="set value for this key")
|
||||
set_group.add_argument("values", metavar="<value>", nargs="+", help="Value to set")
|
||||
get_group = parser.add_argument_group("getting values")
|
||||
get_group.add_argument("keys", metavar="<keys>", nargs="*", help="one or more keys separated by space")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
if options["set"]:
|
||||
return self._set_config(*args, **options)
|
||||
if options.set:
|
||||
return self._set_config(options)
|
||||
else:
|
||||
return self._get_config(*args, **options)
|
||||
return self._get_config(options)
|
||||
|
||||
def _get_config(self, *args, **options):
|
||||
def _get_config(self, options):
|
||||
def _on_get_config(config):
|
||||
keys = sorted(config.keys())
|
||||
s = ""
|
||||
for key in keys:
|
||||
if args and key not in args:
|
||||
if key not in options.values:
|
||||
continue
|
||||
color = "{!white,black,bold!}"
|
||||
value = config[key]
|
||||
|
@ -107,10 +109,16 @@ class Command(BaseCommand):
|
|||
|
||||
return client.core.get_config().addCallback(_on_get_config)
|
||||
|
||||
def _set_config(self, *args, **options):
|
||||
def _set_config(self, options):
|
||||
config = component.get("CoreConfig")
|
||||
key = options["set"][0]
|
||||
val = simple_eval(options["set"][1] + " " .join(args))
|
||||
key = options.set
|
||||
val = " ".join(options.values)
|
||||
|
||||
try:
|
||||
val = simple_eval(val)
|
||||
except SyntaxError as ex:
|
||||
self.console.write("{!error!}%s" % ex)
|
||||
return
|
||||
|
||||
if key not in config.keys():
|
||||
self.console.write("{!error!}The key '%s' is invalid!" % key)
|
||||
|
@ -126,7 +134,7 @@ class Command(BaseCommand):
|
|||
def on_set_config(result):
|
||||
self.console.write("{!success!}Configuration value successfully updated.")
|
||||
|
||||
self.console.write("Setting %s to %s.." % (key, val))
|
||||
self.console.write("Setting '%s' to '%s'" % (key, val))
|
||||
return client.core.set_config({key: val}).addCallback(on_set_config)
|
||||
|
||||
def complete(self, text):
|
||||
|
|
|
@ -8,27 +8,41 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console.main import BaseCommand
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Connect to a new deluge server."""
|
||||
|
||||
usage = "Usage: connect <host[:port]> <username> <password>"
|
||||
usage = "Usage: connect <host[:port]> [<username>] [<password>]"
|
||||
|
||||
def handle(self, host="127.0.0.1:58846", username="", password="", **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("host", help="host and port", metavar="<host[:port]>")
|
||||
parser.add_argument("username", help="Username", metavar="<username>", nargs="?", default="")
|
||||
parser.add_argument("password", help="Password", metavar="<password>", nargs="?", default="")
|
||||
|
||||
def add_parser(self, subparsers):
|
||||
parser = subparsers.add_parser(self.name, help=self.__doc__, description=self.__doc__, prog="connect")
|
||||
self.add_arguments(parser)
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
host = options.host
|
||||
try:
|
||||
host, port = host.split(":")
|
||||
port = int(port)
|
||||
except ValueError:
|
||||
port = 58846
|
||||
else:
|
||||
port = int(port)
|
||||
|
||||
def do_connect():
|
||||
d = client.connect(host, port, username, password)
|
||||
d = client.connect(host, port, options.username, options.password)
|
||||
|
||||
def on_connect(result):
|
||||
if self.console.interactive:
|
||||
|
@ -39,17 +53,18 @@ class Command(BaseCommand):
|
|||
try:
|
||||
msg = result.value.exception_msg
|
||||
except AttributeError:
|
||||
msg = result.value.args[0]
|
||||
msg = result.value.message
|
||||
self.console.write("{!error!}Failed to connect to %s:%s with reason: %s" % (host, port, msg))
|
||||
return result
|
||||
|
||||
d.addCallback(on_connect)
|
||||
d.addErrback(on_connect_fail)
|
||||
d.addCallbacks(on_connect, on_connect_fail)
|
||||
return d
|
||||
|
||||
if client.connected():
|
||||
|
||||
def on_disconnect(result):
|
||||
self.console.statusbars.update_statusbars()
|
||||
if self.console.statusbars:
|
||||
self.console.statusbars.update_statusbars()
|
||||
return do_connect()
|
||||
return client.disconnect().addCallback(on_disconnect)
|
||||
else:
|
||||
|
|
|
@ -17,12 +17,14 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
class Command(BaseCommand):
|
||||
"""Enable and disable debugging"""
|
||||
usage = "Usage: debug [on|off]"
|
||||
|
||||
def handle(self, state="", **options):
|
||||
if state == "on":
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("state", metavar="<on|off>", choices=["on", "off"], help="The new state")
|
||||
|
||||
def handle(self, options):
|
||||
if options.state == "on":
|
||||
deluge.log.set_logger_level("debug")
|
||||
elif state == "off":
|
||||
elif options.state == "off":
|
||||
deluge.log.set_logger_level("error")
|
||||
else:
|
||||
component.get("ConsoleUI").write("{!error!}%s" % self.usage)
|
||||
|
|
|
@ -7,22 +7,26 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.console.main import BaseCommand
|
||||
from deluge.ui.console.modes.alltorrents import AllTorrents
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Exit this mode and go into the more 'gui' like mode"""
|
||||
usage = "Usage: gui"
|
||||
"""Enable interactive mode"""
|
||||
interactive_only = True
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
console = component.get("ConsoleUI")
|
||||
try:
|
||||
at = component.get("AllTorrents")
|
||||
except KeyError:
|
||||
at = AllTorrents(console.stdscr, console.encoding)
|
||||
|
||||
console.set_mode(at)
|
||||
at._go_top = True
|
||||
at.resume()
|
||||
|
|
|
@ -15,9 +15,8 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
class Command(BaseCommand):
|
||||
"Shutdown the deluge server"
|
||||
usage = "Usage: halt"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
def on_shutdown(result):
|
||||
|
|
|
@ -8,27 +8,32 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.console.main import BaseCommand
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""displays help on other commands"""
|
||||
|
||||
usage = "Usage: help [command]"
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("commands", metavar="<command>", nargs="*", help="One or more commands")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
self._commands = self.console._commands
|
||||
deferred = defer.succeed(True)
|
||||
if args:
|
||||
for arg in args:
|
||||
if options.commands:
|
||||
for arg in options.commands:
|
||||
try:
|
||||
cmd = self._commands[arg]
|
||||
except KeyError:
|
||||
self.console.write("{!error!}Unknown command %r" % args[0])
|
||||
self.console.write("{!error!}Unknown command %s" % arg)
|
||||
continue
|
||||
try:
|
||||
parser = cmd.create_parser()
|
||||
|
@ -38,8 +43,15 @@ class Command(BaseCommand):
|
|||
self.console.write(" ")
|
||||
else:
|
||||
self.console.set_batch_write(True)
|
||||
cmds_doc = ""
|
||||
for cmd in sorted(self._commands):
|
||||
self.console.write("{!info!}" + cmd + "{!input!} - " + self._commands[cmd].__doc__ or '')
|
||||
if cmd in self._commands[cmd].aliases:
|
||||
continue
|
||||
parser = self._commands[cmd].create_parser()
|
||||
cmd_doc = "{!info!}" + "%-9s" % self._commands[cmd].name_with_alias + "{!input!} - "\
|
||||
+ self._commands[cmd].__doc__ + "\n " + parser.format_usage() or ''
|
||||
cmds_doc += parser.formatter.format_colors(cmd_doc)
|
||||
self.console.write(cmds_doc)
|
||||
self.console.write(" ")
|
||||
self.console.write("For help on a specific command, use '<command> --help'")
|
||||
self.console.set_batch_write(False)
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
from optparse import make_option
|
||||
from os.path import sep as dirsep
|
||||
|
||||
import deluge.common as common
|
||||
|
@ -90,44 +89,51 @@ class Command(BaseCommand):
|
|||
|
||||
sort_help = "sort items. Possible keys: " + ", ".join(STATUS_KEYS)
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("-v", "--verbose", action="store_true", default=False, dest="verbose",
|
||||
help="Show more information per torrent."),
|
||||
make_option("-d", "--detailed", action="store_true", default=False, dest="detailed",
|
||||
help="Show more detailed information including files and peers."),
|
||||
make_option("-s", "--state", action="store", dest="state",
|
||||
help="Show torrents with state STATE: %s." % (", ".join(STATES))),
|
||||
make_option("--sort", action="store", type="string", default="", dest="sort", help=sort_help),
|
||||
make_option("--sort-reverse", action="store", type="string", default="", dest="sort_rev",
|
||||
help="Same as --sort but items are in reverse order.")
|
||||
)
|
||||
epilog = """
|
||||
You can give the first few characters of a torrent-id to identify the torrent.
|
||||
|
||||
usage = """Usage: info [-v | -d | -s <state>] [<torrent-id> [<torrent-id> ...]]
|
||||
You can give the first few characters of a torrent-id to identify the torrent.
|
||||
info * will list all torrents.
|
||||
Tab Completion (info *pattern*<tab>):\n
|
||||
| First press of <tab> will output up to 15 matches;
|
||||
| hitting <tab> a second time, will print 15 more matches;
|
||||
| and a third press will print all remaining matches.
|
||||
| (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)
|
||||
"""
|
||||
|
||||
Tab Completion (info *pattern*<tab>):
|
||||
| First press of <tab> will output up to 15 matches;
|
||||
| hitting <tab> a second time, will print 15 more matches;
|
||||
| and a third press will print all remaining matches.
|
||||
| (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)"""
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("-v", "--verbose", action="store_true", default=False, dest="verbose",
|
||||
help="Show more information per torrent.")
|
||||
parser.add_argument("-d", "--detailed", action="store_true", default=False, dest="detailed",
|
||||
help="Show more detailed information including files and peers.")
|
||||
parser.add_argument("-s", "--state", action="store", dest="state",
|
||||
help="Show torrents with state STATE: %s." % (", ".join(STATES)))
|
||||
parser.add_argument("--sort", action="store", type=str, default="", dest="sort", help=self.sort_help)
|
||||
parser.add_argument("--sort-reverse", action="store", type=str, default="", dest="sort_rev",
|
||||
help="Same as --sort but items are in reverse order.")
|
||||
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="*",
|
||||
help="One or more torrent ids. If none is given, list all")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_subparser(self, subparsers):
|
||||
parser = subparsers.add_parser(self.name, prog=self.name, help=self.__doc__,
|
||||
description=self.__doc__, epilog=self.epilog)
|
||||
self.add_arguments(parser)
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
# Compile a list of torrent_ids to request the status of
|
||||
torrent_ids = []
|
||||
for arg in args:
|
||||
torrent_ids.extend(self.console.match_torrent(arg))
|
||||
|
||||
if not args:
|
||||
if options.torrent_ids:
|
||||
for t_id in options.torrent_ids:
|
||||
torrent_ids.extend(self.console.match_torrent(t_id))
|
||||
else:
|
||||
torrent_ids.extend(self.console.match_torrent(""))
|
||||
|
||||
def on_torrents_status(status):
|
||||
# Print out the information for each torrent
|
||||
sort_key = options["sort"]
|
||||
sort_key = options.sort
|
||||
sort_reverse = False
|
||||
if not sort_key:
|
||||
sort_key = options["sort_rev"]
|
||||
sort_key = options.sort_rev
|
||||
sort_reverse = True
|
||||
if not sort_key:
|
||||
sort_key = "name"
|
||||
|
@ -138,19 +144,19 @@ class Command(BaseCommand):
|
|||
sort_key = "name"
|
||||
sort_reverse = False
|
||||
for key, value in sorted(status.items(), key=lambda x: x[1].get(sort_key), reverse=sort_reverse):
|
||||
self.show_info(key, status[key], options["verbose"], options["detailed"])
|
||||
self.show_info(key, status[key], options.verbose, options.detailed)
|
||||
|
||||
def on_torrents_status_fail(reason):
|
||||
self.console.write("{!error!}Error getting torrent info: %s" % reason)
|
||||
|
||||
status_dict = {"id": torrent_ids}
|
||||
|
||||
if options["state"]:
|
||||
options["state"] = options["state"].capitalize()
|
||||
if options["state"] in STATES:
|
||||
status_dict["state"] = options["state"]
|
||||
if options.state:
|
||||
options.state = options.state.capitalize()
|
||||
if options.state in STATES:
|
||||
status_dict.state = options.state
|
||||
else:
|
||||
self.console.write("Invalid state: %s" % options["state"])
|
||||
self.console.write("Invalid state: %s" % options.state)
|
||||
self.console.write("Possible values are: %s." % (", ".join(STATES)))
|
||||
return
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
from optparse import make_option
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
import deluge.component as component
|
||||
|
@ -35,20 +33,25 @@ torrent_options = {
|
|||
class Command(BaseCommand):
|
||||
"""Show and manage per-torrent options"""
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
|
||||
)
|
||||
usage = "Usage: manage <torrent-id> [<key1> [<key2> ...]]\n"\
|
||||
" manage <torrent-id> --set <key> <value>"
|
||||
usage = "manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("torrent", metavar="<torrent>",
|
||||
help="an expression matched against torrent ids and torrent names")
|
||||
set_group = parser.add_argument_group("setting a value")
|
||||
set_group.add_argument("-s", "--set", action="store", metavar="<key>", help="set value for this key")
|
||||
set_group.add_argument("values", metavar="<value>", nargs="+", help="Value to set")
|
||||
get_group = parser.add_argument_group("getting values")
|
||||
get_group.add_argument("keys", metavar="<keys>", nargs="*", help="one or more keys separated by space")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
if options['set']:
|
||||
return self._set_option(*args, **options)
|
||||
if options.set:
|
||||
return self._set_option(options)
|
||||
else:
|
||||
return self._get_option(*args, **options)
|
||||
return self._get_option(options)
|
||||
|
||||
def _get_option(self, *args, **options):
|
||||
def _get_option(self, options):
|
||||
|
||||
def on_torrents_status(status):
|
||||
for torrentid, data in status.items():
|
||||
|
@ -63,11 +66,10 @@ class Command(BaseCommand):
|
|||
def on_torrents_status_fail(reason):
|
||||
self.console.write('{!error!}Failed to get torrent data.')
|
||||
|
||||
torrent_ids = []
|
||||
torrent_ids.extend(self.console.match_torrent(args[0]))
|
||||
torrent_ids = self.console.match_torrent(options.torrent)
|
||||
|
||||
request_options = []
|
||||
for opt in args[1:]:
|
||||
for opt in options.values:
|
||||
if opt not in torrent_options:
|
||||
self.console.write('{!error!}Unknown torrent option: %s' % opt)
|
||||
return
|
||||
|
@ -77,16 +79,14 @@ class Command(BaseCommand):
|
|||
request_options.append('name')
|
||||
|
||||
d = client.core.get_torrents_status({"id": torrent_ids}, request_options)
|
||||
d.addCallback(on_torrents_status)
|
||||
d.addErrback(on_torrents_status_fail)
|
||||
d.addCallbacks(on_torrents_status, on_torrents_status_fail)
|
||||
return d
|
||||
|
||||
def _set_option(self, *args, **options):
|
||||
def _set_option(self, options):
|
||||
deferred = defer.Deferred()
|
||||
torrent_ids = []
|
||||
torrent_ids.extend(self.console.match_torrent(args[0]))
|
||||
key = options["set"][0]
|
||||
val = options["set"][1] + " " .join(args[1:])
|
||||
key = options.set
|
||||
val = " " .join(options.values)
|
||||
torrent_ids = self.console.match_torrent(options.torrent)
|
||||
|
||||
if key not in torrent_options:
|
||||
self.console.write("{!error!}The key '%s' is invalid!" % key)
|
||||
|
|
|
@ -7,43 +7,43 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console.main import BaseCommand
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Move torrents' storage location"""
|
||||
usage = "Usage: move <torrent-id> [<torrent-id> ...] <path>"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+", help="One or more torrent ids")
|
||||
parser.add_argument("path", metavar="<path>", help="The path to move the torrents to")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
if len(args) < 2:
|
||||
self.console.write(self.usage)
|
||||
return
|
||||
|
||||
path = args[-1]
|
||||
|
||||
if os.path.exists(path) and not os.path.isdir(path):
|
||||
self.console.write("{!error!}Cannot Move Download Folder: %s exists and is not a directory" % path)
|
||||
if os.path.exists(options.path) and not os.path.isdir(options.path):
|
||||
self.console.write("{!error!}Cannot Move Download Folder: %s exists and is not a directory" % options.path)
|
||||
return
|
||||
|
||||
ids = []
|
||||
for i in args[:-1]:
|
||||
ids.extend(self.console.match_torrent(i))
|
||||
|
||||
names = []
|
||||
for i in ids:
|
||||
names.append(self.console.get_torrent_name(i))
|
||||
namestr = ", ".join(names)
|
||||
for t_id in options.torrent_ids:
|
||||
tid = self.console.match_torrent(t_id)
|
||||
ids.extend(tid)
|
||||
names.append(self.console.get_torrent_name(tid))
|
||||
|
||||
def on_move(res):
|
||||
self.console.write("Moved \"%s\" to %s" % (namestr, path))
|
||||
msg = "Moved \"%s\" to %s" % (", ".join(names), options.path)
|
||||
self.console.write(msg)
|
||||
log.info(msg)
|
||||
|
||||
d = client.core.move_storage(ids, path)
|
||||
d = client.core.move_storage(ids, options.path)
|
||||
d.addCallback(on_move)
|
||||
return d
|
||||
|
||||
|
@ -81,5 +81,4 @@ class Command(BaseCommand):
|
|||
if os.path.isdir(p):
|
||||
p += "/"
|
||||
ret.append(p)
|
||||
|
||||
return ret
|
||||
|
|
|
@ -14,21 +14,22 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Pause a torrent"""
|
||||
usage = "Usage: pause [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
"""Pause torrents"""
|
||||
usage = "pause [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+",
|
||||
help="One or more torrent ids. '*' pauses all torrents")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
if len(args) == 0:
|
||||
self.console.write(self.usage)
|
||||
return
|
||||
if len(args) > 0 and args[0].lower() == '*':
|
||||
if options.torrent_ids[0] == "*":
|
||||
client.core.pause_session()
|
||||
return
|
||||
|
||||
torrent_ids = []
|
||||
for arg in args:
|
||||
for arg in options.torrent_ids:
|
||||
torrent_ids.extend(self.console.match_torrent(arg))
|
||||
|
||||
if torrent_ids:
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
from optparse import make_option
|
||||
|
||||
import deluge.component as component
|
||||
import deluge.configmanager
|
||||
from deluge.ui.client import client
|
||||
|
@ -16,41 +14,28 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Manage plugins with this command"""
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("-l", "--list", action="store_true", default=False, dest="list",
|
||||
help="Lists available plugins"),
|
||||
make_option("-s", "--show", action="store_true", default=False, dest="show",
|
||||
help="Shows enabled plugins"),
|
||||
make_option("-e", "--enable", dest="enable",
|
||||
help="Enables a plugin"),
|
||||
make_option("-d", "--disable", dest="disable",
|
||||
help="Disables a plugin"),
|
||||
make_option("-r", "--reload", action="store_true", default=False, dest="reload",
|
||||
help="Reload list of available plugins"),
|
||||
make_option("-i", "--install", dest="plugin_file",
|
||||
help="Install a plugin from an .egg file"),
|
||||
)
|
||||
usage = """Usage: plugin [ -l | --list ]
|
||||
plugin [ -s | --show ]
|
||||
plugin [ -e | --enable ] <plugin-name>
|
||||
plugin [ -d | --disable ] <plugin-name>
|
||||
plugin [ -i | --install ] <plugin-file>
|
||||
plugin [ -r | --reload]"""
|
||||
"""Manage plugins"""
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("-l", "--list", action="store_true", default=False, dest="list",
|
||||
help="Lists available plugins")
|
||||
parser.add_argument("-s", "--show", action="store_true", default=False, dest="show",
|
||||
help="Shows enabled plugins")
|
||||
parser.add_argument("-e", "--enable", dest="enable", nargs="+", help="Enables a plugin")
|
||||
parser.add_argument("-d", "--disable", dest="disable", nargs="+", help="Disables a plugin")
|
||||
parser.add_argument("-r", "--reload", action="store_true", default=False, dest="reload",
|
||||
help="Reload list of available plugins")
|
||||
parser.add_argument("-i", "--install", help="Install a plugin from an .egg file")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
if len(args) == 0 and not any(options.values()):
|
||||
self.console.write(self.usage)
|
||||
return
|
||||
|
||||
if options["reload"]:
|
||||
if options.reload:
|
||||
client.core.pluginmanager.rescan_plugins()
|
||||
self.console.write("{!green!}Plugin list successfully reloaded")
|
||||
return
|
||||
|
||||
elif options["list"]:
|
||||
elif options.list:
|
||||
def on_available_plugins(result):
|
||||
self.console.write("{!info!}Available Plugins:")
|
||||
for p in result:
|
||||
|
@ -58,7 +43,7 @@ class Command(BaseCommand):
|
|||
|
||||
return client.core.get_available_plugins().addCallback(on_available_plugins)
|
||||
|
||||
elif options["show"]:
|
||||
elif options.show:
|
||||
def on_enabled_plugins(result):
|
||||
self.console.write("{!info!}Enabled Plugins:")
|
||||
for p in result:
|
||||
|
@ -66,50 +51,42 @@ class Command(BaseCommand):
|
|||
|
||||
return client.core.get_enabled_plugins().addCallback(on_enabled_plugins)
|
||||
|
||||
elif options["enable"]:
|
||||
elif options.enable:
|
||||
def on_available_plugins(result):
|
||||
plugins = {}
|
||||
for p in result:
|
||||
plugins[p.lower()] = p
|
||||
p_args = [options["enable"]] + list(args)
|
||||
|
||||
for arg in p_args:
|
||||
for arg in options.enable:
|
||||
if arg.lower() in plugins:
|
||||
client.core.enable_plugin(plugins[arg.lower()])
|
||||
|
||||
return client.core.get_available_plugins().addCallback(on_available_plugins)
|
||||
|
||||
elif options["disable"]:
|
||||
elif options.disable:
|
||||
def on_enabled_plugins(result):
|
||||
plugins = {}
|
||||
for p in result:
|
||||
plugins[p.lower()] = p
|
||||
p_args = [options["disable"]] + list(args)
|
||||
|
||||
for arg in p_args:
|
||||
for arg in options.disable:
|
||||
if arg.lower() in plugins:
|
||||
client.core.disable_plugin(plugins[arg.lower()])
|
||||
|
||||
return client.core.get_enabled_plugins().addCallback(on_enabled_plugins)
|
||||
|
||||
elif options["plugin_file"]:
|
||||
|
||||
filepath = options["plugin_file"]
|
||||
|
||||
elif options.install:
|
||||
import os.path
|
||||
import base64
|
||||
import shutil
|
||||
|
||||
filepath = options.install
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
self.console.write("{!error!}Invalid path: %s" % filepath)
|
||||
return
|
||||
|
||||
config_dir = deluge.configmanager.get_config_dir()
|
||||
filename = os.path.split(filepath)[1]
|
||||
|
||||
shutil.copyfile(
|
||||
filepath,
|
||||
os.path.join(config_dir, "plugins", filename))
|
||||
shutil.copyfile(filepath, os.path.join(config_dir, "plugins", filename))
|
||||
|
||||
client.core.rescan_plugins()
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Exit from the client."""
|
||||
"""Exit the client."""
|
||||
aliases = ["exit"]
|
||||
interactive_only = True
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
if client.connected():
|
||||
def on_disconnect(result):
|
||||
reactor.stop()
|
||||
|
|
|
@ -14,20 +14,20 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
class Command(BaseCommand):
|
||||
"""Forces a recheck of the torrent data"""
|
||||
usage = "Usage: recheck [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
usage = "recheck [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+", help="One or more torrent ids")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
if len(args) == 0:
|
||||
self.console.write(self.usage)
|
||||
return
|
||||
if len(args) > 0 and args[0].lower() == "*":
|
||||
if options.torrent_ids[0] == "*":
|
||||
client.core.force_recheck(self.console.match_torrent(""))
|
||||
return
|
||||
|
||||
torrent_ids = []
|
||||
for arg in args:
|
||||
for arg in options.torrent_ids:
|
||||
torrent_ids.extend(self.console.match_torrent(arg))
|
||||
|
||||
if torrent_ids:
|
||||
|
|
|
@ -14,22 +14,23 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Resume a torrent"""
|
||||
usage = "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
"""Resume torrents"""
|
||||
usage = "resume [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+",
|
||||
help="One or more torrent ids. '*' resumes all torrents")
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
if len(args) == 0:
|
||||
self.console.write(self.usage)
|
||||
return
|
||||
if len(args) > 0 and args[0] == "*":
|
||||
if options.torrent_ids[0] == "*":
|
||||
client.core.resume_session()
|
||||
return
|
||||
|
||||
torrent_ids = []
|
||||
for arg in args:
|
||||
torrent_ids.extend(self.console.match_torrent(arg))
|
||||
for t_id in options.torrent_ids:
|
||||
torrent_ids.extend(self.console.match_torrent(t_id))
|
||||
|
||||
if torrent_ids:
|
||||
return client.core.resume_torrent(torrent_ids)
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
from optparse import make_option
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console.main import BaseCommand
|
||||
|
@ -17,21 +15,16 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
class Command(BaseCommand):
|
||||
"""Remove a torrent"""
|
||||
usage = "Usage: rm <torrent-id>"
|
||||
aliases = ["del"]
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("--remove_data", action="store_true", default=False,
|
||||
help="remove the torrent's data"),
|
||||
)
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("--remove_data", action="store_true", default=False, help="remove the torrent's data")
|
||||
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+", help="One or more torrent ids")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
if len(args) == 0:
|
||||
self.console.write(self.usage)
|
||||
|
||||
torrent_ids = []
|
||||
for arg in args:
|
||||
for arg in options.torrent_ids:
|
||||
torrent_ids.extend(self.console.match_torrent(arg))
|
||||
|
||||
def on_removed_finished(errors):
|
||||
|
@ -40,7 +33,7 @@ class Command(BaseCommand):
|
|||
for t_id, e_msg in errors:
|
||||
self.console.write("Error removing torrent %s : %s" % (t_id, e_msg))
|
||||
|
||||
d = client.core.remove_torrents(torrent_ids, options["remove_data"])
|
||||
d = client.core.remove_torrents(torrent_ids, options.remove_data)
|
||||
d.addCallback(on_removed_finished)
|
||||
|
||||
def complete(self, line):
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
from optparse import make_option
|
||||
import logging
|
||||
|
||||
from twisted.internet import defer
|
||||
|
||||
|
@ -16,44 +16,34 @@ from deluge.common import TORRENT_STATE, fspeed
|
|||
from deluge.ui.client import client
|
||||
from deluge.ui.console.main import BaseCommand
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""Shows a various status information from the daemon."""
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option("-r", "--raw", action="store_true", default=False, dest="raw",
|
||||
help="Don't format upload/download rates in KiB/s \
|
||||
(useful for scripts that want to do their own parsing)"),
|
||||
make_option("-n", "--no-torrents", action="store_false", default=True, dest="show_torrents",
|
||||
help="Don't show torrent status (this will make the command a bit faster)"),
|
||||
)
|
||||
|
||||
usage = "Usage: status [-r] [-n]"
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("-r", "--raw", action="store_true", default=False, dest="raw",
|
||||
help="Don't format upload/download rates in KiB/s \
|
||||
(useful for scripts that want to do their own parsing)")
|
||||
parser.add_argument("-n", "--no-torrents", action="store_false", default=True, dest="show_torrents",
|
||||
help="Don't show torrent status (this will make the command a bit faster)")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
self.status = None
|
||||
self.connections = None
|
||||
if options["show_torrents"]:
|
||||
self.torrents = None
|
||||
else:
|
||||
self.torrents = -2
|
||||
|
||||
self.raw = options["raw"]
|
||||
self.torrents = 1 if options.show_torrents else 0
|
||||
self.raw = options.raw
|
||||
|
||||
def on_session_status(status):
|
||||
self.status = status
|
||||
if self.status is not None and self.connections is not None and self.torrents is not None:
|
||||
self.print_status()
|
||||
|
||||
def on_torrents_status(status):
|
||||
self.torrents = status
|
||||
if self.status is not None and self.connections is not None and self.torrents is not None:
|
||||
self.print_status()
|
||||
|
||||
def on_torrents_status_fail(reason):
|
||||
self.torrents = -1
|
||||
if self.status is not None and self.connections is not None and self.torrents is not None:
|
||||
self.print_status()
|
||||
log.warn("Failed to retrieve session status: %s", reason)
|
||||
self.torrents = -2
|
||||
|
||||
deferreds = []
|
||||
|
||||
|
@ -61,15 +51,15 @@ class Command(BaseCommand):
|
|||
ds.addCallback(on_session_status)
|
||||
deferreds.append(ds)
|
||||
|
||||
if options["show_torrents"]:
|
||||
if options.show_torrents:
|
||||
dt = client.core.get_torrents_status({}, ["state"])
|
||||
dt.addCallback(on_torrents_status)
|
||||
dt.addErrback(on_torrents_status_fail)
|
||||
deferreds.append(dt)
|
||||
|
||||
return defer.DeferredList(deferreds)
|
||||
return defer.DeferredList(deferreds).addCallback(self.print_status)
|
||||
|
||||
def print_status(self):
|
||||
def print_status(self, *args):
|
||||
self.console.set_batch_write(True)
|
||||
if self.raw:
|
||||
self.console.write("{!info!}Total upload: %f" % self.status["payload_upload_rate"])
|
||||
|
@ -78,12 +68,12 @@ class Command(BaseCommand):
|
|||
self.console.write("{!info!}Total upload: %s" % fspeed(self.status["payload_upload_rate"]))
|
||||
self.console.write("{!info!}Total download: %s" % fspeed(self.status["payload_download_rate"]))
|
||||
self.console.write("{!info!}DHT Nodes: %i" % self.status["dht_nodes"])
|
||||
self.console.write("{!info!}Total connections: %i" % self.connections)
|
||||
if self.torrents == -1:
|
||||
self.console.write("{!error!}Error getting torrent info")
|
||||
elif self.torrents != -2:
|
||||
self.console.write("{!info!}Total torrents: %i" % len(self.torrents))
|
||||
|
||||
if isinstance(self.torrents, int):
|
||||
if self.torrents == -2:
|
||||
self.console.write("{!error!}Error getting torrent info")
|
||||
else:
|
||||
self.console.write("{!info!}Total torrents: %i" % len(self.torrents))
|
||||
state_counts = {}
|
||||
for state in TORRENT_STATE:
|
||||
state_counts[state] = 0
|
||||
|
|
|
@ -15,15 +15,17 @@ from deluge.ui.console.main import BaseCommand
|
|||
|
||||
class Command(BaseCommand):
|
||||
"""Update tracker for torrent(s)"""
|
||||
usage = "Usage: update_tracker [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
usage = "update_tracker [ * | <torrent-id> [<torrent-id> ...] ]"
|
||||
aliases = ['reannounce']
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('torrent_ids', metavar="<torrent-id>", nargs='+',
|
||||
help='One or more torrent ids. "*" updates all torrents')
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get("ConsoleUI")
|
||||
if len(args) == 0:
|
||||
self.console.write(self.usage)
|
||||
return
|
||||
if len(args) > 0 and args[0].lower() == '*':
|
||||
args = options.torrent_ids
|
||||
if options.torrent_ids[0] == "*":
|
||||
args = [""]
|
||||
|
||||
torrent_ids = []
|
||||
|
|
|
@ -8,29 +8,36 @@
|
|||
# See LICENSE for more details.
|
||||
#
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from deluge.ui.baseargparser import DelugeTextHelpFormatter
|
||||
from deluge.ui.console import UI_PATH
|
||||
from deluge.ui.ui import UI
|
||||
|
||||
|
||||
def load_commands(command_dir, exclude=[]):
|
||||
#
|
||||
# Note: Cannot import from console.main here because it imports the twisted reactor.
|
||||
# Console is imported from console/__init__.py loaded by the script entry points
|
||||
# defined in setup.py
|
||||
#
|
||||
|
||||
def load_commands(command_dir):
|
||||
|
||||
def get_command(name):
|
||||
return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
|
||||
|
||||
try:
|
||||
commands = []
|
||||
for filename in os.listdir(command_dir):
|
||||
if filename.split('.')[0] in exclude or filename.startswith('_'):
|
||||
if filename.startswith('_'):
|
||||
continue
|
||||
if not (filename.endswith('.py') or filename.endswith('.pyc')):
|
||||
continue
|
||||
cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
|
||||
aliases = [filename.split('.')[len(filename.split('.')) - 2]]
|
||||
aliases.extend(cmd.aliases)
|
||||
for a in aliases:
|
||||
cmd._name = filename.split('.')[len(filename.split('.')) - 2]
|
||||
names = [cmd._name]
|
||||
names.extend(cmd.aliases)
|
||||
for a in names:
|
||||
commands.append((a, cmd))
|
||||
return dict(commands)
|
||||
except OSError:
|
||||
|
@ -44,61 +51,29 @@ class Console(UI):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Console, self).__init__("console", *args, **kwargs)
|
||||
group = optparse.OptionGroup(self.parser, "Console Options", "These options control how "
|
||||
"the console connects to the daemon. These options will be "
|
||||
"used if you pass a command, or if you have autoconnect "
|
||||
"enabled for the console ui.")
|
||||
|
||||
group.add_option("-d", "--daemon", dest="daemon_addr",
|
||||
action="store", type="str", default="127.0.0.1",
|
||||
help="Set the address of the daemon to connect to. [default: %default]")
|
||||
group.add_option("-p", "--port", dest="daemon_port",
|
||||
help="Set the port to connect to the daemon on. [default: %default]",
|
||||
action="store", type="int", default=58846)
|
||||
group.add_option("-u", "--username", dest="daemon_user",
|
||||
help="Set the username to connect to the daemon with. [default: %default]",
|
||||
action="store", type="string")
|
||||
group.add_option("-P", "--password", dest="daemon_pass",
|
||||
help="Set the password to connect to the daemon with. [default: %default]",
|
||||
action="store", type="string")
|
||||
self.parser.add_option_group(group)
|
||||
group = self.parser.add_argument_group(_("Console Options"), "These daemon connect options will be "
|
||||
"used for commands, or if console ui autoconnect is enabled.")
|
||||
group.add_argument("-d", "--daemon", dest="daemon_addr", required=False, default="127.0.0.1")
|
||||
group.add_argument("-p", "--port", dest="daemon_port", type=int, required=False, default="58846")
|
||||
group.add_argument("-U", "--username", dest="daemon_user", required=False)
|
||||
group.add_argument("-P", "--password", dest="daemon_pass", required=False)
|
||||
|
||||
self.cmds = load_commands(os.path.join(UI_PATH, 'commands'))
|
||||
|
||||
class CommandOptionGroup(optparse.OptionGroup):
|
||||
def __init__(self, parser, title, description=None, cmds=None):
|
||||
optparse.OptionGroup.__init__(self, parser, title, description)
|
||||
self.cmds = cmds
|
||||
|
||||
def format_help(self, formatter):
|
||||
result = formatter.format_heading(self.title)
|
||||
formatter.indent()
|
||||
if self.description:
|
||||
result += "%s\n" % formatter.format_description(self.description)
|
||||
for cname in self.cmds:
|
||||
cmd = self.cmds[cname]
|
||||
if cmd.interactive_only or cname in cmd.aliases:
|
||||
continue
|
||||
allnames = [cname]
|
||||
allnames.extend(cmd.aliases)
|
||||
cname = "/".join(allnames)
|
||||
result += formatter.format_heading(" - ".join([cname, cmd.__doc__]))
|
||||
formatter.indent()
|
||||
result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage)
|
||||
formatter.dedent()
|
||||
formatter.dedent()
|
||||
return result
|
||||
cmd_group = CommandOptionGroup(self.parser, "Console Commands",
|
||||
description="The following commands can be issued at the "
|
||||
"command line. Commands should be quoted, so, for example, "
|
||||
"to pause torrent with id 'abc' you would run: '%s "
|
||||
"\"pause abc\"'" % os.path.basename(sys.argv[0]),
|
||||
cmds=self.cmds)
|
||||
self.parser.add_option_group(cmd_group)
|
||||
# To properly print help message for the console commands ( e.g. deluge-console info -h),
|
||||
# we add a subparser for each command which will trigger the help/usage when given
|
||||
from deluge.ui.console.main import ConsoleCommandParser # import here because (see top)
|
||||
self.console_parser = ConsoleCommandParser(parents=[self.parser], add_help=False,
|
||||
formatter_class=lambda prog:
|
||||
DelugeTextHelpFormatter(prog, max_help_position=33, width=90))
|
||||
self.parser.subparser = self.console_parser
|
||||
subparsers = self.console_parser.add_subparsers(title="Console commands", help="Description", dest="commands",
|
||||
description="The following console commands are available:",
|
||||
metavar="command")
|
||||
self.console_cmds = load_commands(os.path.join(UI_PATH, "commands"))
|
||||
for c in sorted(self.console_cmds):
|
||||
self.console_cmds[c].add_subparser(subparsers)
|
||||
|
||||
def start(self, args=None):
|
||||
from main import ConsoleUI
|
||||
super(Console, self).start(args)
|
||||
ConsoleUI(self.args, self.cmds, (self.options.daemon_addr,
|
||||
self.options.daemon_port, self.options.daemon_user,
|
||||
self.options.daemon_pass))
|
||||
from deluge.ui.console.main import ConsoleUI # import here because (see top)
|
||||
ConsoleUI(self.options, self.console_cmds)
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import locale
|
||||
import logging
|
||||
import optparse
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
|
@ -21,8 +20,10 @@ from twisted.internet import defer, reactor
|
|||
|
||||
import deluge.common
|
||||
import deluge.component as component
|
||||
from deluge.error import DelugeError
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console import colors
|
||||
from deluge.ui.console.colors import ConsoleColorFormatter
|
||||
from deluge.ui.console.eventlog import EventLog
|
||||
from deluge.ui.console.statusbars import StatusBars
|
||||
from deluge.ui.coreconfig import CoreConfig
|
||||
|
@ -31,84 +32,32 @@ from deluge.ui.sessionproxy import SessionProxy
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DelugeHelpFormatter(optparse.IndentedHelpFormatter):
|
||||
"""
|
||||
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
|
||||
"""
|
||||
class ConsoleCommandParser(argparse.ArgumentParser):
|
||||
|
||||
replace_dict = {
|
||||
"<torrent-id>": "{!green!}%s{!input!}",
|
||||
"<state>": "{!yellow!}%s{!input!}",
|
||||
"\\.\\.\\.": "{!yellow!}%s{!input!}",
|
||||
"\\s\\*\\s": "{!blue!}%s{!input!}",
|
||||
"(?<![\\-a-z])(-[a-zA-Z0-9])": "{!red!}%s{!input!}",
|
||||
# "(\-[a-zA-Z0-9])": "{!red!}%s{!input!}",
|
||||
"--[_\\-a-zA-Z0-9]+": "{!green!}%s{!input!}",
|
||||
"(\\[|\\])": "{!info!}%s{!input!}",
|
||||
def format_help(self):
|
||||
"""
|
||||
Differs from ArgumentParser.format_help by adding the raw epilog
|
||||
as formatted in the string. Default bahavior mangles the formatting.
|
||||
|
||||
"<tab>": "{!white!}%s{!input!}",
|
||||
"[_A-Z]{3,}": "{!cyan!}%s{!input!}",
|
||||
|
||||
"<download-folder>": "{!yellow!}%s{!input!}",
|
||||
"<torrent-file>": "{!green!}%s{!input!}"
|
||||
|
||||
}
|
||||
|
||||
def __init__(self,
|
||||
indent_increment=2,
|
||||
max_help_position=24,
|
||||
width=None,
|
||||
short_first=1):
|
||||
optparse.IndentedHelpFormatter.__init__(
|
||||
self, indent_increment, max_help_position, width, short_first)
|
||||
|
||||
def _format_colors(self, string):
|
||||
def r(repl):
|
||||
return lambda s: repl % s.group()
|
||||
|
||||
for key, replacement in self.replace_dict.items():
|
||||
string = re.sub(key, r(replacement), string)
|
||||
|
||||
return string
|
||||
|
||||
def format_usage(self, usage):
|
||||
|
||||
return _("{!info!}Usage{!input!}: %s\n") % self._format_colors(usage)
|
||||
|
||||
def format_option(self, option):
|
||||
result = []
|
||||
opts = self.option_strings[option]
|
||||
opt_width = self.help_position - self.current_indent - 2
|
||||
if len(opts) > opt_width:
|
||||
opts = "%*s%s\n" % (self.current_indent, "", opts)
|
||||
opts = self._format_colors(opts)
|
||||
indent_first = self.help_position
|
||||
else: # start help on same line as opts
|
||||
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
|
||||
opts = self._format_colors(opts)
|
||||
indent_first = 0
|
||||
result.append(opts)
|
||||
if option.help:
|
||||
help_text = self.expand_default(option)
|
||||
help_text = self._format_colors(help_text)
|
||||
help_lines = optparse.textwrap.wrap(help_text, self.help_width)
|
||||
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
|
||||
result.extend(["%*s%s\n" % (self.help_position, "", line)
|
||||
for line in help_lines[1:]])
|
||||
elif opts[-1] != "\n":
|
||||
result.append("\n")
|
||||
return "".join(result)
|
||||
"""
|
||||
# Handle epilog manually to keep the text formatting
|
||||
epilog = self.epilog
|
||||
self.epilog = ""
|
||||
help_str = super(ConsoleCommandParser, self).format_help()
|
||||
if epilog is not None:
|
||||
help_str += epilog
|
||||
self.epilog = epilog
|
||||
return help_str
|
||||
|
||||
|
||||
class OptionParser(optparse.OptionParser):
|
||||
"""subclass from optparse.OptionParser so exit() won't exit."""
|
||||
class OptionParser(ConsoleCommandParser):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
optparse.OptionParser.__init__(self, **kwargs)
|
||||
|
||||
self.formatter = DelugeHelpFormatter()
|
||||
super(OptionParser, self).__init__(**kwargs)
|
||||
self.formatter = ConsoleColorFormatter()
|
||||
|
||||
def exit(self, status=0, msg=None):
|
||||
self.values._exit = True
|
||||
self._exit = True
|
||||
if msg:
|
||||
print(msg)
|
||||
|
||||
|
@ -124,7 +73,7 @@ class OptionParser(optparse.OptionParser):
|
|||
def print_usage(self, _file=None):
|
||||
console = component.get("ConsoleUI")
|
||||
if self.usage:
|
||||
for line in self.get_usage().splitlines():
|
||||
for line in self.format_usage().splitlines():
|
||||
console.write(line)
|
||||
|
||||
def print_help(self, _file=None):
|
||||
|
@ -134,43 +83,36 @@ class OptionParser(optparse.OptionParser):
|
|||
console.write(line)
|
||||
console.set_batch_write(False)
|
||||
|
||||
def format_option_help(self, formatter=None):
|
||||
if formatter is None:
|
||||
formatter = self.formatter
|
||||
formatter.store_option_strings(self)
|
||||
result = []
|
||||
result.append(formatter.format_heading(_("{!info!}Options{!input!}")))
|
||||
formatter.indent()
|
||||
if self.option_list:
|
||||
result.append(optparse.OptionContainer.format_option_help(self, formatter))
|
||||
result.append("\\n")
|
||||
for group in self.option_groups:
|
||||
result.append(group.format_help(formatter))
|
||||
result.append("\\n")
|
||||
formatter.dedent()
|
||||
# Drop the last "\\n", or the header if no options or option groups:
|
||||
return "".join(result[:-1])
|
||||
def format_help(self):
|
||||
"""Return help formatted with colors."""
|
||||
help_str = super(OptionParser, self).format_help()
|
||||
return self.formatter.format_colors(help_str)
|
||||
|
||||
|
||||
class BaseCommand(object):
|
||||
|
||||
usage = "usage"
|
||||
usage = None
|
||||
interactive_only = False
|
||||
option_list = tuple()
|
||||
aliases = []
|
||||
_name = "base"
|
||||
epilog = ""
|
||||
|
||||
def complete(self, text, *args):
|
||||
return []
|
||||
|
||||
def handle(self, *args, **options):
|
||||
def handle(self, options):
|
||||
pass
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return "base"
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def epilog(self):
|
||||
def name_with_alias(self):
|
||||
return "/".join([self._name] + self.aliases)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return self.__doc__
|
||||
|
||||
def split(self, text):
|
||||
|
@ -183,16 +125,32 @@ class BaseCommand(object):
|
|||
return result
|
||||
|
||||
def create_parser(self):
|
||||
return OptionParser(prog=self.name, usage=self.usage, epilog=self.epilog, option_list=self.option_list)
|
||||
opts = {"prog": self.name_with_alias, "description": self.__doc__, "epilog": self.epilog}
|
||||
if self.usage:
|
||||
opts["usage"] = self.usage
|
||||
parser = OptionParser(**opts)
|
||||
parser.add_argument(self.name, metavar="")
|
||||
self.add_arguments(parser)
|
||||
return parser
|
||||
|
||||
def add_subparser(self, subparsers):
|
||||
opts = {"prog": self.name_with_alias, "help": self.__doc__, "description": self.__doc__}
|
||||
if self.usage:
|
||||
opts["usage"] = self.usage
|
||||
parser = subparsers.add_parser(self.name, **opts)
|
||||
self.add_arguments(parser)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
pass
|
||||
|
||||
|
||||
class ConsoleUI(component.Component):
|
||||
def __init__(self, args=None, cmds=None, daemon=None):
|
||||
component.Component.__init__(self, "ConsoleUI", 2)
|
||||
|
||||
def __init__(self, options=None, cmds=None):
|
||||
component.Component.__init__(self, "ConsoleUI", 2)
|
||||
# keep track of events for the log view
|
||||
self.events = []
|
||||
|
||||
self.statusbars = None
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
self.encoding = locale.getpreferredencoding()
|
||||
|
@ -209,19 +167,14 @@ class ConsoleUI(component.Component):
|
|||
# Set the interactive flag to indicate where we should print the output
|
||||
self.interactive = True
|
||||
self._commands = cmds
|
||||
if args:
|
||||
args = " ".join(args)
|
||||
|
||||
if options.remaining:
|
||||
self.interactive = False
|
||||
if not cmds:
|
||||
print("Sorry, couldn't find any commands")
|
||||
return
|
||||
else:
|
||||
from deluge.ui.console.commander import Commander
|
||||
cmdr = Commander(cmds)
|
||||
if daemon:
|
||||
cmdr.exec_args(args, *daemon)
|
||||
else:
|
||||
cmdr.exec_args(args, None, None, None, None)
|
||||
self.exec_args(options)
|
||||
|
||||
self.coreconfig = CoreConfig()
|
||||
if self.interactive and not deluge.common.windows_check():
|
||||
|
@ -240,6 +193,60 @@ Please use commands from the command line, eg:\n
|
|||
else:
|
||||
reactor.run()
|
||||
|
||||
def exec_args(self, options):
|
||||
args = options.remaining
|
||||
commands = []
|
||||
if args:
|
||||
cmd = " ".join([arg for arg in args])
|
||||
# Multiple commands split by ";"
|
||||
commands += [arg.strip() for arg in cmd.split(";")]
|
||||
|
||||
from deluge.ui.console.commander import Commander
|
||||
commander = Commander(self._commands)
|
||||
|
||||
def on_connect(result):
|
||||
def on_started(result):
|
||||
def on_started(result):
|
||||
def do_command(result, cmd):
|
||||
return commander.do_command(cmd)
|
||||
d = defer.succeed(None)
|
||||
for command in commands:
|
||||
if command in ("quit", "exit"):
|
||||
break
|
||||
d.addCallback(do_command, command)
|
||||
d.addCallback(do_command, "quit")
|
||||
|
||||
# We need to wait for the rpcs in start() to finish before processing
|
||||
# any of the commands.
|
||||
self.started_deferred.addCallback(on_started)
|
||||
component.start().addCallback(on_started)
|
||||
|
||||
def on_connect_fail(reason):
|
||||
if reason.check(DelugeError):
|
||||
rm = reason.getErrorMessage()
|
||||
else:
|
||||
rm = reason.value.message
|
||||
print("Could not connect to daemon: %s:%s\n %s" % (options.daemon_addr, options.daemon_port, rm))
|
||||
commander.do_command("quit")
|
||||
|
||||
d = None
|
||||
if not self.interactive:
|
||||
if commands[0] is not None:
|
||||
if commands[0].startswith("connect"):
|
||||
d = commander.do_command(commands.pop(0))
|
||||
if d is None:
|
||||
# Error parsing command
|
||||
sys.exit(0)
|
||||
elif "help" in commands:
|
||||
commander.do_command("help")
|
||||
sys.exit(0)
|
||||
if not d:
|
||||
log.info("connect: host=%s, port=%s, username=%s, password=%s",
|
||||
options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass)
|
||||
d = client.connect(options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass)
|
||||
d.addCallback(on_connect)
|
||||
d.addErrback(on_connect_fail)
|
||||
|
||||
def run(self, stdscr):
|
||||
"""
|
||||
This method is called by the curses.wrapper to start the mainloop and
|
||||
|
@ -332,6 +339,7 @@ Please use commands from the command line, eg:\n
|
|||
self.screen = mode
|
||||
self.statusbars.screen = self.screen
|
||||
reactor.addReader(self.screen)
|
||||
self.stdscr.clear()
|
||||
mode.refresh()
|
||||
|
||||
def on_client_disconnect(self):
|
||||
|
|
|
@ -812,9 +812,6 @@ class AllTorrents(BaseMode, component.Component):
|
|||
self.refresh()
|
||||
|
||||
def refresh(self, lines=None):
|
||||
# log.error("ref")
|
||||
# import traceback
|
||||
# traceback.print_stack()
|
||||
# Something has requested we scroll to the top of the list
|
||||
if self._go_top:
|
||||
self.cursel = 1
|
||||
|
|
|
@ -38,6 +38,7 @@ DEFAULT_CONFIG = {
|
|||
|
||||
|
||||
class ConnectionManager(BaseMode):
|
||||
|
||||
def __init__(self, stdscr, encoding=None):
|
||||
self.popup = None
|
||||
self.statuses = {}
|
||||
|
@ -87,6 +88,7 @@ class ConnectionManager(BaseMode):
|
|||
port = host[2]
|
||||
user = host[3]
|
||||
password = host[4]
|
||||
log.debug("connect: hadr=%s, port=%s, user=%s, password=%s", hadr, port, user, password)
|
||||
d = c.connect(hadr, port, user, password)
|
||||
d.addCallback(on_connect, c, host[0])
|
||||
d.addErrback(on_connect_failed, host[0])
|
||||
|
|
|
@ -24,6 +24,7 @@ import deluge.component as component
|
|||
import deluge.configmanager
|
||||
import deluge.ui.console.colors as colors
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.console.commander import Commander
|
||||
from deluge.ui.console.modes import format_utils
|
||||
from deluge.ui.console.modes.basemode import BaseMode
|
||||
|
||||
|
@ -94,11 +95,15 @@ def commonprefix(m):
|
|||
return s2
|
||||
|
||||
|
||||
class Legacy(BaseMode, component.Component):
|
||||
class Legacy(BaseMode, Commander, component.Component):
|
||||
def __init__(self, stdscr, encoding=None):
|
||||
|
||||
component.Component.__init__(self, "LegacyUI", 1, depend=["SessionProxy"])
|
||||
|
||||
# Get a handle to the main console
|
||||
self.console = component.get("ConsoleUI")
|
||||
Commander.__init__(self, self.console._commands, interactive=True)
|
||||
|
||||
self.batch_write = False
|
||||
|
||||
# A list of strings to be displayed based on the offset (scroll)
|
||||
|
@ -122,9 +127,6 @@ class Legacy(BaseMode, component.Component):
|
|||
# Keep track of double- and multi-tabs
|
||||
self.tab_count = 0
|
||||
|
||||
# Get a handle to the main console
|
||||
self.console = component.get("ConsoleUI")
|
||||
|
||||
self.console_config = component.get("AllTorrents").config
|
||||
|
||||
# To avoid having to truncate the file every time we're writing
|
||||
|
@ -586,64 +588,6 @@ class Legacy(BaseMode, component.Component):
|
|||
|
||||
col += strwidth(s)
|
||||
|
||||
def do_command(self, cmd):
|
||||
"""
|
||||
Processes a command.
|
||||
|
||||
:param cmd: str, the command string
|
||||
|
||||
"""
|
||||
if not cmd:
|
||||
return
|
||||
cmd, _, line = cmd.partition(" ")
|
||||
try:
|
||||
parser = self.console._commands[cmd].create_parser()
|
||||
except KeyError:
|
||||
self.write("{!error!}Unknown command: %s" % cmd)
|
||||
return
|
||||
|
||||
try:
|
||||
args = self.console._commands[cmd].split(line)
|
||||
except ValueError as ex:
|
||||
self.write("{!error!}Error parsing command: %s" % ex)
|
||||
return
|
||||
|
||||
# Do a little hack here to print 'command --help' properly
|
||||
parser._print_help = parser.print_help
|
||||
|
||||
def print_help(f=None):
|
||||
parser._print_help(f)
|
||||
parser.print_help = print_help
|
||||
|
||||
# Only these commands can be run when not connected to a daemon
|
||||
not_connected_cmds = ["help", "connect", "quit"]
|
||||
aliases = []
|
||||
for c in not_connected_cmds:
|
||||
aliases.extend(self.console._commands[c].aliases)
|
||||
not_connected_cmds.extend(aliases)
|
||||
|
||||
if not client.connected() and cmd not in not_connected_cmds:
|
||||
self.write("{!error!}Not connected to a daemon, please use the connect command first.")
|
||||
return
|
||||
|
||||
try:
|
||||
options, args = parser.parse_args(args)
|
||||
except TypeError as ex:
|
||||
self.write("{!error!}Error parsing options: %s" % ex)
|
||||
return
|
||||
|
||||
if not getattr(options, "_exit", False):
|
||||
try:
|
||||
ret = self.console._commands[cmd].handle(*args, **options.__dict__)
|
||||
except Exception as ex:
|
||||
self.write("{!error!} %s" % ex)
|
||||
log.exception(ex)
|
||||
import traceback
|
||||
self.write("%s" % traceback.format_exc())
|
||||
return defer.succeed(True)
|
||||
else:
|
||||
return ret
|
||||
|
||||
def set_batch_write(self, batch):
|
||||
"""
|
||||
When this is set the screen is not refreshed after a `:meth:write` until
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#
|
||||
# We skip isorting this file as it want to move the gtk2reactor.install() below the imports
|
||||
# isort:skip_file
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import logging
|
||||
|
@ -24,7 +25,7 @@ from twisted.internet.task import LoopingCall
|
|||
try:
|
||||
# Install twisted reactor, before any other modules import reactor.
|
||||
reactor = gtk2reactor.install()
|
||||
except ReactorAlreadyInstalledError:
|
||||
except ReactorAlreadyInstalledError as ex:
|
||||
# Running unit tests so trial already installed a rector
|
||||
pass
|
||||
|
||||
|
@ -196,7 +197,7 @@ class GtkUI(object):
|
|||
# Start the IPC Interface before anything else.. Just in case we are
|
||||
# already running.
|
||||
self.queuedtorrents = QueuedTorrents()
|
||||
self.ipcinterface = IPCInterface(args)
|
||||
self.ipcinterface = IPCInterface(args.torrents)
|
||||
|
||||
# Initialize gdk threading
|
||||
gtk.gdk.threads_init()
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
#
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
|
||||
import deluge.common
|
||||
import deluge.configmanager
|
||||
import deluge.log
|
||||
from deluge.commonoptions import CommonOptionParser
|
||||
from deluge.ui.baseargparser import BaseArgParser
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from setproctitle import setproctitle
|
||||
|
@ -21,10 +22,6 @@ except ImportError:
|
|||
def setproctitle(title):
|
||||
return
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
"default_ui": "gtk"
|
||||
}
|
||||
|
||||
if 'dev' not in deluge.common.get_version():
|
||||
import warnings
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning, module='twisted')
|
||||
|
@ -32,15 +29,16 @@ if 'dev' not in deluge.common.get_version():
|
|||
|
||||
class UI(object):
|
||||
|
||||
def __init__(self, name="gtk", skip_common=False):
|
||||
def __init__(self, name="gtk", parser=None):
|
||||
self.__name = name
|
||||
self.__parser = parser if parser else BaseArgParser()
|
||||
deluge.common.setup_translations(setup_pygtk=(name == "gtk"))
|
||||
|
||||
if name == "gtk":
|
||||
deluge.common.setup_translations(setup_pygtk=True)
|
||||
else:
|
||||
deluge.common.setup_translations()
|
||||
|
||||
self.__parser = optparse.OptionParser() if skip_common else CommonOptionParser()
|
||||
def parse_args(self, args=None):
|
||||
options = self.parser.parse_args(args)
|
||||
if not hasattr(options, "remaining"):
|
||||
options.remaining = []
|
||||
return options
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -54,22 +52,15 @@ class UI(object):
|
|||
def options(self):
|
||||
return self.__options
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.__args
|
||||
def start(self, extra_args=None):
|
||||
args = deluge.common.unicode_argv()[1:]
|
||||
if extra_args:
|
||||
args.extend(extra_args)
|
||||
|
||||
def start(self, args=None):
|
||||
if args is None:
|
||||
# Make sure all arguments are unicode
|
||||
args = deluge.common.unicode_argv()[1:]
|
||||
|
||||
self.__options, self.__args = self.__parser.parse_args(args)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
self.__options = self.parse_args(args)
|
||||
|
||||
setproctitle("deluge-%s" % self.__name)
|
||||
|
||||
log.info("Deluge ui %s", deluge.common.get_version())
|
||||
log.debug("options: %s", self.__options)
|
||||
log.debug("args: %s", self.__args)
|
||||
log.info("Starting %s ui..", self.__name)
|
||||
|
|
|
@ -10,13 +10,18 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
from optparse import OptionGroup
|
||||
|
||||
from deluge.common import osx_check, windows_check
|
||||
from deluge.configmanager import get_config_dir
|
||||
from deluge.ui.ui import UI
|
||||
|
||||
|
||||
#
|
||||
# Note: Cannot import twisted.internet.reactor because Web is imported from
|
||||
# from web/__init__.py loaded by the script entry points defined in setup.py
|
||||
#
|
||||
|
||||
|
||||
class WebUI(object):
|
||||
def __init__(self, args):
|
||||
from deluge.ui.web import server
|
||||
|
@ -33,36 +38,36 @@ class Web(UI):
|
|||
super(Web, self).__init__("web", *args, **kwargs)
|
||||
self.__server = None
|
||||
|
||||
group = OptionGroup(self.parser, "Web Options")
|
||||
group.add_option("-b", "--base", dest="base", action="store", default=None,
|
||||
help="Set the base path that the ui is running on (proxying)")
|
||||
group = self.parser.add_argument_group(_('Web Options'))
|
||||
|
||||
group.add_argument("-b", "--base", metavar="<path>", action="store", default=None,
|
||||
help="Set the base path that the ui is running on (proxying)")
|
||||
if not (windows_check() or osx_check()):
|
||||
group.add_option("-d", "--do-not-daemonize", dest="donotdaemonize", action="store_true", default=False,
|
||||
help="Do not daemonize the web interface")
|
||||
group.add_option("-P", "--pidfile", dest="pidfile", type="str", action="store", default=None,
|
||||
help="Use pidfile to store process id")
|
||||
group.add_argument("-d", "--do-not-daemonize", dest="donotdaemonize", action="store_true", default=False,
|
||||
help="Do not daemonize the web interface")
|
||||
group.add_argument("-P", "--pidfile", metavar="<pidfile>", action="store", default=None,
|
||||
help="Use pidfile to store process id")
|
||||
if not windows_check():
|
||||
group.add_option("-U", "--user", dest="user", type="str", action="store", default=None,
|
||||
help="User to switch to. Only use it when starting as root")
|
||||
group.add_option("-g", "--group", dest="group", type="str", action="store", default=None,
|
||||
help="Group to switch to. Only use it when starting as root")
|
||||
group.add_option("-i", "--interface", dest="interface", action="store", default=None,
|
||||
type="str", help="Binds the webserver to a specific IP address")
|
||||
group.add_option("-p", "--port", dest="port", type="int", action="store", default=None,
|
||||
help="Sets the port to be used for the webserver")
|
||||
group.add_option("--profile", dest="profile", action="store_true", default=False,
|
||||
help="Profile the web server code")
|
||||
group.add_argument("-U", "--user", metavar="<user>", action="store", default=None,
|
||||
help="User to switch to. Only use it when starting as root")
|
||||
group.add_argument("-g", "--group", metavar="<group>", action="store", default=None,
|
||||
help="Group to switch to. Only use it when starting as root")
|
||||
group.add_argument("-i", "--interface", metavar="<interface>", action="store", default=None,
|
||||
help="Binds the webserver to a specific IP address")
|
||||
group.add_argument("-p", "--port", metavar="<port>", type=int, action="store", default=None,
|
||||
help="Sets the port to be used for the webserver")
|
||||
group.add_argument("--profile", action="store_true", default=False,
|
||||
help="Profile the web server code")
|
||||
try:
|
||||
import OpenSSL
|
||||
assert OpenSSL.__version__
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
group.add_option("--no-ssl", dest="ssl", action="store_false",
|
||||
help="Forces the webserver to disable ssl", default=False)
|
||||
group.add_option("--ssl", dest="ssl", action="store_true",
|
||||
help="Forces the webserver to use ssl", default=False)
|
||||
self.parser.add_option_group(group)
|
||||
group.add_argument("--no-ssl", dest="ssl", action="store_false",
|
||||
help="Forces the webserver to disable ssl", default=False)
|
||||
group.add_argument("--ssl", dest="ssl", action="store_true",
|
||||
help="Forces the webserver to use ssl", default=False)
|
||||
|
||||
@property
|
||||
def server(self):
|
||||
|
@ -73,7 +78,7 @@ class Web(UI):
|
|||
|
||||
# Steps taken from http://www.faqs.org/faqs/unix-faq/programmer/faq/
|
||||
# Section 1.7
|
||||
if not self.options.ensure_value("donotdaemonize", True):
|
||||
if self.options.donotdaemonize is not True:
|
||||
# fork() so the parent can exit, returns control to the command line
|
||||
# or shell invoking the program.
|
||||
if os.fork():
|
||||
|
@ -93,12 +98,12 @@ class Web(UI):
|
|||
if self.options.pidfile:
|
||||
open(self.options.pidfile, "wb").write("%d\n" % os.getpid())
|
||||
|
||||
if self.options.ensure_value("group", None):
|
||||
if self.options.group:
|
||||
if not self.options.group.isdigit():
|
||||
import grp
|
||||
self.options.group = grp.getgrnam(self.options.group)[2]
|
||||
os.setuid(self.options.group)
|
||||
if self.options.ensure_value("user", None):
|
||||
if self.options.user:
|
||||
if not self.options.user.isdigit():
|
||||
import pwd
|
||||
self.options.user = pwd.getpwnam(self.options.user)[2]
|
||||
|
@ -116,8 +121,7 @@ class Web(UI):
|
|||
if self.options.port:
|
||||
self.server.port = self.options.port
|
||||
|
||||
if self.options.ensure_value("ssl", None):
|
||||
self.server.https = self.options.ssl
|
||||
self.server.https = self.options.ssl
|
||||
|
||||
def run_server():
|
||||
self.server.install_signal_handlers()
|
||||
|
@ -133,7 +137,7 @@ class Web(UI):
|
|||
profiler.dump_stats(profile_output)
|
||||
print("Profile stats saved to %s" % profile_output)
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import reactor # import here because (see top)
|
||||
reactor.addSystemEventTrigger("before", "shutdown", save_profile_stats)
|
||||
print("Running with profiler...")
|
||||
profiler.runcall(run_server)
|
||||
|
|
Loading…
Reference in New Issue