mirror of
https://git.deluge-torrent.org/deluge
synced 2025-04-19 19:14:55 +00:00
Fix files to pass new Flake8 checkers
Some new flake8 checkers were added so fix these new warnings and any issues uncovered. Use add-trailing-comma to fix missing trailing commas. It does not format it as well as I would like however it was fast to change and helps with git changes in future. Removed pylint from tox due to large number of warnings.
This commit is contained in:
parent
bae1647e99
commit
d642fa3989
166 changed files with 3294 additions and 1907 deletions
|
@ -19,7 +19,7 @@ class RpcApi(object):
|
|||
|
||||
def scan_for_methods(obj):
|
||||
methods = {
|
||||
'__doc__': 'Methods available in %s' % obj.__name__.lower()
|
||||
'__doc__': 'Methods available in %s' % obj.__name__.lower(),
|
||||
}
|
||||
for d in dir(obj):
|
||||
if not hasattr(getattr(obj, d), '_rpcserver_export'):
|
||||
|
|
|
@ -31,9 +31,9 @@ def decode_int(x, f):
|
|||
f += 1
|
||||
newf = x.index(END_DELIM, f)
|
||||
n = int(x[f:newf])
|
||||
if x[f:f+1] == b'-' and x[f+1:f+2] == b'0':
|
||||
if x[f:f + 1] == b'-' and x[f + 1:f + 2] == b'0':
|
||||
raise ValueError
|
||||
elif x[f:f+1] == b'0' and newf != f + 1:
|
||||
elif x[f:f + 1] == b'0' and newf != f + 1:
|
||||
raise ValueError
|
||||
return (n, newf + 1)
|
||||
|
||||
|
@ -41,7 +41,7 @@ def decode_int(x, f):
|
|||
def decode_string(x, f):
|
||||
colon = x.index(BYTE_SEP, f)
|
||||
n = int(x[f:colon])
|
||||
if x[f:f+1] == b'0' and colon != f + 1:
|
||||
if x[f:f + 1] == b'0' and colon != f + 1:
|
||||
raise ValueError
|
||||
colon += 1
|
||||
return (x[colon:colon + n], colon + n)
|
||||
|
@ -49,17 +49,17 @@ def decode_string(x, f):
|
|||
|
||||
def decode_list(x, f):
|
||||
r, f = [], f + 1
|
||||
while x[f:f+1] != END_DELIM:
|
||||
v, f = decode_func[x[f:f+1]](x, f)
|
||||
while x[f:f + 1] != END_DELIM:
|
||||
v, f = decode_func[x[f:f + 1]](x, f)
|
||||
r.append(v)
|
||||
return (r, f + 1)
|
||||
|
||||
|
||||
def decode_dict(x, f):
|
||||
r, f = {}, f + 1
|
||||
while x[f:f+1] != END_DELIM:
|
||||
while x[f:f + 1] != END_DELIM:
|
||||
k, f = decode_string(x, f)
|
||||
r[k], f = decode_func[x[f:f+1]](x, f)
|
||||
r[k], f = decode_func[x[f:f + 1]](x, f)
|
||||
return (r, f + 1)
|
||||
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ TORRENT_STATE = [
|
|||
'Paused',
|
||||
'Error',
|
||||
'Queued',
|
||||
'Moving'
|
||||
'Moving',
|
||||
]
|
||||
|
||||
# The output formatting for json.dump
|
||||
|
@ -103,7 +103,8 @@ def get_default_config_dir(filename=None):
|
|||
import _winreg as winreg # For Python 2.
|
||||
hkey = winreg.OpenKey(
|
||||
winreg.HKEY_CURRENT_USER,
|
||||
'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders')
|
||||
'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders',
|
||||
)
|
||||
app_data_reg = winreg.QueryValueEx(hkey, 'AppData')
|
||||
app_data_path = app_data_reg[0]
|
||||
winreg.CloseKey(hkey)
|
||||
|
@ -273,7 +274,7 @@ def resource_filename(module, path):
|
|||
This is a work-around that.
|
||||
"""
|
||||
return pkg_resources.require('Deluge>=%s' % get_version())[0].get_resource_filename(
|
||||
pkg_resources._manager, os.path.join(*(module.split('.') + [path]))
|
||||
pkg_resources._manager, os.path.join(*(module.split('.') + [path])),
|
||||
)
|
||||
|
||||
|
||||
|
@ -554,18 +555,20 @@ def tokenize(text):
|
|||
return tokenized_input
|
||||
|
||||
|
||||
size_units = (dict(prefix='b', divider=1, singular='byte', plural='bytes'),
|
||||
dict(prefix='KiB', divider=1024**1),
|
||||
dict(prefix='MiB', divider=1024**2),
|
||||
dict(prefix='GiB', divider=1024**3),
|
||||
dict(prefix='TiB', divider=1024**4),
|
||||
dict(prefix='PiB', divider=1024**5),
|
||||
dict(prefix='KB', divider=1000**1),
|
||||
dict(prefix='MB', divider=1000**2),
|
||||
dict(prefix='GB', divider=1000**3),
|
||||
dict(prefix='TB', divider=1000**4),
|
||||
dict(prefix='PB', divider=1000**5),
|
||||
dict(prefix='m', divider=1000**2))
|
||||
size_units = [
|
||||
{'prefix': 'b', 'divider': 1, 'singular': 'byte', 'plural': 'bytes'},
|
||||
{'prefix': 'KiB', 'divider': 1024**1},
|
||||
{'prefix': 'MiB', 'divider': 1024**2},
|
||||
{'prefix': 'GiB', 'divider': 1024**3},
|
||||
{'prefix': 'TiB', 'divider': 1024**4},
|
||||
{'prefix': 'PiB', 'divider': 1024**5},
|
||||
{'prefix': 'KB', 'divider': 1000**1},
|
||||
{'prefix': 'MB', 'divider': 1000**2},
|
||||
{'prefix': 'GB', 'divider': 1000**3},
|
||||
{'prefix': 'TB', 'divider': 1000**4},
|
||||
{'prefix': 'PB', 'divider': 1000**5},
|
||||
{'prefix': 'm', 'divider': 1000**2},
|
||||
]
|
||||
|
||||
|
||||
class InvalidSize(Exception):
|
||||
|
@ -906,10 +909,12 @@ def decode_bytes(byte_str, encoding='utf8'):
|
|||
elif not isinstance(byte_str, bytes):
|
||||
return byte_str
|
||||
|
||||
encodings = [lambda: ('utf8', 'strict'),
|
||||
lambda: ('iso-8859-1', 'strict'),
|
||||
lambda: (chardet.detect(byte_str)['encoding'], 'strict'),
|
||||
lambda: (encoding, 'ignore')]
|
||||
encodings = [
|
||||
lambda: ('utf8', 'strict'),
|
||||
lambda: ('iso-8859-1', 'strict'),
|
||||
lambda: (chardet.detect(byte_str)['encoding'], 'strict'),
|
||||
lambda: (encoding, 'ignore'),
|
||||
]
|
||||
|
||||
if encoding is not 'utf8':
|
||||
encodings.insert(0, lambda: (encoding, 'strict'))
|
||||
|
@ -949,7 +954,10 @@ def utf8_encode_structure(data):
|
|||
if isinstance(data, (list, tuple)):
|
||||
return type(data)([utf8_encode_structure(d) for d in data])
|
||||
elif isinstance(data, dict):
|
||||
return dict([utf8_encode_structure(d) for d in data.items()])
|
||||
return {
|
||||
utf8_encode_structure(k): utf8_encode_structure(v)
|
||||
for k, v in data.items()
|
||||
}
|
||||
elif not isinstance(data, bytes):
|
||||
try:
|
||||
return data.encode('utf8')
|
||||
|
@ -968,7 +976,8 @@ class VersionSplit(object):
|
|||
|
||||
"""
|
||||
def __init__(self, ver):
|
||||
version_re = re.compile(r"""
|
||||
version_re = re.compile(
|
||||
r"""
|
||||
^
|
||||
(?P<version>\d+\.\d+) # minimum 'N.N'
|
||||
(?P<extraversion>(?:\.\d+)*) # any number of extra '.N' segments
|
||||
|
@ -978,7 +987,8 @@ class VersionSplit(object):
|
|||
(?P<prerelversion>\d+(?:\.\d+)*)
|
||||
)?
|
||||
(?P<postdev>(\.post(?P<post>\d+))?(\.dev(?P<dev>\d+))?)?
|
||||
$""", re.VERBOSE)
|
||||
$""", re.VERBOSE,
|
||||
)
|
||||
|
||||
# Check for PEP 386 compliant version
|
||||
match = re.search(version_re, ver)
|
||||
|
@ -1061,11 +1071,13 @@ def create_localclient_account(append=False):
|
|||
create_auth_file()
|
||||
|
||||
with open(auth_file, 'a' if append else 'w') as _file:
|
||||
_file.write(':'.join([
|
||||
'localclient',
|
||||
sha(str(random.random()).encode('utf8')).hexdigest(),
|
||||
str(AUTH_LEVEL_ADMIN)
|
||||
]) + '\n')
|
||||
_file.write(
|
||||
':'.join([
|
||||
'localclient',
|
||||
sha(str(random.random()).encode('utf8')).hexdigest(),
|
||||
str(AUTH_LEVEL_ADMIN),
|
||||
]) + '\n',
|
||||
)
|
||||
_file.flush()
|
||||
os.fsync(_file.fileno())
|
||||
|
||||
|
|
|
@ -146,10 +146,12 @@ class Component(object):
|
|||
elif self._component_state == 'Started':
|
||||
d = succeed(True)
|
||||
else:
|
||||
d = fail(ComponentException('Trying to start component "%s" but it is '
|
||||
'not in a stopped state. Current state: %s' %
|
||||
(self._component_name, self._component_state),
|
||||
traceback.format_stack(limit=4)))
|
||||
d = fail(ComponentException(
|
||||
'Trying to start component "%s" but it is '
|
||||
'not in a stopped state. Current state: %s' %
|
||||
(self._component_name, self._component_state),
|
||||
traceback.format_stack(limit=4),
|
||||
))
|
||||
return d
|
||||
|
||||
def _component_stop(self):
|
||||
|
@ -193,10 +195,12 @@ class Component(object):
|
|||
elif self._component_state == 'Paused':
|
||||
d = succeed(None)
|
||||
else:
|
||||
d = fail(ComponentException('Trying to pause component "%s" but it is '
|
||||
'not in a started state. Current state: %s' %
|
||||
(self._component_name, self._component_state),
|
||||
traceback.format_stack(limit=4)))
|
||||
d = fail(ComponentException(
|
||||
'Trying to pause component "%s" but it is '
|
||||
'not in a started state. Current state: %s' %
|
||||
(self._component_name, self._component_state),
|
||||
traceback.format_stack(limit=4),
|
||||
))
|
||||
return d
|
||||
|
||||
def _component_resume(self):
|
||||
|
@ -207,10 +211,12 @@ class Component(object):
|
|||
d = maybeDeferred(self._component_start_timer)
|
||||
d.addCallback(on_resume)
|
||||
else:
|
||||
d = fail(ComponentException('Trying to resume component "%s" but it is '
|
||||
'not in a paused state. Current state: %s' %
|
||||
(self._component_name, self._component_state),
|
||||
traceback.format_stack(limit=4)))
|
||||
d = fail(ComponentException(
|
||||
'Trying to resume component "%s" but it is '
|
||||
'not in a paused state. Current state: %s' %
|
||||
(self._component_name, self._component_state),
|
||||
traceback.format_stack(limit=4),
|
||||
))
|
||||
return d
|
||||
|
||||
def _component_shutdown(self):
|
||||
|
|
|
@ -128,7 +128,7 @@ class Config(object):
|
|||
# These hold the version numbers and they will be set when loaded
|
||||
self.__version = {
|
||||
'format': 1,
|
||||
'file': file_version
|
||||
'file': file_version,
|
||||
}
|
||||
|
||||
# This will get set with a reactor.callLater whenever a config option
|
||||
|
@ -189,7 +189,8 @@ class Config(object):
|
|||
|
||||
# Do not allow the type to change unless it is None
|
||||
if value is not None and not isinstance(
|
||||
self.__config[key], type(None)) and not isinstance(self.__config[key], type(value)):
|
||||
self.__config[key], type(None),
|
||||
) and not isinstance(self.__config[key], type(value)):
|
||||
try:
|
||||
oldtype = type(self.__config[key])
|
||||
value = oldtype(value)
|
||||
|
@ -427,8 +428,10 @@ class Config(object):
|
|||
log.exception(ex)
|
||||
log.warning('Unable to load config file: %s', filename)
|
||||
|
||||
log.debug('Config %s version: %s.%s loaded: %s', filename,
|
||||
self.__version['format'], self.__version['file'], self.__config)
|
||||
log.debug(
|
||||
'Config %s version: %s.%s loaded: %s', filename,
|
||||
self.__version['format'], self.__version['file'], self.__config,
|
||||
)
|
||||
|
||||
def save(self, filename=None):
|
||||
"""Save configuration to disk.
|
||||
|
@ -510,16 +513,20 @@ class Config(object):
|
|||
raise ValueError('output_version needs to be greater than input_range')
|
||||
|
||||
if self.__version['file'] not in input_range:
|
||||
log.debug('File version %s is not in input_range %s, ignoring converter function..',
|
||||
self.__version['file'], input_range)
|
||||
log.debug(
|
||||
'File version %s is not in input_range %s, ignoring converter function..',
|
||||
self.__version['file'], input_range,
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
self.__config = func(self.__config)
|
||||
except Exception as ex:
|
||||
log.exception(ex)
|
||||
log.error('There was an exception try to convert config file %s %s to %s',
|
||||
self.__config_file, self.__version['file'], output_version)
|
||||
log.error(
|
||||
'There was an exception try to convert config file %s %s to %s',
|
||||
self.__config_file, self.__version['file'], output_version,
|
||||
)
|
||||
raise ex
|
||||
else:
|
||||
self.__version['file'] = output_version
|
||||
|
|
|
@ -94,9 +94,11 @@ class _ConfigManager(object):
|
|||
log.debug('Getting config: %s', config_file)
|
||||
# Create the config object if not already created
|
||||
if config_file not in self.config_files:
|
||||
self.config_files[config_file] = Config(config_file, defaults,
|
||||
config_dir=self.config_directory,
|
||||
file_version=file_version)
|
||||
self.config_files[config_file] = Config(
|
||||
config_file, defaults,
|
||||
config_dir=self.config_directory,
|
||||
file_version=file_version,
|
||||
)
|
||||
|
||||
return self.config_files[config_file]
|
||||
|
||||
|
|
|
@ -39,13 +39,15 @@ class AlertManager(component.Component):
|
|||
self.alert_queue_size = 10000
|
||||
self.set_alert_queue_size(self.alert_queue_size)
|
||||
|
||||
alert_mask = (lt.alert.category_t.error_notification |
|
||||
lt.alert.category_t.port_mapping_notification |
|
||||
lt.alert.category_t.storage_notification |
|
||||
lt.alert.category_t.tracker_notification |
|
||||
lt.alert.category_t.status_notification |
|
||||
lt.alert.category_t.ip_block_notification |
|
||||
lt.alert.category_t.performance_warning)
|
||||
alert_mask = (
|
||||
lt.alert.category_t.error_notification |
|
||||
lt.alert.category_t.port_mapping_notification |
|
||||
lt.alert.category_t.storage_notification |
|
||||
lt.alert.category_t.tracker_notification |
|
||||
lt.alert.category_t.status_notification |
|
||||
lt.alert.category_t.ip_block_notification |
|
||||
lt.alert.category_t.performance_warning
|
||||
)
|
||||
|
||||
self.session.apply_settings({'alert_mask': alert_mask})
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ AUTH_LEVELS_MAPPING = {
|
|||
'READONLY': AUTH_LEVEL_READONLY,
|
||||
'DEFAULT': AUTH_LEVEL_NORMAL,
|
||||
'NORMAL': AUTH_LEVEL_DEFAULT,
|
||||
'ADMIN': AUTH_LEVEL_ADMIN}
|
||||
'ADMIN': AUTH_LEVEL_ADMIN,
|
||||
}
|
||||
AUTH_LEVELS_MAPPING_REVERSE = {v: k for k, v in AUTH_LEVELS_MAPPING.items()}
|
||||
|
||||
|
||||
|
@ -45,7 +46,7 @@ class Account(object):
|
|||
'username': self.username,
|
||||
'password': self.password,
|
||||
'authlevel': AUTH_LEVELS_MAPPING_REVERSE[self.authlevel],
|
||||
'authlevel_int': self.authlevel
|
||||
'authlevel_int': self.authlevel,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -98,7 +99,7 @@ class AuthManager(component.Component):
|
|||
"""
|
||||
if not username:
|
||||
raise AuthenticationRequired(
|
||||
'Username and Password are required.', username
|
||||
'Username and Password are required.', username,
|
||||
)
|
||||
|
||||
if username not in self.__auth:
|
||||
|
@ -129,8 +130,10 @@ class AuthManager(component.Component):
|
|||
if authlevel not in AUTH_LEVELS_MAPPING:
|
||||
raise AuthManagerError('Invalid auth level: %s' % authlevel)
|
||||
try:
|
||||
self.__auth[username] = Account(username, password,
|
||||
AUTH_LEVELS_MAPPING[authlevel])
|
||||
self.__auth[username] = Account(
|
||||
username, password,
|
||||
AUTH_LEVELS_MAPPING[authlevel],
|
||||
)
|
||||
self.write_auth_file()
|
||||
return True
|
||||
except Exception as ex:
|
||||
|
@ -157,7 +160,7 @@ class AuthManager(component.Component):
|
|||
raise AuthManagerError('Username not known', username)
|
||||
elif username == component.get('RPCServer').get_session_user():
|
||||
raise AuthManagerError(
|
||||
'You cannot delete your own account while logged in!', username
|
||||
'You cannot delete your own account while logged in!', username,
|
||||
)
|
||||
|
||||
del self.__auth[username]
|
||||
|
@ -232,8 +235,10 @@ class AuthManager(component.Component):
|
|||
lsplit = line.split(':')
|
||||
if len(lsplit) == 2:
|
||||
username, password = lsplit
|
||||
log.warning('Your auth entry for %s contains no auth level, '
|
||||
'using AUTH_LEVEL_DEFAULT(%s)..', username, AUTH_LEVEL_DEFAULT)
|
||||
log.warning(
|
||||
'Your auth entry for %s contains no auth level, '
|
||||
'using AUTH_LEVEL_DEFAULT(%s)..', username, AUTH_LEVEL_DEFAULT,
|
||||
)
|
||||
if username == 'localclient':
|
||||
authlevel = AUTH_LEVEL_ADMIN
|
||||
else:
|
||||
|
|
|
@ -109,7 +109,8 @@ class Core(component.Component):
|
|||
peer_id = self._create_peer_id(DELUGE_VER)
|
||||
log.debug(
|
||||
'Starting session (peer_id: %s, user_agent: %s)',
|
||||
peer_id, user_agent)
|
||||
peer_id, user_agent,
|
||||
)
|
||||
settings_pack = {
|
||||
'peer_fingerprint': peer_id,
|
||||
'user_agent': user_agent,
|
||||
|
@ -390,7 +391,7 @@ class Core(component.Component):
|
|||
|
||||
try:
|
||||
d = self.torrentmanager.add_async(
|
||||
filedump=filedump, options=options, filename=filename, save_state=save_state
|
||||
filedump=filedump, options=options, filename=filename, save_state=save_state,
|
||||
)
|
||||
except RuntimeError as ex:
|
||||
log.error('There was an error adding the torrent file %s: %s', filename, ex)
|
||||
|
@ -418,7 +419,8 @@ class Core(component.Component):
|
|||
|
||||
try:
|
||||
return self.torrentmanager.add(
|
||||
filedump=filedump, options=options, filename=filename)
|
||||
filedump=filedump, options=options, filename=filename,
|
||||
)
|
||||
except RuntimeError as ex:
|
||||
log.error('There was an error adding the torrent file %s: %s', filename, ex)
|
||||
raise
|
||||
|
@ -441,7 +443,8 @@ class Core(component.Component):
|
|||
for idx, torrent in enumerate(torrent_files):
|
||||
try:
|
||||
yield self.add_torrent_file_async(
|
||||
torrent[0], torrent[1], torrent[2], save_state=idx == last_index)
|
||||
torrent[0], torrent[1], torrent[2], save_state=idx == last_index,
|
||||
)
|
||||
except AddTorrentError as ex:
|
||||
log.warn('Error when adding torrent: %s', ex)
|
||||
errors.append(ex)
|
||||
|
@ -651,8 +654,10 @@ class Core(component.Component):
|
|||
@export
|
||||
def get_torrent_status(self, torrent_id, keys, diff=False):
|
||||
torrent_keys, plugin_keys = self.torrentmanager.separate_keys(keys, [torrent_id])
|
||||
return self.create_torrent_status(torrent_id, torrent_keys, plugin_keys, diff=diff, update=True,
|
||||
all_keys=not keys)
|
||||
return self.create_torrent_status(
|
||||
torrent_id, torrent_keys, plugin_keys, diff=diff, update=True,
|
||||
all_keys=not keys,
|
||||
)
|
||||
|
||||
@export
|
||||
def get_torrents_status(self, filter_dict, keys, diff=False):
|
||||
|
@ -699,7 +704,7 @@ class Core(component.Component):
|
|||
@export
|
||||
def get_config_values(self, keys):
|
||||
"""Get the config values for the entered keys"""
|
||||
return dict((key, self.config.get(key)) for key in keys)
|
||||
return {key: self.config.get(key) for key in keys}
|
||||
|
||||
@export
|
||||
def set_config(self, config):
|
||||
|
@ -740,7 +745,7 @@ class Core(component.Component):
|
|||
'port': proxy_port,
|
||||
'proxy_hostnames': settings['proxy_hostnames'],
|
||||
'proxy_peer_connections': settings['proxy_peer_connections'],
|
||||
'proxy_tracker_connections': settings['proxy_tracker_connections']
|
||||
'proxy_tracker_connections': settings['proxy_tracker_connections'],
|
||||
}
|
||||
|
||||
return proxy_dict
|
||||
|
@ -870,25 +875,32 @@ class Core(component.Component):
|
|||
return deluge.common.get_path_size(path)
|
||||
|
||||
@export
|
||||
def create_torrent(self, path, tracker, piece_length, comment, target,
|
||||
webseeds, private, created_by, trackers, add_to_session):
|
||||
def create_torrent(
|
||||
self, path, tracker, piece_length, comment, target,
|
||||
webseeds, private, created_by, trackers, add_to_session,
|
||||
):
|
||||
|
||||
log.debug('creating torrent..')
|
||||
threading.Thread(target=self._create_torrent_thread,
|
||||
args=(
|
||||
path,
|
||||
tracker,
|
||||
piece_length,
|
||||
comment,
|
||||
target,
|
||||
webseeds,
|
||||
private,
|
||||
created_by,
|
||||
trackers,
|
||||
add_to_session)).start()
|
||||
threading.Thread(
|
||||
target=self._create_torrent_thread,
|
||||
args=(
|
||||
path,
|
||||
tracker,
|
||||
piece_length,
|
||||
comment,
|
||||
target,
|
||||
webseeds,
|
||||
private,
|
||||
created_by,
|
||||
trackers,
|
||||
add_to_session,
|
||||
),
|
||||
).start()
|
||||
|
||||
def _create_torrent_thread(self, path, tracker, piece_length, comment, target,
|
||||
webseeds, private, created_by, trackers, add_to_session):
|
||||
def _create_torrent_thread(
|
||||
self, path, tracker, piece_length, comment, target,
|
||||
webseeds, private, created_by, trackers, add_to_session,
|
||||
):
|
||||
from deluge import metafile
|
||||
metafile.make_meta_file(
|
||||
path,
|
||||
|
@ -899,7 +911,8 @@ class Core(component.Component):
|
|||
webseeds=webseeds,
|
||||
private=private,
|
||||
created_by=created_by,
|
||||
trackers=trackers)
|
||||
trackers=trackers,
|
||||
)
|
||||
log.debug('torrent created!')
|
||||
if add_to_session:
|
||||
options = {}
|
||||
|
@ -1055,8 +1068,10 @@ class Core(component.Component):
|
|||
:rtype: bool
|
||||
|
||||
"""
|
||||
d = getPage(b'http://deluge-torrent.org/test_port.php?port=%s' %
|
||||
self.get_listen_port(), timeout=30)
|
||||
d = getPage(
|
||||
b'http://deluge-torrent.org/test_port.php?port=%s' %
|
||||
self.get_listen_port(), timeout=30,
|
||||
)
|
||||
|
||||
def on_get_page(result):
|
||||
return bool(int(result))
|
||||
|
|
|
@ -65,8 +65,10 @@ def is_daemon_running(pid_file):
|
|||
class Daemon(object):
|
||||
"""The Deluge Daemon class"""
|
||||
|
||||
def __init__(self, listen_interface=None, outgoing_interface=None, interface=None, port=None, standalone=False,
|
||||
read_only_config_keys=None):
|
||||
def __init__(
|
||||
self, listen_interface=None, outgoing_interface=None, interface=None, port=None, standalone=False,
|
||||
read_only_config_keys=None,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
listen_interface (str, optional): The IP address to listen to bittorrent connections on.
|
||||
|
@ -98,9 +100,11 @@ class Daemon(object):
|
|||
SetConsoleCtrlHandler(win_handler)
|
||||
|
||||
# Start the core as a thread and join it until it's done
|
||||
self.core = Core(listen_interface=listen_interface,
|
||||
outgoing_interface=outgoing_interface,
|
||||
read_only_config_keys=read_only_config_keys)
|
||||
self.core = Core(
|
||||
listen_interface=listen_interface,
|
||||
outgoing_interface=outgoing_interface,
|
||||
read_only_config_keys=read_only_config_keys,
|
||||
)
|
||||
|
||||
if port is None:
|
||||
port = self.core.config['daemon_port']
|
||||
|
@ -114,11 +118,13 @@ class Daemon(object):
|
|||
port=port,
|
||||
allow_remote=self.core.config['allow_remote'],
|
||||
listen=not standalone,
|
||||
interface=interface
|
||||
interface=interface,
|
||||
)
|
||||
|
||||
log.debug('Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s',
|
||||
interface, port, listen_interface, outgoing_interface)
|
||||
log.debug(
|
||||
'Listening to UI on: %s:%s and bittorrent on: %s Making connections out on: %s',
|
||||
interface, port, listen_interface, outgoing_interface,
|
||||
)
|
||||
|
||||
def start(self):
|
||||
# Register the daemon and the core RPCs
|
||||
|
|
|
@ -23,16 +23,26 @@ from deluge.ui.translations_util import set_dummy_trans
|
|||
|
||||
def add_daemon_options(parser):
|
||||
group = parser.add_argument_group(_('Daemon Options'))
|
||||
group.add_argument('-u', '--ui-interface', metavar='<ip-addr>', action='store',
|
||||
help=_('IP address to listen for UI connections'))
|
||||
group.add_argument('-p', '--port', metavar='<port>', action='store', type=int,
|
||||
help=_('Port to listen for UI connections on'))
|
||||
group.add_argument('-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store',
|
||||
help=_('IP address to listen for BitTorrent connections'))
|
||||
group.add_argument('-o', '--outinterface', metavar='<ip-addr>', dest='outgoing_interface',
|
||||
action='store', help=_('The IP address for outgoing BitTorrent connections.'))
|
||||
group.add_argument('--read-only-config-keys', metavar='<comma-separated-keys>', action='store',
|
||||
help=_('Config keys to be unmodified by `set_config` RPC'), type=str, default='')
|
||||
group.add_argument(
|
||||
'-u', '--ui-interface', metavar='<ip-addr>', action='store',
|
||||
help=_('IP address to listen for UI connections'),
|
||||
)
|
||||
group.add_argument(
|
||||
'-p', '--port', metavar='<port>', action='store', type=int,
|
||||
help=_('Port to listen for UI connections on'),
|
||||
)
|
||||
group.add_argument(
|
||||
'-i', '--interface', metavar='<ip-addr>', dest='listen_interface', action='store',
|
||||
help=_('IP address to listen for BitTorrent connections'),
|
||||
)
|
||||
group.add_argument(
|
||||
'-o', '--outinterface', metavar='<ip-addr>', dest='outgoing_interface',
|
||||
action='store', help=_('The IP address for outgoing BitTorrent connections.'),
|
||||
)
|
||||
group.add_argument(
|
||||
'--read-only-config-keys', metavar='<comma-separated-keys>', action='store',
|
||||
help=_('Config keys to be unmodified by `set_config` RPC'), type=str, default='',
|
||||
)
|
||||
parser.add_process_arg_group()
|
||||
|
||||
|
||||
|
@ -59,8 +69,10 @@ def start_daemon(skip_start=False):
|
|||
from deluge.core.daemon import is_daemon_running
|
||||
pid_file = get_config_dir('deluged.pid')
|
||||
if is_daemon_running(pid_file):
|
||||
print('Cannot run multiple daemons with same config directory.\n'
|
||||
'If you believe this is an error, force starting by deleting: %s' % pid_file)
|
||||
print(
|
||||
'Cannot run multiple daemons with same config directory.\n'
|
||||
'If you believe this is an error, force starting by deleting: %s' % pid_file,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
@ -74,19 +86,23 @@ def start_daemon(skip_start=False):
|
|||
def run_daemon(options):
|
||||
try:
|
||||
from deluge.core.daemon import Daemon
|
||||
daemon = Daemon(listen_interface=options.listen_interface,
|
||||
outgoing_interface=options.outgoing_interface,
|
||||
interface=options.ui_interface,
|
||||
port=options.port,
|
||||
read_only_config_keys=options.read_only_config_keys.split(','))
|
||||
daemon = Daemon(
|
||||
listen_interface=options.listen_interface,
|
||||
outgoing_interface=options.outgoing_interface,
|
||||
interface=options.ui_interface,
|
||||
port=options.port,
|
||||
read_only_config_keys=options.read_only_config_keys.split(','),
|
||||
)
|
||||
if skip_start:
|
||||
return daemon
|
||||
else:
|
||||
daemon.start()
|
||||
except CannotListenError as ex:
|
||||
log.error('Cannot start deluged, listen port in use.\n'
|
||||
' Check for other running daemons or services using this port: %s:%s',
|
||||
ex.interface, ex.port)
|
||||
log.error(
|
||||
'Cannot start deluged, listen port in use.\n'
|
||||
' Check for other running daemons or services using this port: %s:%s',
|
||||
ex.interface, ex.port,
|
||||
)
|
||||
sys.exit(1)
|
||||
except Exception as ex:
|
||||
log.error('Unable to start deluged: %s', ex)
|
||||
|
|
|
@ -194,7 +194,7 @@ class FilterManager(component.Component):
|
|||
tree_keys.remove(cat)
|
||||
|
||||
torrent_keys, plugin_keys = self.torrents.separate_keys(tree_keys, torrent_ids)
|
||||
items = dict((field, self.tree_fields[field]()) for field in tree_keys)
|
||||
items = {field: self.tree_fields[field]() for field in tree_keys}
|
||||
|
||||
for torrent_id in list(torrent_ids):
|
||||
status = self.core.create_torrent_status(torrent_id, torrent_keys, plugin_keys) # status={key:value}
|
||||
|
|
|
@ -33,7 +33,8 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Compon
|
|||
|
||||
# Call the PluginManagerBase constructor
|
||||
deluge.pluginmanagerbase.PluginManagerBase.__init__(
|
||||
self, 'core.conf', 'deluge.plugin.core')
|
||||
self, 'core.conf', 'deluge.plugin.core',
|
||||
)
|
||||
|
||||
def start(self):
|
||||
# Enable plugins that are enabled in the config
|
||||
|
|
|
@ -72,8 +72,10 @@ DEFAULT_PREFS = {
|
|||
'max_upload_speed': -1.0,
|
||||
'max_download_speed': -1.0,
|
||||
'max_upload_slots_global': 4,
|
||||
'max_half_open_connections': (lambda: deluge.common.windows_check() and
|
||||
(lambda: deluge.common.vista_check() and 4 or 8)() or 50)(),
|
||||
'max_half_open_connections': (
|
||||
lambda: deluge.common.windows_check() and
|
||||
(lambda: deluge.common.vista_check() and 4 or 8)() or 50
|
||||
)(),
|
||||
'max_connections_per_second': 20,
|
||||
'ignore_limits_on_local_network': True,
|
||||
'max_connections_per_torrent': -1,
|
||||
|
@ -123,7 +125,7 @@ DEFAULT_PREFS = {
|
|||
'cache_expiry': 60,
|
||||
'auto_manage_prefer_seeds': False,
|
||||
'shared': False,
|
||||
'super_seeding': False
|
||||
'super_seeding': False,
|
||||
}
|
||||
|
||||
|
||||
|
@ -207,19 +209,28 @@ class PreferencesManager(component.Component):
|
|||
interface = str(self.config['listen_interface'].strip())
|
||||
interface = interface if interface else '0.0.0.0'
|
||||
|
||||
log.debug('Listen Interface: %s, Ports: %s with use_sys_port: %s',
|
||||
interface, listen_ports, self.config['listen_use_sys_port'])
|
||||
interfaces = ['%s:%s' % (interface, port) for port in range(listen_ports[0], listen_ports[1]+1)]
|
||||
log.debug(
|
||||
'Listen Interface: %s, Ports: %s with use_sys_port: %s',
|
||||
interface, listen_ports, self.config['listen_use_sys_port'],
|
||||
)
|
||||
interfaces = [
|
||||
'%s:%s' % (interface, port)
|
||||
for port in range(listen_ports[0], listen_ports[1] + 1)
|
||||
]
|
||||
self.core.apply_session_settings(
|
||||
{'listen_system_port_fallback': self.config['listen_use_sys_port'],
|
||||
'listen_interfaces': ''.join(interfaces)})
|
||||
{
|
||||
'listen_system_port_fallback': self.config['listen_use_sys_port'],
|
||||
'listen_interfaces': ''.join(interfaces),
|
||||
},
|
||||
)
|
||||
|
||||
def __set_outgoing_on(self):
|
||||
""" Set the interface address for outgoing BitTorrent connections."""
|
||||
outinterface = self.config['outgoing_interface'].strip()
|
||||
outinterface = outinterface if outinterface else '0.0.0.0'
|
||||
self.core.apply_session_settings(
|
||||
{'outgoing_interfaces': outinterface})
|
||||
{'outgoing_interfaces': outinterface},
|
||||
)
|
||||
|
||||
def _on_set_outgoing_ports(self, key, value):
|
||||
self.__set_outgoing_ports()
|
||||
|
@ -254,7 +265,8 @@ class PreferencesManager(component.Component):
|
|||
'router.bitcomet.com:6881',
|
||||
'dht.transmissionbt.com:6881',
|
||||
'dht.aelitis.com:6881',
|
||||
])
|
||||
],
|
||||
)
|
||||
self.core.apply_session_settings({
|
||||
'dht_bootstrap_nodes': ','.join(dht_bootstraps),
|
||||
'enable_dht': value,
|
||||
|
@ -286,10 +298,13 @@ class PreferencesManager(component.Component):
|
|||
# Convert Deluge enc_level values to libtorrent enc_level values.
|
||||
pe_enc_level = {0: lt.enc_level.plaintext, 1: lt.enc_level.rc4, 2: lt.enc_level.both}
|
||||
self.core.apply_session_settings(
|
||||
{'out_enc_policy': lt.enc_policy(self.config['enc_out_policy']),
|
||||
'in_enc_policy': lt.enc_policy(self.config['enc_in_policy']),
|
||||
'allowed_enc_level': lt.enc_level(pe_enc_level[self.config['enc_level']]),
|
||||
'prefer_rc4': True})
|
||||
{
|
||||
'out_enc_policy': lt.enc_policy(self.config['enc_out_policy']),
|
||||
'in_enc_policy': lt.enc_policy(self.config['enc_in_policy']),
|
||||
'allowed_enc_level': lt.enc_level(pe_enc_level[self.config['enc_level']]),
|
||||
'prefer_rc4': True,
|
||||
},
|
||||
)
|
||||
|
||||
def _on_set_max_connections_global(self, key, value):
|
||||
self.core.apply_session_setting('connections_limit', value)
|
||||
|
@ -376,7 +391,8 @@ class PreferencesManager(component.Component):
|
|||
self.new_release_timer.stop()
|
||||
# Set a timer to check for a new release every 3 days
|
||||
self.new_release_timer = LoopingCall(
|
||||
self._on_set_new_release_check, 'new_release_check', True)
|
||||
self._on_set_new_release_check, 'new_release_check', True,
|
||||
)
|
||||
self.new_release_timer.start(72 * 60 * 60, False)
|
||||
else:
|
||||
if self.new_release_timer and self.new_release_timer.running:
|
||||
|
@ -392,7 +408,7 @@ class PreferencesManager(component.Component):
|
|||
'proxy_peer_connections': value['proxy_peer_connections'],
|
||||
'proxy_tracker_connections': value['proxy_tracker_connections'],
|
||||
'force_proxy': value['force_proxy'],
|
||||
'anonymous_mode': value['anonymous_mode']
|
||||
'anonymous_mode': value['anonymous_mode'],
|
||||
}
|
||||
|
||||
if value['type'] == lt.proxy_type.i2p_proxy:
|
||||
|
|
|
@ -134,8 +134,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
|
||||
for call in request:
|
||||
if len(call) != 4:
|
||||
log.debug('Received invalid rpc request: number of items '
|
||||
'in request is %s', len(call))
|
||||
log.debug(
|
||||
'Received invalid rpc request: number of items '
|
||||
'in request is %s', len(call),
|
||||
)
|
||||
continue
|
||||
# log.debug('RPCRequest: %s', format_request(call))
|
||||
reactor.callLater(0, self.dispatch, *call)
|
||||
|
@ -161,11 +163,14 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
This method is called when a new client connects.
|
||||
"""
|
||||
peer = self.transport.getPeer()
|
||||
log.info('Deluge Client connection made from: %s:%s',
|
||||
peer.host, peer.port)
|
||||
log.info(
|
||||
'Deluge Client connection made from: %s:%s',
|
||||
peer.host, peer.port,
|
||||
)
|
||||
# Set the initial auth level of this session to AUTH_LEVEL_NONE
|
||||
self.factory.authorized_sessions[
|
||||
self.transport.sessionno] = self.AuthLevel(AUTH_LEVEL_NONE, '')
|
||||
self.transport.sessionno
|
||||
] = self.AuthLevel(AUTH_LEVEL_NONE, '')
|
||||
|
||||
def connectionLost(self, reason=connectionDone): # NOQA: N802
|
||||
"""
|
||||
|
@ -219,16 +224,19 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
exc_type.__name__,
|
||||
exc_value._args,
|
||||
exc_value._kwargs,
|
||||
formated_tb
|
||||
formated_tb,
|
||||
))
|
||||
except AttributeError:
|
||||
# This is not a deluge exception (object has no attribute '_args), let's wrap it
|
||||
log.warning('An exception occurred while sending RPC_ERROR to '
|
||||
'client. Wrapping it and resending. Error to '
|
||||
'send(causing exception goes next):\n%s', formated_tb)
|
||||
log.warning(
|
||||
'An exception occurred while sending RPC_ERROR to '
|
||||
'client. Wrapping it and resending. Error to '
|
||||
'send(causing exception goes next):\n%s', formated_tb,
|
||||
)
|
||||
try:
|
||||
raise WrappedException(
|
||||
str(exc_value), exc_type.__name__, formated_tb)
|
||||
str(exc_value), exc_type.__name__, formated_tb,
|
||||
)
|
||||
except WrappedException:
|
||||
send_error()
|
||||
except Exception as ex:
|
||||
|
@ -249,7 +257,8 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
ret = component.get('AuthManager').authorize(*args, **kwargs)
|
||||
if ret:
|
||||
self.factory.authorized_sessions[
|
||||
self.transport.sessionno] = self.AuthLevel(ret, args[0])
|
||||
self.transport.sessionno
|
||||
] = self.AuthLevel(ret, args[0])
|
||||
self.factory.session_protocols[self.transport.sessionno] = self
|
||||
except Exception as ex:
|
||||
send_error()
|
||||
|
@ -294,8 +303,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
auth_level = self.factory.authorized_sessions[self.transport.sessionno].auth_level
|
||||
if auth_level < method_auth_requirement:
|
||||
# This session is not allowed to call this method
|
||||
log.debug('Session %s is attempting an unauthorized method call!',
|
||||
self.transport.sessionno)
|
||||
log.debug(
|
||||
'Session %s is attempting an unauthorized method call!',
|
||||
self.transport.sessionno,
|
||||
)
|
||||
raise NotAuthorizedError(auth_level, method_auth_requirement)
|
||||
# Set the session_id in the factory so that methods can know
|
||||
# which session is calling it.
|
||||
|
@ -514,7 +525,7 @@ class RPCServer(component.Component):
|
|||
log.debug('Emit Event: %s %s', event.name, event.args)
|
||||
# This session is interested so send a RPC_EVENT
|
||||
self.factory.session_protocols[session_id].sendData(
|
||||
(RPC_EVENT, event.name, event.args)
|
||||
(RPC_EVENT, event.name, event.args),
|
||||
)
|
||||
|
||||
def emit_event_for_session_id(self, session_id, event):
|
||||
|
@ -530,14 +541,18 @@ class RPCServer(component.Component):
|
|||
log.debug('Session ID %s is not valid. Not sending event "%s".', session_id, event.name)
|
||||
return
|
||||
if session_id not in self.factory.interested_events:
|
||||
log.debug('Session ID %s is not interested in any events. Not sending event "%s".',
|
||||
session_id, event.name)
|
||||
log.debug(
|
||||
'Session ID %s is not interested in any events. Not sending event "%s".',
|
||||
session_id, event.name,
|
||||
)
|
||||
return
|
||||
if event.name not in self.factory.interested_events[session_id]:
|
||||
log.debug('Session ID %s is not interested in event "%s". Not sending it.', session_id, event.name)
|
||||
return
|
||||
log.debug('Sending event "%s" with args "%s" to session id "%s".',
|
||||
event.name, event.args, session_id)
|
||||
log.debug(
|
||||
'Sending event "%s" with args "%s" to session id "%s".',
|
||||
event.name, event.args, session_id,
|
||||
)
|
||||
self.factory.session_protocols[session_id].sendData((RPC_EVENT, event.name, event.args))
|
||||
|
||||
def stop(self):
|
||||
|
|
|
@ -52,7 +52,7 @@ LT_TORRENT_STATE_MAP = {
|
|||
'finished': 'Seeding',
|
||||
'seeding': 'Seeding',
|
||||
'allocating': 'Allocating',
|
||||
'checking_resume_data': 'Checking'
|
||||
'checking_resume_data': 'Checking',
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,7 +114,7 @@ def convert_lt_files(files):
|
|||
'index': index,
|
||||
'path': file_path.replace('\\', '/'),
|
||||
'size': _file.size,
|
||||
'offset': _file.offset
|
||||
'offset': _file.offset,
|
||||
})
|
||||
|
||||
return filelist
|
||||
|
@ -172,7 +172,7 @@ class TorrentOptions(dict):
|
|||
'shared': 'shared',
|
||||
'stop_at_ratio': 'stop_seed_at_ratio',
|
||||
'stop_ratio': 'stop_seed_ratio',
|
||||
'super_seeding': 'super_seeding'
|
||||
'super_seeding': 'super_seeding',
|
||||
}
|
||||
for opt_k, conf_k in options_conf_map.items():
|
||||
self[opt_k] = config[conf_k]
|
||||
|
@ -639,8 +639,10 @@ class Torrent(object):
|
|||
component.get('EventManager').emit(TorrentStateChangedEvent(self.torrent_id, self.state))
|
||||
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
log.debug('State from lt was: %s | Session is paused: %s\nTorrent state set from "%s" to "%s" (%s)',
|
||||
'error' if status_error else status.state, session_paused, old_state, self.state, self.torrent_id)
|
||||
log.debug(
|
||||
'State from lt was: %s | Session is paused: %s\nTorrent state set from "%s" to "%s" (%s)',
|
||||
'error' if status_error else status.state, session_paused, old_state, self.state, self.torrent_id,
|
||||
)
|
||||
if self.forced_error:
|
||||
log.debug('Torrent Error state message: %s', self.forced_error.error_message)
|
||||
|
||||
|
@ -699,8 +701,10 @@ class Torrent(object):
|
|||
eta = 0
|
||||
if self.is_finished and self.options['stop_at_ratio'] and status.upload_payload_rate:
|
||||
# We're a seed, so calculate the time to the 'stop_share_ratio'
|
||||
eta = ((status.all_time_download * self.options['stop_ratio']) -
|
||||
status.all_time_upload) // status.upload_payload_rate
|
||||
eta = (
|
||||
(status.all_time_download * self.options['stop_ratio']) -
|
||||
status.all_time_upload
|
||||
) // status.upload_payload_rate
|
||||
elif status.download_payload_rate:
|
||||
left = status.total_wanted - status.total_wanted_done
|
||||
if left > 0:
|
||||
|
@ -825,8 +829,10 @@ class Torrent(object):
|
|||
"""
|
||||
if not self.has_metadata:
|
||||
return []
|
||||
return [progress / _file.size if _file.size else 0.0 for progress, _file in
|
||||
zip(self.handle.file_progress(), self.torrent_info.files())]
|
||||
return [
|
||||
progress / _file.size if _file.size else 0.0 for progress, _file in
|
||||
zip(self.handle.file_progress(), self.torrent_info.files())
|
||||
]
|
||||
|
||||
def get_tracker_host(self):
|
||||
"""Get the hostname of the currently connected tracker.
|
||||
|
@ -1019,7 +1025,8 @@ class Torrent(object):
|
|||
'save_path': lambda: self.options['download_location'], # Deprecated: Use download_location
|
||||
'download_location': lambda: self.options['download_location'],
|
||||
'seeds_peers_ratio': lambda: -1.0 if self.status.num_incomplete == 0 else ( # Use -1.0 to signify infinity
|
||||
self.status.num_complete / self.status.num_incomplete),
|
||||
self.status.num_complete / self.status.num_incomplete
|
||||
),
|
||||
'seed_rank': lambda: self.status.seed_rank,
|
||||
'state': lambda: self.state,
|
||||
'stop_at_ratio': lambda: self.options['stop_at_ratio'],
|
||||
|
@ -1061,7 +1068,7 @@ class Torrent(object):
|
|||
'super_seeding': lambda: self.status.super_seeding,
|
||||
'time_since_download': lambda: self.status.time_since_download,
|
||||
'time_since_upload': lambda: self.status.time_since_upload,
|
||||
'time_since_transfer': self.get_time_since_transfer
|
||||
'time_since_transfer': self.get_time_since_transfer,
|
||||
}
|
||||
|
||||
def pause(self):
|
||||
|
@ -1147,9 +1154,11 @@ class Torrent(object):
|
|||
try:
|
||||
os.makedirs(dest)
|
||||
except OSError as ex:
|
||||
log.error('Could not move storage for torrent %s since %s does '
|
||||
'not exist and could not create the directory: %s',
|
||||
self.torrent_id, dest, ex)
|
||||
log.error(
|
||||
'Could not move storage for torrent %s since %s does '
|
||||
'not exist and could not create the directory: %s',
|
||||
self.torrent_id, dest, ex,
|
||||
)
|
||||
return False
|
||||
|
||||
try:
|
||||
|
@ -1183,7 +1192,8 @@ class Torrent(object):
|
|||
# Don't generate fastresume data if torrent is in a Deluge Error state.
|
||||
if self.forced_error:
|
||||
component.get('TorrentManager').waiting_on_resume_data[self.torrent_id].errback(
|
||||
UserWarning('Skipped creating resume_data while in Error state'))
|
||||
UserWarning('Skipped creating resume_data while in Error state'),
|
||||
)
|
||||
else:
|
||||
self.handle.save_resume_data(flags)
|
||||
|
||||
|
@ -1315,7 +1325,7 @@ class Torrent(object):
|
|||
if _file['path'].startswith(folder):
|
||||
# Keep track of filerenames we're waiting on
|
||||
wait_on_folder[_file['index']] = Deferred().addBoth(
|
||||
on_file_rename_complete, wait_on_folder, _file['index']
|
||||
on_file_rename_complete, wait_on_folder, _file['index'],
|
||||
)
|
||||
new_path = _file['path'].replace(folder, new_folder, 1)
|
||||
try:
|
||||
|
|
|
@ -38,7 +38,8 @@ LT_DEFAULT_ADD_TORRENT_FLAGS = (
|
|||
lt.add_torrent_params_flags_t.flag_paused |
|
||||
lt.add_torrent_params_flags_t.flag_auto_managed |
|
||||
lt.add_torrent_params_flags_t.flag_update_subscribe |
|
||||
lt.add_torrent_params_flags_t.flag_apply_ip_filter)
|
||||
lt.add_torrent_params_flags_t.flag_apply_ip_filter
|
||||
)
|
||||
|
||||
|
||||
class TorrentState: # pylint: disable=old-style-class
|
||||
|
@ -48,33 +49,35 @@ class TorrentState: # pylint: disable=old-style-class
|
|||
This must be old style class to avoid breaking torrent.state file.
|
||||
|
||||
"""
|
||||
def __init__(self,
|
||||
torrent_id=None,
|
||||
filename=None,
|
||||
trackers=None,
|
||||
storage_mode='sparse',
|
||||
paused=False,
|
||||
save_path=None,
|
||||
max_connections=-1,
|
||||
max_upload_slots=-1,
|
||||
max_upload_speed=-1.0,
|
||||
max_download_speed=-1.0,
|
||||
prioritize_first_last=False,
|
||||
sequential_download=False,
|
||||
file_priorities=None,
|
||||
queue=None,
|
||||
auto_managed=True,
|
||||
is_finished=False,
|
||||
stop_ratio=2.00,
|
||||
stop_at_ratio=False,
|
||||
remove_at_ratio=False,
|
||||
move_completed=False,
|
||||
move_completed_path=None,
|
||||
magnet=None,
|
||||
owner=None,
|
||||
shared=False,
|
||||
super_seeding=False,
|
||||
name=None):
|
||||
def __init__(
|
||||
self,
|
||||
torrent_id=None,
|
||||
filename=None,
|
||||
trackers=None,
|
||||
storage_mode='sparse',
|
||||
paused=False,
|
||||
save_path=None,
|
||||
max_connections=-1,
|
||||
max_upload_slots=-1,
|
||||
max_upload_speed=-1.0,
|
||||
max_download_speed=-1.0,
|
||||
prioritize_first_last=False,
|
||||
sequential_download=False,
|
||||
file_priorities=None,
|
||||
queue=None,
|
||||
auto_managed=True,
|
||||
is_finished=False,
|
||||
stop_ratio=2.00,
|
||||
stop_at_ratio=False,
|
||||
remove_at_ratio=False,
|
||||
move_completed=False,
|
||||
move_completed_path=None,
|
||||
magnet=None,
|
||||
owner=None,
|
||||
shared=False,
|
||||
super_seeding=False,
|
||||
name=None,
|
||||
):
|
||||
# Build the class atrribute list from args
|
||||
for key, value in locals().items():
|
||||
if key == 'self':
|
||||
|
@ -113,8 +116,10 @@ class TorrentManager(component.Component):
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, 'TorrentManager', interval=5,
|
||||
depend=['CorePluginManager', 'AlertManager'])
|
||||
component.Component.__init__(
|
||||
self, 'TorrentManager', interval=5,
|
||||
depend=['CorePluginManager', 'AlertManager'],
|
||||
)
|
||||
log.debug('TorrentManager init...')
|
||||
# Set the libtorrent session
|
||||
self.session = component.get('Core').session
|
||||
|
@ -154,8 +159,10 @@ class TorrentManager(component.Component):
|
|||
self.prev_saved_state = None
|
||||
|
||||
# Register set functions
|
||||
set_config_keys = ['max_connections_per_torrent', 'max_upload_slots_per_torrent',
|
||||
'max_upload_speed_per_torrent', 'max_download_speed_per_torrent']
|
||||
set_config_keys = [
|
||||
'max_connections_per_torrent', 'max_upload_slots_per_torrent',
|
||||
'max_upload_speed_per_torrent', 'max_download_speed_per_torrent',
|
||||
]
|
||||
|
||||
for config_key in set_config_keys:
|
||||
on_set_func = getattr(self, ''.join(['on_set_', config_key]))
|
||||
|
@ -170,7 +177,7 @@ class TorrentManager(component.Component):
|
|||
'file_renamed_alert', 'file_error_alert', 'file_completed_alert',
|
||||
'storage_moved_alert', 'storage_moved_failed_alert', 'state_update_alert',
|
||||
'state_changed_alert', 'save_resume_data_alert', 'save_resume_data_failed_alert',
|
||||
'fastresume_rejected_alert'
|
||||
'fastresume_rejected_alert',
|
||||
]
|
||||
|
||||
for alert_handle in alert_handles:
|
||||
|
@ -230,7 +237,8 @@ class TorrentManager(component.Component):
|
|||
for torrent_id, torrent in self.torrents.items():
|
||||
# XXX: Should the state check be those that _can_ be stopped at ratio
|
||||
if torrent.options['stop_at_ratio'] and torrent.state not in (
|
||||
'Checking', 'Allocating', 'Paused', 'Queued'):
|
||||
'Checking', 'Allocating', 'Paused', 'Queued',
|
||||
):
|
||||
# If the global setting is set, but the per-torrent isn't...
|
||||
# Just skip to the next torrent.
|
||||
# This is so that a user can turn-off the stop at ratio option on a per-torrent basis
|
||||
|
@ -308,7 +316,7 @@ class TorrentManager(component.Component):
|
|||
return options
|
||||
|
||||
def _build_torrent_params(
|
||||
self, torrent_info=None, magnet=None, options=None, resume_data=None
|
||||
self, torrent_info=None, magnet=None, options=None, resume_data=None,
|
||||
):
|
||||
"""Create the add_torrent_params dict for adding torrent to libtorrent."""
|
||||
add_torrent_params = {}
|
||||
|
@ -361,10 +369,14 @@ class TorrentManager(component.Component):
|
|||
add_torrent_params['resume_data'] = resume_data
|
||||
|
||||
# Set flags: enable duplicate_is_error & override_resume_data, disable auto_managed.
|
||||
add_torrent_params['flags'] = ((LT_DEFAULT_ADD_TORRENT_FLAGS |
|
||||
lt.add_torrent_params_flags_t.flag_duplicate_is_error |
|
||||
lt.add_torrent_params_flags_t.flag_override_resume_data) ^
|
||||
lt.add_torrent_params_flags_t.flag_auto_managed)
|
||||
add_torrent_params['flags'] = (
|
||||
(
|
||||
LT_DEFAULT_ADD_TORRENT_FLAGS |
|
||||
lt.add_torrent_params_flags_t.flag_duplicate_is_error |
|
||||
lt.add_torrent_params_flags_t.flag_override_resume_data
|
||||
) ^
|
||||
lt.add_torrent_params_flags_t.flag_auto_managed
|
||||
)
|
||||
if options['seed_mode']:
|
||||
add_torrent_params['flags'] |= lt.add_torrent_params_flags_t.flag_seed_mode
|
||||
|
||||
|
@ -411,7 +423,8 @@ class TorrentManager(component.Component):
|
|||
|
||||
options = self._build_torrent_options(options)
|
||||
__, add_torrent_params = self._build_torrent_params(
|
||||
torrent_info, magnet, options, resume_data)
|
||||
torrent_info, magnet, options, resume_data,
|
||||
)
|
||||
|
||||
# We need to pause the AlertManager momentarily to prevent alerts
|
||||
# for this torrent being generated before a Torrent object is created.
|
||||
|
@ -426,7 +439,8 @@ class TorrentManager(component.Component):
|
|||
raise AddTorrentError('Unable to add torrent to session: %s' % ex)
|
||||
|
||||
torrent = self._add_torrent_obj(
|
||||
handle, options, state, filename, magnet, resume_data, filedump, save_state)
|
||||
handle, options, state, filename, magnet, resume_data, filedump, save_state,
|
||||
)
|
||||
return torrent.torrent_id
|
||||
|
||||
def add_async(
|
||||
|
@ -470,7 +484,8 @@ class TorrentManager(component.Component):
|
|||
|
||||
options = self._build_torrent_options(options)
|
||||
torrent_id, add_torrent_params = self._build_torrent_params(
|
||||
torrent_info, magnet, options, resume_data)
|
||||
torrent_info, magnet, options, resume_data,
|
||||
)
|
||||
|
||||
d = Deferred()
|
||||
self.torrents_loading[torrent_id] = (d, options, state, filename, magnet, resume_data, filedump, save_state)
|
||||
|
@ -509,10 +524,12 @@ class TorrentManager(component.Component):
|
|||
log.debug('Torrent added: %s', str(handle.info_hash()))
|
||||
if log.isEnabledFor(logging.INFO):
|
||||
name_and_owner = torrent.get_status(['name', 'owner'])
|
||||
log.info('Torrent %s from user "%s" %s',
|
||||
name_and_owner['name'],
|
||||
name_and_owner['owner'],
|
||||
from_state and 'loaded' or 'added')
|
||||
log.info(
|
||||
'Torrent %s from user "%s" %s',
|
||||
name_and_owner['name'],
|
||||
name_and_owner['owner'],
|
||||
from_state and 'loaded' or 'added',
|
||||
)
|
||||
|
||||
# Write the .torrent file to the state directory.
|
||||
if filedump:
|
||||
|
@ -525,10 +542,11 @@ class TorrentManager(component.Component):
|
|||
return torrent
|
||||
|
||||
def add_async_callback(
|
||||
self, handle, d, options, state, filename, magnet, resume_data, filedump, save_state
|
||||
self, handle, d, options, state, filename, magnet, resume_data, filedump, save_state,
|
||||
):
|
||||
torrent = self._add_torrent_obj(
|
||||
handle, options, state, filename, magnet, resume_data, filedump, save_state)
|
||||
handle, options, state, filename, magnet, resume_data, filedump, save_state,
|
||||
)
|
||||
|
||||
d.callback(torrent.torrent_id)
|
||||
|
||||
|
@ -670,7 +688,8 @@ class TorrentManager(component.Component):
|
|||
|
||||
magnet = t_state.magnet
|
||||
torrent_info = self.get_torrent_info_from_file(
|
||||
os.path.join(self.state_dir, t_state.torrent_id + '.torrent'))
|
||||
os.path.join(self.state_dir, t_state.torrent_id + '.torrent'),
|
||||
)
|
||||
if torrent_info:
|
||||
magnet = None
|
||||
|
||||
|
@ -740,7 +759,7 @@ class TorrentManager(component.Component):
|
|||
torrent.options['owner'],
|
||||
torrent.options['shared'],
|
||||
torrent.options['super_seeding'],
|
||||
torrent.options['name']
|
||||
torrent.options['name'],
|
||||
)
|
||||
state.torrents.append(torrent_state)
|
||||
return state
|
||||
|
@ -1058,9 +1077,11 @@ class TorrentManager(component.Component):
|
|||
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
log.debug('Finished %s ', torrent_id)
|
||||
log.debug('Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s',
|
||||
torrent.is_finished, total_download, torrent.options['move_completed'],
|
||||
torrent.options['move_completed_path'])
|
||||
log.debug(
|
||||
'Torrent settings: is_finished: %s, total_download: %s, move_completed: %s, move_path: %s',
|
||||
torrent.is_finished, total_download, torrent.options['move_completed'],
|
||||
torrent.options['move_completed_path'],
|
||||
)
|
||||
|
||||
torrent.update_state()
|
||||
if not torrent.is_finished and total_download:
|
||||
|
|
|
@ -129,8 +129,10 @@ def deprecated(func):
|
|||
@wraps(func)
|
||||
def depr_func(*args, **kwargs):
|
||||
warnings.simplefilter('always', DeprecationWarning) # Turn off filter
|
||||
warnings.warn('Call to deprecated function {}.'.format(func.__name__),
|
||||
category=DeprecationWarning, stacklevel=2)
|
||||
warnings.warn(
|
||||
'Call to deprecated function {}.'.format(func.__name__),
|
||||
category=DeprecationWarning, stacklevel=2,
|
||||
)
|
||||
warnings.simplefilter('default', DeprecationWarning) # Reset filter
|
||||
return func(*args, **kwargs)
|
||||
|
||||
|
|
|
@ -63,17 +63,22 @@ class IncompatibleClient(_ClientSideRecreateError):
|
|||
|
||||
def __init__(self, daemon_version):
|
||||
self.daemon_version = daemon_version
|
||||
msg = 'Your deluge client is not compatible with the daemon. '\
|
||||
'Please upgrade your client to %(daemon_version)s' % \
|
||||
dict(daemon_version=self.daemon_version)
|
||||
msg = (
|
||||
'Your deluge client is not compatible with the daemon. '
|
||||
'Please upgrade your client to %(daemon_version)s'
|
||||
) % {'daemon_version': self.daemon_version}
|
||||
super(IncompatibleClient, self).__init__(message=msg)
|
||||
|
||||
|
||||
class NotAuthorizedError(_ClientSideRecreateError):
|
||||
|
||||
def __init__(self, current_level, required_level):
|
||||
msg = 'Auth level too low: %(current_level)s < %(required_level)s' % \
|
||||
dict(current_level=current_level, required_level=required_level)
|
||||
msg = (
|
||||
'Auth level too low: %(current_level)s < %(required_level)s'
|
||||
) % {
|
||||
'current_level': current_level,
|
||||
'required_level': required_level,
|
||||
}
|
||||
super(NotAuthorizedError, self).__init__(message=msg)
|
||||
self.current_level = current_level
|
||||
self.required_level = required_level
|
||||
|
|
|
@ -34,8 +34,10 @@ class HTTPDownloader(client.HTTPDownloader):
|
|||
"""
|
||||
Factory class for downloading files and keeping track of progress.
|
||||
"""
|
||||
def __init__(self, url, filename, part_callback=None, headers=None,
|
||||
force_filename=False, allow_compression=True):
|
||||
def __init__(
|
||||
self, url, filename, part_callback=None, headers=None,
|
||||
force_filename=False, allow_compression=True,
|
||||
):
|
||||
"""
|
||||
:param url: the url to download from
|
||||
:type url: string
|
||||
|
@ -227,8 +229,10 @@ def _download_file(url, filename, callback=None, headers=None, force_filename=Fa
|
|||
return factory.deferred
|
||||
|
||||
|
||||
def download_file(url, filename, callback=None, headers=None, force_filename=False,
|
||||
allow_compression=True, handle_redirects=True):
|
||||
def download_file(
|
||||
url, filename, callback=None, headers=None, force_filename=False,
|
||||
allow_compression=True, handle_redirects=True,
|
||||
):
|
||||
"""
|
||||
Downloads a file from a specific URL and returns a Deferred. A callback
|
||||
function can be specified to be called as parts are received.
|
||||
|
@ -259,18 +263,24 @@ def download_file(url, filename, callback=None, headers=None, force_filename=Fal
|
|||
def on_download_fail(failure):
|
||||
if failure.check(PageRedirect) and handle_redirects:
|
||||
new_url = urljoin(url, failure.getErrorMessage().split(' to ')[1])
|
||||
result = _download_file(new_url, filename, callback=callback, headers=headers,
|
||||
force_filename=force_filename,
|
||||
allow_compression=allow_compression)
|
||||
result = _download_file(
|
||||
new_url, filename, callback=callback, headers=headers,
|
||||
force_filename=force_filename,
|
||||
allow_compression=allow_compression,
|
||||
)
|
||||
result.addCallbacks(on_download_success, on_download_fail)
|
||||
else:
|
||||
# Log the failure and pass to the caller
|
||||
log.warning('Error occurred downloading file from "%s": %s',
|
||||
url, failure.getErrorMessage())
|
||||
log.warning(
|
||||
'Error occurred downloading file from "%s": %s',
|
||||
url, failure.getErrorMessage(),
|
||||
)
|
||||
result = failure
|
||||
return result
|
||||
|
||||
d = _download_file(url, filename, callback=callback, headers=headers,
|
||||
force_filename=force_filename, allow_compression=allow_compression)
|
||||
d = _download_file(
|
||||
url, filename, callback=callback, headers=headers,
|
||||
force_filename=force_filename, allow_compression=allow_compression,
|
||||
)
|
||||
d.addCallbacks(on_download_success, on_download_fail)
|
||||
return d
|
||||
|
|
|
@ -45,7 +45,7 @@ class Logging(LoggingLoggerClass):
|
|||
for handler in logging.getLogger().handlers:
|
||||
handler.setFormatter(logging.Formatter(
|
||||
DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH,
|
||||
datefmt='%H:%M:%S'
|
||||
datefmt='%H:%M:%S',
|
||||
))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -88,8 +88,10 @@ class Logging(LoggingLoggerClass):
|
|||
while hasattr(f, 'f_code'):
|
||||
co = f.f_code
|
||||
filename = os.path.normcase(co.co_filename)
|
||||
if filename in (__file__.replace('.pyc', '.py'),
|
||||
defer.__file__.replace('.pyc', '.py')):
|
||||
if filename in (
|
||||
__file__.replace('.pyc', '.py'),
|
||||
defer.__file__.replace('.pyc', '.py'),
|
||||
):
|
||||
f = f.f_back
|
||||
continue
|
||||
rv = (filename, f.f_lineno, co.co_name)
|
||||
|
@ -105,12 +107,14 @@ levels = {
|
|||
'none': logging.CRITICAL,
|
||||
'debug': logging.DEBUG,
|
||||
'trace': 5,
|
||||
'garbage': 1
|
||||
'garbage': 1,
|
||||
}
|
||||
|
||||
|
||||
def setup_logger(level='error', filename=None, filemode='w', logrotate=None,
|
||||
output_stream=sys.stdout, twisted_observer=True):
|
||||
def setup_logger(
|
||||
level='error', filename=None, filemode='w', logrotate=None,
|
||||
output_stream=sys.stdout, twisted_observer=True,
|
||||
):
|
||||
"""
|
||||
Sets up the basic logger and if `:param:filename` is set, then it will log
|
||||
to that file instead of stdout.
|
||||
|
@ -137,7 +141,7 @@ def setup_logger(level='error', filename=None, filemode='w', logrotate=None,
|
|||
if filename and logrotate:
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
filename, maxBytes=logrotate,
|
||||
backupCount=5, encoding='utf-8'
|
||||
backupCount=5, encoding='utf-8',
|
||||
)
|
||||
elif filename and filemode == 'w':
|
||||
handler_cls = logging.FileHandler
|
||||
|
@ -151,7 +155,7 @@ def setup_logger(level='error', filename=None, filemode='w', logrotate=None,
|
|||
|
||||
formatter = logging.Formatter(
|
||||
DEFAULT_LOGGING_FORMAT % MAX_LOGGER_NAME_LENGTH,
|
||||
datefmt='%H:%M:%S'
|
||||
datefmt='%H:%M:%S',
|
||||
)
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
|
@ -214,8 +218,10 @@ def tweak_logging_levels():
|
|||
if not os.path.isfile(logging_config_file):
|
||||
return
|
||||
log = logging.getLogger(__name__)
|
||||
log.warn('logging.conf found! tweaking logging levels from %s',
|
||||
logging_config_file)
|
||||
log.warn(
|
||||
'logging.conf found! tweaking logging levels from %s',
|
||||
logging_config_file,
|
||||
)
|
||||
with open(logging_config_file, 'r') as _file:
|
||||
for line in _file:
|
||||
if line.strip().startswith('#'):
|
||||
|
@ -249,9 +255,11 @@ def get_plugin_logger(logger_name):
|
|||
caller_module = inspect.getmodule(module_stack[0])
|
||||
# In some weird cases caller_module might be None, try to continue
|
||||
caller_module_name = getattr(caller_module, '__name__', '')
|
||||
warnings.warn_explicit(DEPRECATION_WARNING, DeprecationWarning,
|
||||
module_stack[1], module_stack[2],
|
||||
caller_module_name)
|
||||
warnings.warn_explicit(
|
||||
DEPRECATION_WARNING, DeprecationWarning,
|
||||
module_stack[1], module_stack[2],
|
||||
caller_module_name,
|
||||
)
|
||||
|
||||
if 'deluge.plugins.' in logger_name:
|
||||
return logging.getLogger(logger_name)
|
||||
|
@ -291,16 +299,20 @@ class _BackwardsCompatibleLOG(object):
|
|||
caller_module = inspect.getmodule(module_stack[0])
|
||||
# In some weird cases caller_module might be None, try to continue
|
||||
caller_module_name = getattr(caller_module, '__name__', '')
|
||||
warnings.warn_explicit(DEPRECATION_WARNING, DeprecationWarning,
|
||||
module_stack[1], module_stack[2],
|
||||
caller_module_name)
|
||||
warnings.warn_explicit(
|
||||
DEPRECATION_WARNING, DeprecationWarning,
|
||||
module_stack[1], module_stack[2],
|
||||
caller_module_name,
|
||||
)
|
||||
if caller_module:
|
||||
for member in stack:
|
||||
module = inspect.getmodule(member[0])
|
||||
if not module:
|
||||
continue
|
||||
if module.__name__ in ('deluge.plugins.pluginbase',
|
||||
'deluge.plugins.init'):
|
||||
if module.__name__ in (
|
||||
'deluge.plugins.pluginbase',
|
||||
'deluge.plugins.init',
|
||||
):
|
||||
logger_name += '.plugin.%s' % caller_module_name
|
||||
# Monkey Patch The Plugin Module
|
||||
caller_module.log = logging.getLogger(logger_name)
|
||||
|
@ -308,7 +320,7 @@ class _BackwardsCompatibleLOG(object):
|
|||
else:
|
||||
logging.getLogger(logger_name).warning(
|
||||
"Unable to monkey-patch the calling module's `log` attribute! "
|
||||
'You should really update and rebuild your plugins...'
|
||||
'You should really update and rebuild your plugins...',
|
||||
)
|
||||
return getattr(logging.getLogger(logger_name), name)
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class TorrentMetadata(object):
|
|||
raise InvalidPath('Need to set a data_path!')
|
||||
|
||||
torrent = {
|
||||
'info': {}
|
||||
'info': {},
|
||||
}
|
||||
|
||||
if self.comment:
|
||||
|
|
|
@ -50,13 +50,15 @@ class RemoteFileProgress(object):
|
|||
|
||||
def __call__(self, piece_count, num_pieces):
|
||||
component.get('RPCServer').emit_event_for_session_id(
|
||||
self.session_id, CreateTorrentProgressEvent(piece_count, num_pieces)
|
||||
self.session_id, CreateTorrentProgressEvent(piece_count, num_pieces),
|
||||
)
|
||||
|
||||
|
||||
def make_meta_file(path, url, piece_length, progress=None, title=None, comment=None,
|
||||
safe=None, content_type=None, target=None, webseeds=None, name=None,
|
||||
private=False, created_by=None, trackers=None):
|
||||
def make_meta_file(
|
||||
path, url, piece_length, progress=None, title=None, comment=None,
|
||||
safe=None, content_type=None, target=None, webseeds=None, name=None,
|
||||
private=False, created_by=None, trackers=None,
|
||||
):
|
||||
data = {'creation date': int(gmtime())}
|
||||
if url:
|
||||
data['announce'] = url.strip()
|
||||
|
@ -147,8 +149,10 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private
|
|||
size = os.path.getsize(f)
|
||||
p2 = [n.encode('utf8') for n in p]
|
||||
if content_type:
|
||||
fs.append({'length': size, 'path': p2,
|
||||
'content_type': content_type}) # HEREDAVE. bad for batch!
|
||||
fs.append({
|
||||
'length': size, 'path': p2,
|
||||
'content_type': content_type,
|
||||
}) # HEREDAVE. bad for batch!
|
||||
else:
|
||||
fs.append({'length': size, 'path': p2})
|
||||
with open(f, 'rb') as file_:
|
||||
|
@ -173,11 +177,13 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private
|
|||
if not name:
|
||||
name = os.path.split(path)[1]
|
||||
|
||||
return {'pieces': b''.join(pieces),
|
||||
'piece length': piece_length,
|
||||
'files': fs,
|
||||
'name': name.encode('utf8'),
|
||||
'private': private}
|
||||
return {
|
||||
'pieces': b''.join(pieces),
|
||||
'piece length': piece_length,
|
||||
'files': fs,
|
||||
'name': name.encode('utf8'),
|
||||
'private': private,
|
||||
}
|
||||
else:
|
||||
size = os.path.getsize(path)
|
||||
if size >= piece_length:
|
||||
|
@ -198,15 +204,19 @@ def makeinfo(path, piece_length, progress, name=None, content_type=None, private
|
|||
progress(piece_count, num_pieces)
|
||||
name = os.path.split(path)[1].encode('utf8')
|
||||
if content_type is not None:
|
||||
return {'pieces': b''.join(pieces),
|
||||
'piece length': piece_length, 'length': size,
|
||||
'name': name,
|
||||
'content_type': content_type,
|
||||
'private': private}
|
||||
return {'pieces': b''.join(pieces),
|
||||
return {
|
||||
'pieces': b''.join(pieces),
|
||||
'piece length': piece_length, 'length': size,
|
||||
'name': name,
|
||||
'private': private}
|
||||
'content_type': content_type,
|
||||
'private': private,
|
||||
}
|
||||
return {
|
||||
'pieces': b''.join(pieces),
|
||||
'piece length': piece_length, 'length': size,
|
||||
'name': name,
|
||||
'private': private,
|
||||
}
|
||||
|
||||
|
||||
def subfiles(d):
|
||||
|
|
|
@ -108,10 +108,12 @@ class PluginManagerBase(object):
|
|||
|
||||
self.available_plugins = []
|
||||
for name in self.pkg_env:
|
||||
log.debug('Found plugin: %s %s at %s',
|
||||
self.pkg_env[name][0].project_name,
|
||||
self.pkg_env[name][0].version,
|
||||
self.pkg_env[name][0].location)
|
||||
log.debug(
|
||||
'Found plugin: %s %s at %s',
|
||||
self.pkg_env[name][0].project_name,
|
||||
self.pkg_env[name][0].version,
|
||||
self.pkg_env[name][0].location,
|
||||
)
|
||||
self.available_plugins.append(self.pkg_env[name][0].project_name)
|
||||
|
||||
def enable_plugin(self, plugin_name):
|
||||
|
@ -162,7 +164,7 @@ class PluginManagerBase(object):
|
|||
warnings.warn_explicit(
|
||||
DEPRECATION_WARNING % name,
|
||||
DeprecationWarning,
|
||||
instance.__module__, 0
|
||||
instance.__module__, 0,
|
||||
)
|
||||
if self._component_state == 'Started':
|
||||
def on_enabled(result, instance):
|
||||
|
@ -179,8 +181,10 @@ class PluginManagerBase(object):
|
|||
return True
|
||||
|
||||
def on_started_error(result, instance):
|
||||
log.error('Failed to start plugin: %s\n%s', plugin_name,
|
||||
result.getTraceback(elideFrameworkCode=1, detail='brief'))
|
||||
log.error(
|
||||
'Failed to start plugin: %s\n%s', plugin_name,
|
||||
result.getTraceback(elideFrameworkCode=1, detail='brief'),
|
||||
)
|
||||
self.plugins[plugin_name.replace('-', ' ')] = instance
|
||||
self.disable_plugin(plugin_name)
|
||||
return False
|
||||
|
|
|
@ -37,7 +37,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
DEFAULT_PREFS = {
|
||||
'watchdirs': {},
|
||||
'next_id': 1
|
||||
'next_id': 1,
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,7 +64,7 @@ OPTIONS_AVAILABLE = { # option: builtin
|
|||
'add_paused': True,
|
||||
'queue_to_top': False,
|
||||
'owner': True,
|
||||
'seed_mode': True
|
||||
'seed_mode': True,
|
||||
}
|
||||
|
||||
MAX_NUM_ATTEMPTS = 10
|
||||
|
@ -91,7 +91,7 @@ class Core(CorePluginBase):
|
|||
self.watchdirs = self.config['watchdirs']
|
||||
|
||||
component.get('EventManager').register_event_handler(
|
||||
'PreTorrentRemovedEvent', self.__on_pre_torrent_removed
|
||||
'PreTorrentRemovedEvent', self.__on_pre_torrent_removed,
|
||||
)
|
||||
|
||||
# Dict of Filename:Attempts
|
||||
|
@ -109,7 +109,7 @@ class Core(CorePluginBase):
|
|||
def disable(self):
|
||||
# disable all running looping calls
|
||||
component.get('EventManager').deregister_event_handler(
|
||||
'PreTorrentRemovedEvent', self.__on_pre_torrent_removed
|
||||
'PreTorrentRemovedEvent', self.__on_pre_torrent_removed,
|
||||
)
|
||||
for loopingcall in self.update_timers.values():
|
||||
loopingcall.stop()
|
||||
|
@ -124,12 +124,12 @@ class Core(CorePluginBase):
|
|||
watchdir_id = str(watchdir_id)
|
||||
options = self._make_unicode(options)
|
||||
check_input(
|
||||
watchdir_id in self.watchdirs, _('Watch folder does not exist.')
|
||||
watchdir_id in self.watchdirs, _('Watch folder does not exist.'),
|
||||
)
|
||||
if 'path' in options:
|
||||
options['abspath'] = os.path.abspath(options['path'])
|
||||
check_input(
|
||||
os.path.isdir(options['abspath']), _('Path does not exist.')
|
||||
os.path.isdir(options['abspath']), _('Path does not exist.'),
|
||||
)
|
||||
for w_id, w in self.watchdirs.items():
|
||||
if options['abspath'] == w['abspath'] and watchdir_id != w_id:
|
||||
|
@ -210,8 +210,10 @@ class Core(CorePluginBase):
|
|||
watchdir = self.watchdirs[watchdir_id]
|
||||
if not watchdir['enabled']:
|
||||
# We shouldn't be updating because this watchdir is not enabled
|
||||
log.debug('Watchdir id %s is not enabled. Disabling it.',
|
||||
watchdir_id)
|
||||
log.debug(
|
||||
'Watchdir id %s is not enabled. Disabling it.',
|
||||
watchdir_id,
|
||||
)
|
||||
self.disable_watchdir(watchdir_id)
|
||||
return
|
||||
|
||||
|
@ -271,8 +273,10 @@ class Core(CorePluginBase):
|
|||
if filename in self.invalid_torrents:
|
||||
self.invalid_torrents[filename] += 1
|
||||
if self.invalid_torrents[filename] >= MAX_NUM_ATTEMPTS:
|
||||
log.warning('Maximum attempts reached while trying to add the '
|
||||
'torrent file with the path %s', filepath)
|
||||
log.warning(
|
||||
'Maximum attempts reached while trying to add the '
|
||||
'torrent file with the path %s', filepath,
|
||||
)
|
||||
os.rename(filepath, filepath + '.invalid')
|
||||
del self.invalid_torrents[filename]
|
||||
else:
|
||||
|
@ -305,8 +309,10 @@ class Core(CorePluginBase):
|
|||
elif watchdir.get('copy_torrent_toggle'):
|
||||
copy_torrent_path = watchdir['copy_torrent']
|
||||
copy_torrent_file = os.path.join(copy_torrent_path, filename)
|
||||
log.debug('Moving added torrent file "%s" to "%s"',
|
||||
os.path.basename(filepath), copy_torrent_path)
|
||||
log.debug(
|
||||
'Moving added torrent file "%s" to "%s"',
|
||||
os.path.basename(filepath), copy_torrent_path,
|
||||
)
|
||||
shutil.move(filepath, copy_torrent_file)
|
||||
else:
|
||||
os.remove(filepath)
|
||||
|
@ -322,7 +328,8 @@ class Core(CorePluginBase):
|
|||
d = component.get('Core').add_torrent_magnet(filedump.strip(), options)
|
||||
else:
|
||||
d = component.get('Core').add_torrent_file_async(
|
||||
filename, base64.encodestring(filedump), options)
|
||||
filename, base64.encodestring(filedump), options,
|
||||
)
|
||||
d.addCallback(on_torrent_added, filename, filepath)
|
||||
d.addErrback(fail_torrent_add, filepath, magnet)
|
||||
except AddTorrentError as ex:
|
||||
|
@ -331,8 +338,10 @@ class Core(CorePluginBase):
|
|||
def on_update_watchdir_error(self, failure, watchdir_id):
|
||||
"""Disables any watch folders with un-handled exceptions."""
|
||||
self.disable_watchdir(watchdir_id)
|
||||
log.error('Disabling "%s", error during update: %s',
|
||||
self.watchdirs[watchdir_id]['path'], failure)
|
||||
log.error(
|
||||
'Disabling "%s", error during update: %s',
|
||||
self.watchdirs[watchdir_id]['path'], failure,
|
||||
)
|
||||
|
||||
@export
|
||||
def enable_watchdir(self, watchdir_id):
|
||||
|
@ -341,7 +350,7 @@ class Core(CorePluginBase):
|
|||
if w_id not in self.update_timers or not self.update_timers[w_id].running:
|
||||
self.update_timers[w_id] = LoopingCall(self.update_watchdir, w_id)
|
||||
self.update_timers[w_id].start(5).addErrback(
|
||||
self.on_update_watchdir_error, w_id
|
||||
self.on_update_watchdir_error, w_id,
|
||||
)
|
||||
# Update the config
|
||||
if not self.watchdirs[w_id]['enabled']:
|
||||
|
@ -383,8 +392,10 @@ class Core(CorePluginBase):
|
|||
session_user = rpcserver.get_session_user()
|
||||
session_auth_level = rpcserver.get_session_auth_level()
|
||||
if session_auth_level == AUTH_LEVEL_ADMIN:
|
||||
log.debug('Current logged in user %s is an ADMIN, send all '
|
||||
'watchdirs', session_user)
|
||||
log.debug(
|
||||
'Current logged in user %s is an ADMIN, send all '
|
||||
'watchdirs', session_user,
|
||||
)
|
||||
return self.watchdirs
|
||||
|
||||
watchdirs = {}
|
||||
|
@ -392,8 +403,10 @@ class Core(CorePluginBase):
|
|||
if watchdir.get('owner', 'localclient') == session_user:
|
||||
watchdirs[watchdir_id] = watchdir
|
||||
|
||||
log.debug('Current logged in user %s is not an ADMIN, send only '
|
||||
'their watchdirs: %s', session_user, list(watchdirs))
|
||||
log.debug(
|
||||
'Current logged in user %s is not an ADMIN, send only '
|
||||
'their watchdirs: %s', session_user, list(watchdirs),
|
||||
)
|
||||
return watchdirs
|
||||
|
||||
def _make_unicode(self, options):
|
||||
|
@ -414,7 +427,7 @@ class Core(CorePluginBase):
|
|||
check_input(os.path.isdir(abswatchdir), _('Path does not exist.'))
|
||||
check_input(
|
||||
os.access(abswatchdir, os.R_OK | os.W_OK),
|
||||
'You must have read and write access to watch folder.'
|
||||
'You must have read and write access to watch folder.',
|
||||
)
|
||||
if abswatchdir in [wd['abspath'] for wd in self.watchdirs.values()]:
|
||||
raise Exception('Path is already being watched.')
|
||||
|
@ -449,9 +462,11 @@ class Core(CorePluginBase):
|
|||
try:
|
||||
torrent = component.get('TorrentManager')[torrent_id]
|
||||
except KeyError:
|
||||
log.warning('Unable to remove torrent file for torrent id %s. It'
|
||||
'was already deleted from the TorrentManager',
|
||||
torrent_id)
|
||||
log.warning(
|
||||
'Unable to remove torrent file for torrent id %s. It'
|
||||
'was already deleted from the TorrentManager',
|
||||
torrent_id,
|
||||
)
|
||||
return
|
||||
torrent_fname = torrent.filename
|
||||
for watchdir in self.watchdirs.values():
|
||||
|
@ -466,9 +481,13 @@ class Core(CorePluginBase):
|
|||
if os.path.isfile(torrent_fname_path):
|
||||
try:
|
||||
os.remove(torrent_fname_path)
|
||||
log.info('Removed torrent file "%s" from "%s"',
|
||||
torrent_fname, copy_torrent_path)
|
||||
log.info(
|
||||
'Removed torrent file "%s" from "%s"',
|
||||
torrent_fname, copy_torrent_path,
|
||||
)
|
||||
break
|
||||
except OSError as ex:
|
||||
log.info('Failed to removed torrent file "%s" from "%s": %s',
|
||||
torrent_fname, copy_torrent_path, ex)
|
||||
log.info(
|
||||
'Failed to removed torrent file "%s" from "%s": %s',
|
||||
torrent_fname, copy_torrent_path, ex,
|
||||
)
|
||||
|
|
|
@ -37,8 +37,10 @@ class IncompatibleOption(Exception):
|
|||
class OptionsDialog(object):
|
||||
spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio']
|
||||
spin_int_ids = ['max_upload_slots', 'max_connections']
|
||||
chk_ids = ['stop_at_ratio', 'remove_at_ratio', 'move_completed',
|
||||
'add_paused', 'auto_managed', 'queue_to_top']
|
||||
chk_ids = [
|
||||
'stop_at_ratio', 'remove_at_ratio', 'move_completed',
|
||||
'add_paused', 'auto_managed', 'queue_to_top',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self.accounts = gtk.ListStore(str)
|
||||
|
@ -55,7 +57,7 @@ class OptionsDialog(object):
|
|||
'on_opts_apply': self.on_apply,
|
||||
'on_opts_cancel': self.on_cancel,
|
||||
'on_options_dialog_close': self.on_cancel,
|
||||
'on_toggle_toggled': self.on_toggle_toggled
|
||||
'on_toggle_toggled': self.on_toggle_toggled,
|
||||
})
|
||||
self.dialog = self.builder.get_object('options_dialog')
|
||||
self.dialog.set_transient_for(component.get('Preferences').pref_dialog)
|
||||
|
@ -77,22 +79,22 @@ class OptionsDialog(object):
|
|||
def load_options(self, options):
|
||||
self.builder.get_object('enabled').set_active(options.get('enabled', True))
|
||||
self.builder.get_object('append_extension_toggle').set_active(
|
||||
options.get('append_extension_toggle', False)
|
||||
options.get('append_extension_toggle', False),
|
||||
)
|
||||
self.builder.get_object('append_extension').set_text(
|
||||
options.get('append_extension', '.added')
|
||||
options.get('append_extension', '.added'),
|
||||
)
|
||||
self.builder.get_object('download_location_toggle').set_active(
|
||||
options.get('download_location_toggle', False)
|
||||
options.get('download_location_toggle', False),
|
||||
)
|
||||
self.builder.get_object('copy_torrent_toggle').set_active(
|
||||
options.get('copy_torrent_toggle', False)
|
||||
options.get('copy_torrent_toggle', False),
|
||||
)
|
||||
self.builder.get_object('delete_copy_torrent_toggle').set_active(
|
||||
options.get('delete_copy_torrent_toggle', False)
|
||||
options.get('delete_copy_torrent_toggle', False),
|
||||
)
|
||||
self.builder.get_object('seed_mode').set_active(
|
||||
options.get('seed_mode', False)
|
||||
options.get('seed_mode', False),
|
||||
)
|
||||
self.accounts.clear()
|
||||
self.labels.clear()
|
||||
|
@ -120,17 +122,19 @@ class OptionsDialog(object):
|
|||
self.builder.get_object('isnt_queue_to_top').set_active(True)
|
||||
if not options.get('auto_managed', True):
|
||||
self.builder.get_object('isnt_auto_managed').set_active(True)
|
||||
for field in ['move_completed_path', 'path', 'download_location',
|
||||
'copy_torrent']:
|
||||
for field in [
|
||||
'move_completed_path', 'path', 'download_location',
|
||||
'copy_torrent',
|
||||
]:
|
||||
if client.is_localhost():
|
||||
self.builder.get_object(field + '_chooser').set_current_folder(
|
||||
options.get(field, os.path.expanduser('~'))
|
||||
options.get(field, os.path.expanduser('~')),
|
||||
)
|
||||
self.builder.get_object(field + '_chooser').show()
|
||||
self.builder.get_object(field + '_entry').hide()
|
||||
else:
|
||||
self.builder.get_object(field + '_entry').set_text(
|
||||
options.get(field, '')
|
||||
options.get(field, ''),
|
||||
)
|
||||
self.builder.get_object(field + '_entry').show()
|
||||
self.builder.get_object(field + '_chooser').hide()
|
||||
|
@ -139,33 +143,33 @@ class OptionsDialog(object):
|
|||
def on_core_config(config):
|
||||
if client.is_localhost():
|
||||
self.builder.get_object('download_location_chooser').set_current_folder(
|
||||
options.get('download_location', config['download_location'])
|
||||
options.get('download_location', config['download_location']),
|
||||
)
|
||||
if options.get('move_completed_toggle', config['move_completed']):
|
||||
self.builder.get_object('move_completed_toggle').set_active(True)
|
||||
self.builder.get_object('move_completed_path_chooser').set_current_folder(
|
||||
options.get('move_completed_path', config['move_completed_path'])
|
||||
options.get('move_completed_path', config['move_completed_path']),
|
||||
)
|
||||
if options.get('copy_torrent_toggle', config['copy_torrent_file']):
|
||||
self.builder.get_object('copy_torrent_toggle').set_active(True)
|
||||
self.builder.get_object('copy_torrent_chooser').set_current_folder(
|
||||
options.get('copy_torrent', config['torrentfiles_location'])
|
||||
options.get('copy_torrent', config['torrentfiles_location']),
|
||||
)
|
||||
else:
|
||||
self.builder.get_object('download_location_entry').set_text(
|
||||
options.get('download_location', config['download_location'])
|
||||
options.get('download_location', config['download_location']),
|
||||
)
|
||||
if options.get('move_completed_toggle', config['move_completed']):
|
||||
self.builder.get_object('move_completed_toggle').set_active(
|
||||
options.get('move_completed_toggle', False)
|
||||
options.get('move_completed_toggle', False),
|
||||
)
|
||||
self.builder.get_object('move_completed_path_entry').set_text(
|
||||
options.get('move_completed_path', config['move_completed_path'])
|
||||
options.get('move_completed_path', config['move_completed_path']),
|
||||
)
|
||||
if options.get('copy_torrent_toggle', config['copy_torrent_file']):
|
||||
self.builder.get_object('copy_torrent_toggle').set_active(True)
|
||||
self.builder.get_object('copy_torrent_entry').set_text(
|
||||
options.get('copy_torrent', config['torrentfiles_location'])
|
||||
options.get('copy_torrent', config['torrentfiles_location']),
|
||||
)
|
||||
|
||||
if options.get('delete_copy_torrent_toggle', config['del_copy_torrent_file']):
|
||||
|
@ -180,7 +184,7 @@ class OptionsDialog(object):
|
|||
for account in accounts:
|
||||
acc_iter = self.accounts.append()
|
||||
self.accounts.set_value(
|
||||
acc_iter, 0, account['username']
|
||||
acc_iter, 0, account['username'],
|
||||
)
|
||||
if account['username'] == owner:
|
||||
selected_iter = acc_iter
|
||||
|
@ -215,7 +219,7 @@ class OptionsDialog(object):
|
|||
client.core.get_enabled_plugins().addCallback(on_get_enabled_plugins)
|
||||
if client.get_auth_level() == deluge.common.AUTH_LEVEL_ADMIN:
|
||||
client.core.get_known_accounts().addCallback(
|
||||
on_accounts, options.get('owner', client.get_auth_user())
|
||||
on_accounts, options.get('owner', client.get_auth_user()),
|
||||
).addErrback(on_accounts_failure)
|
||||
else:
|
||||
acc_iter = self.accounts.append()
|
||||
|
@ -224,11 +228,13 @@ class OptionsDialog(object):
|
|||
self.builder.get_object('OwnerCombobox').set_sensitive(False)
|
||||
|
||||
def set_sensitive(self):
|
||||
maintoggles = ['download_location', 'append_extension',
|
||||
'move_completed', 'label', 'max_download_speed',
|
||||
'max_upload_speed', 'max_connections',
|
||||
'max_upload_slots', 'add_paused', 'auto_managed',
|
||||
'stop_at_ratio', 'queue_to_top', 'copy_torrent']
|
||||
maintoggles = [
|
||||
'download_location', 'append_extension',
|
||||
'move_completed', 'label', 'max_download_speed',
|
||||
'max_upload_speed', 'max_connections',
|
||||
'max_upload_slots', 'add_paused', 'auto_managed',
|
||||
'stop_at_ratio', 'queue_to_top', 'copy_torrent',
|
||||
]
|
||||
for maintoggle in maintoggles:
|
||||
self.on_toggle_toggled(self.builder.get_object(maintoggle + '_toggle'))
|
||||
|
||||
|
@ -278,7 +284,7 @@ class OptionsDialog(object):
|
|||
try:
|
||||
options = self.generate_opts()
|
||||
client.autoadd.set_options(
|
||||
str(self.watchdir_id), options
|
||||
str(self.watchdir_id), options,
|
||||
).addCallbacks(self.on_added, self.on_error_show)
|
||||
except IncompatibleOption as ex:
|
||||
dialogs.ErrorDialog(_('Incompatible Option'), str(ex), self.dialog).run()
|
||||
|
@ -308,28 +314,37 @@ class OptionsDialog(object):
|
|||
if client.is_localhost():
|
||||
options['path'] = self.builder.get_object('path_chooser').get_filename()
|
||||
options['download_location'] = self.builder.get_object(
|
||||
'download_location_chooser').get_filename()
|
||||
'download_location_chooser',
|
||||
).get_filename()
|
||||
options['move_completed_path'] = self.builder.get_object(
|
||||
'move_completed_path_chooser').get_filename()
|
||||
'move_completed_path_chooser',
|
||||
).get_filename()
|
||||
options['copy_torrent'] = self.builder.get_object(
|
||||
'copy_torrent_chooser').get_filename()
|
||||
'copy_torrent_chooser',
|
||||
).get_filename()
|
||||
else:
|
||||
options['path'] = self.builder.get_object('path_entry').get_text()
|
||||
options['download_location'] = self.builder.get_object(
|
||||
'download_location_entry').get_text()
|
||||
'download_location_entry',
|
||||
).get_text()
|
||||
options['move_completed_path'] = self.builder.get_object(
|
||||
'move_completed_path_entry').get_text()
|
||||
'move_completed_path_entry',
|
||||
).get_text()
|
||||
options['copy_torrent'] = self.builder.get_object(
|
||||
'copy_torrent_entry').get_text()
|
||||
'copy_torrent_entry',
|
||||
).get_text()
|
||||
|
||||
options['label'] = self.builder.get_object('label').child.get_text().lower()
|
||||
options['append_extension'] = self.builder.get_object('append_extension').get_text()
|
||||
options['owner'] = self.accounts[
|
||||
self.builder.get_object('OwnerCombobox').get_active()][0]
|
||||
self.builder.get_object('OwnerCombobox').get_active()
|
||||
][0]
|
||||
|
||||
for key in ['append_extension_toggle', 'download_location_toggle',
|
||||
'label_toggle', 'copy_torrent_toggle',
|
||||
'delete_copy_torrent_toggle', 'seed_mode']:
|
||||
for key in [
|
||||
'append_extension_toggle', 'download_location_toggle',
|
||||
'label_toggle', 'copy_torrent_toggle',
|
||||
'delete_copy_torrent_toggle', 'seed_mode',
|
||||
]:
|
||||
options[key] = self.builder.get_object(key).get_active()
|
||||
|
||||
for spin_id in self.spin_ids:
|
||||
|
@ -343,8 +358,10 @@ class OptionsDialog(object):
|
|||
options[chk_id + '_toggle'] = self.builder.get_object(chk_id + '_toggle').get_active()
|
||||
|
||||
if options['copy_torrent_toggle'] and options['path'] == options['copy_torrent']:
|
||||
raise IncompatibleOption(_('"Watch Folder" directory and "Copy of .torrent'
|
||||
' files to" directory cannot be the same!'))
|
||||
raise IncompatibleOption(_(
|
||||
'"Watch Folder" directory and "Copy of .torrent'
|
||||
' files to" directory cannot be the same!',
|
||||
))
|
||||
return options
|
||||
|
||||
|
||||
|
@ -357,13 +374,13 @@ class GtkUI(GtkPluginBase):
|
|||
self.opts_dialog = OptionsDialog()
|
||||
|
||||
component.get('PluginManager').register_hook(
|
||||
'on_apply_prefs', self.on_apply_prefs
|
||||
'on_apply_prefs', self.on_apply_prefs,
|
||||
)
|
||||
component.get('PluginManager').register_hook(
|
||||
'on_show_prefs', self.on_show_prefs
|
||||
'on_show_prefs', self.on_show_prefs,
|
||||
)
|
||||
client.register_event_handler(
|
||||
'AutoaddOptionsChangedEvent', self.on_options_changed_event
|
||||
'AutoaddOptionsChangedEvent', self.on_options_changed_event,
|
||||
)
|
||||
|
||||
self.watchdirs = {}
|
||||
|
@ -386,16 +403,16 @@ class GtkUI(GtkPluginBase):
|
|||
sw.add(self.treeView)
|
||||
sw.show_all()
|
||||
component.get('Preferences').add_page(
|
||||
_('AutoAdd'), self.builder.get_object('prefs_box')
|
||||
_('AutoAdd'), self.builder.get_object('prefs_box'),
|
||||
)
|
||||
|
||||
def disable(self):
|
||||
component.get('Preferences').remove_page(_('AutoAdd'))
|
||||
component.get('PluginManager').deregister_hook(
|
||||
'on_apply_prefs', self.on_apply_prefs
|
||||
'on_apply_prefs', self.on_apply_prefs,
|
||||
)
|
||||
component.get('PluginManager').deregister_hook(
|
||||
'on_show_prefs', self.on_show_prefs
|
||||
'on_show_prefs', self.on_show_prefs,
|
||||
)
|
||||
|
||||
def create_model(self):
|
||||
|
@ -403,14 +420,14 @@ class GtkUI(GtkPluginBase):
|
|||
for watchdir_id, watchdir in self.watchdirs.items():
|
||||
store.append([
|
||||
watchdir_id, watchdir['enabled'],
|
||||
watchdir.get('owner', 'localclient'), watchdir['path']
|
||||
watchdir.get('owner', 'localclient'), watchdir['path'],
|
||||
])
|
||||
return store
|
||||
|
||||
def create_columns(self, treeview):
|
||||
renderer_toggle = gtk.CellRendererToggle()
|
||||
column = gtk.TreeViewColumn(
|
||||
_('Active'), renderer_toggle, activatable=1, active=1
|
||||
_('Active'), renderer_toggle, activatable=1, active=1,
|
||||
)
|
||||
column.set_sort_column_id(1)
|
||||
treeview.append_column(column)
|
||||
|
@ -490,7 +507,7 @@ class GtkUI(GtkPluginBase):
|
|||
for watchdir_id, watchdir in self.watchdirs.items():
|
||||
self.store.append([
|
||||
watchdir_id, watchdir['enabled'],
|
||||
watchdir.get('owner', 'localclient'), watchdir['path']
|
||||
watchdir.get('owner', 'localclient'), watchdir['path'],
|
||||
])
|
||||
# Workaround for cached glade signal appearing when re-enabling plugin in same session
|
||||
if self.builder.get_object('edit_button'):
|
||||
|
|
|
@ -45,5 +45,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -165,5 +165,5 @@ class IP(object):
|
|||
|
||||
def __repr__(self):
|
||||
return '<%s long=%s address="%s">' % (
|
||||
self.__class__.__name__, self.long, self.address
|
||||
self.__class__.__name__, self.long, self.address,
|
||||
)
|
||||
|
|
|
@ -104,14 +104,14 @@ class Core(CorePluginBase):
|
|||
self.update_timer = LoopingCall(self.check_import)
|
||||
if self.config['check_after_days'] > 0:
|
||||
self.update_timer.start(
|
||||
self.config['check_after_days'] * 24 * 60 * 60, update_now
|
||||
self.config['check_after_days'] * 24 * 60 * 60, update_now,
|
||||
)
|
||||
|
||||
def disable(self):
|
||||
self.config.save()
|
||||
log.debug('Reset IP filter')
|
||||
self.core.session.get_ip_filter().add_rule(
|
||||
'0.0.0.0', '255.255.255.255', ALLOW_RANGE
|
||||
'0.0.0.0', '255.255.255.255', ALLOW_RANGE,
|
||||
)
|
||||
log.debug('Blocklist: Plugin disabled')
|
||||
|
||||
|
@ -189,7 +189,7 @@ class Core(CorePluginBase):
|
|||
try:
|
||||
ip = IP.parse(ip)
|
||||
self.blocklist.add_rule(
|
||||
ip.address, ip.address, ALLOW_RANGE
|
||||
ip.address, ip.address, ALLOW_RANGE,
|
||||
)
|
||||
saved.add(ip.address)
|
||||
log.debug('Added %s to whitelisted', ip)
|
||||
|
@ -223,15 +223,17 @@ class Core(CorePluginBase):
|
|||
self.update_timer.stop()
|
||||
if self.config['check_after_days'] > 0:
|
||||
self.update_timer.start(
|
||||
self.config['check_after_days'] * 24 * 60 * 60, update_now
|
||||
self.config['check_after_days'] * 24 * 60 * 60, update_now,
|
||||
)
|
||||
continue
|
||||
self.config[key] = config[key]
|
||||
|
||||
if needs_blocklist_import:
|
||||
log.debug('IP addresses were removed from the whitelist. Since we '
|
||||
'do not know if they were blocked before. Re-import '
|
||||
'current blocklist and re-add whitelisted.')
|
||||
log.debug(
|
||||
'IP addresses were removed from the whitelist. Since we '
|
||||
'do not know if they were blocked before. Re-import '
|
||||
'current blocklist and re-add whitelisted.',
|
||||
)
|
||||
self.has_imported = False
|
||||
d = self.import_list(deluge.configmanager.get_config_dir('blocklist.cache'))
|
||||
d.addCallbacks(self.on_import_complete, self.on_import_error)
|
||||
|
@ -318,7 +320,7 @@ class Core(CorePluginBase):
|
|||
self.is_downloading = True
|
||||
return download_file(
|
||||
url, deluge.configmanager.get_config_dir('blocklist.download'),
|
||||
on_retrieve_data, headers
|
||||
on_retrieve_data, headers,
|
||||
)
|
||||
|
||||
def on_download_complete(self, blocklist):
|
||||
|
@ -365,8 +367,10 @@ class Core(CorePluginBase):
|
|||
else:
|
||||
log.warning('Blocklist download failed: %s', error_msg)
|
||||
if self.failed_attempts < self.config['try_times']:
|
||||
log.debug('Try downloading blocklist again... (%s/%s)',
|
||||
self.failed_attempts, self.config['try_times'])
|
||||
log.debug(
|
||||
'Try downloading blocklist again... (%s/%s)',
|
||||
self.failed_attempts, self.config['try_times'],
|
||||
)
|
||||
self.failed_attempts += 1
|
||||
d = self.download_list()
|
||||
d.addCallbacks(self.on_download_complete, self.on_download_error)
|
||||
|
|
|
@ -17,25 +17,25 @@ import zipfile
|
|||
|
||||
def Zipped(reader): # NOQA: N802
|
||||
"""Blocklist reader for zipped blocklists"""
|
||||
def open(self):
|
||||
def _open(self):
|
||||
z = zipfile.ZipFile(self.file)
|
||||
f = z.open(z.namelist()[0])
|
||||
return f
|
||||
reader.open = open
|
||||
reader.open = _open
|
||||
return reader
|
||||
|
||||
|
||||
def GZipped(reader): # NOQA: N802
|
||||
"""Blocklist reader for gzipped blocklists"""
|
||||
def open(self):
|
||||
def _open(self):
|
||||
return gzip.open(self.file)
|
||||
reader.open = open
|
||||
reader.open = _open
|
||||
return reader
|
||||
|
||||
|
||||
def BZipped2(reader): # NOQA: N802
|
||||
"""Blocklist reader for bzipped2 blocklists"""
|
||||
def open(self):
|
||||
def _open(self):
|
||||
return bz2.BZ2File(self.file)
|
||||
reader.open = open
|
||||
reader.open = _open
|
||||
return reader
|
||||
|
|
|
@ -15,19 +15,19 @@ from .readers import EmuleReader, PeerGuardianReader, SafePeerReader
|
|||
COMPRESSION_TYPES = {
|
||||
'PK': 'Zip',
|
||||
'\x1f\x8b': 'GZip',
|
||||
'BZ': 'BZip2'
|
||||
'BZ': 'BZip2',
|
||||
}
|
||||
|
||||
DECOMPRESSERS = {
|
||||
'Zip': Zipped,
|
||||
'GZip': GZipped,
|
||||
'BZip2': BZipped2
|
||||
'BZip2': BZipped2,
|
||||
}
|
||||
|
||||
READERS = {
|
||||
'Emule': EmuleReader,
|
||||
'SafePeer': SafePeerReader,
|
||||
'PeerGuardian': PeerGuardianReader
|
||||
'PeerGuardian': PeerGuardianReader,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class GtkUI(GtkPluginBase):
|
|||
image=common.get_resource('blocklist16.png'),
|
||||
text='',
|
||||
callback=self._on_status_item_clicked,
|
||||
tooltip=_('Blocked IP Ranges /Whitelisted IP Ranges')
|
||||
tooltip=_('Blocked IP Ranges /Whitelisted IP Ranges'),
|
||||
)
|
||||
|
||||
# Register some hooks
|
||||
|
@ -67,7 +67,8 @@ class GtkUI(GtkPluginBase):
|
|||
self.builder.get_object('image_up_to_date').hide()
|
||||
|
||||
self.status_item.set_text(
|
||||
'Downloading %.2f%%' % (status['file_progress'] * 100))
|
||||
'Downloading %.2f%%' % (status['file_progress'] * 100),
|
||||
)
|
||||
self.progress_bar.set_text('Downloading %.2f%%' % (status['file_progress'] * 100))
|
||||
self.progress_bar.set_fraction(status['file_progress'])
|
||||
self.progress_bar.show()
|
||||
|
@ -79,7 +80,8 @@ class GtkUI(GtkPluginBase):
|
|||
self.builder.get_object('image_up_to_date').hide()
|
||||
|
||||
self.status_item.set_text(
|
||||
'Importing ' + str(status['num_blocked']))
|
||||
'Importing ' + str(status['num_blocked']),
|
||||
)
|
||||
self.progress_bar.set_text('Importing %s' % (status['num_blocked']))
|
||||
self.progress_bar.pulse()
|
||||
self.progress_bar.show()
|
||||
|
@ -97,12 +99,15 @@ class GtkUI(GtkPluginBase):
|
|||
self.status_item.set_text('%(num_blocked)s/%(num_whited)s' % status)
|
||||
|
||||
self.builder.get_object('label_filesize').set_text(
|
||||
deluge.common.fsize(status['file_size']))
|
||||
deluge.common.fsize(status['file_size']),
|
||||
)
|
||||
self.builder.get_object('label_modified').set_text(
|
||||
datetime.fromtimestamp(status['file_date']).strftime('%c'))
|
||||
datetime.fromtimestamp(status['file_date']).strftime('%c'),
|
||||
)
|
||||
self.builder.get_object('label_type').set_text(status['file_type'])
|
||||
self.builder.get_object('label_url').set_text(
|
||||
status['file_url'])
|
||||
status['file_url'],
|
||||
)
|
||||
|
||||
client.blocklist.get_status().addCallback(_on_get_status)
|
||||
|
||||
|
@ -155,18 +160,24 @@ class GtkUI(GtkPluginBase):
|
|||
self.builder.connect_signals({
|
||||
'on_button_check_download_clicked': self._on_button_check_download_clicked,
|
||||
'on_button_force_download_clicked': self._on_button_force_download_clicked,
|
||||
'on_whitelist_add_clicked': (self.on_add_button_clicked,
|
||||
self.whitelist_treeview),
|
||||
'on_whitelist_remove_clicked': (self.on_delete_button_clicked,
|
||||
self.whitelist_treeview),
|
||||
'on_whitelist_add_clicked': (
|
||||
self.on_add_button_clicked,
|
||||
self.whitelist_treeview,
|
||||
),
|
||||
'on_whitelist_remove_clicked': (
|
||||
self.on_delete_button_clicked,
|
||||
self.whitelist_treeview,
|
||||
),
|
||||
})
|
||||
|
||||
# Set button icons
|
||||
self.builder.get_object('image_download').set_from_file(
|
||||
common.get_resource('blocklist_download24.png'))
|
||||
common.get_resource('blocklist_download24.png'),
|
||||
)
|
||||
|
||||
self.builder.get_object('image_import').set_from_file(
|
||||
common.get_resource('blocklist_import24.png'))
|
||||
common.get_resource('blocklist_import24.png'),
|
||||
)
|
||||
|
||||
# Update the preferences page with config values from the core
|
||||
self._on_show_prefs()
|
||||
|
@ -174,13 +185,14 @@ class GtkUI(GtkPluginBase):
|
|||
# Add the page to the preferences dialog
|
||||
self.plugin.add_preferences_page(
|
||||
_('Blocklist'),
|
||||
self.builder.get_object('blocklist_prefs_box'))
|
||||
self.builder.get_object('blocklist_prefs_box'),
|
||||
)
|
||||
|
||||
def build_whitelist_model_treeview(self):
|
||||
self.whitelist_treeview = self.builder.get_object('whitelist_treeview')
|
||||
treeview_selection = self.whitelist_treeview.get_selection()
|
||||
treeview_selection.connect(
|
||||
'changed', self.on_whitelist_treeview_selection_changed
|
||||
'changed', self.on_whitelist_treeview_selection_changed,
|
||||
)
|
||||
self.whitelist_model = gtk.ListStore(str, bool)
|
||||
renderer = gtk.CellRendererText()
|
||||
|
@ -207,11 +219,15 @@ class GtkUI(GtkPluginBase):
|
|||
def on_whitelist_treeview_selection_changed(self, selection):
|
||||
model, selected_connection_iter = selection.get_selected()
|
||||
if selected_connection_iter:
|
||||
self.builder.get_object('whitelist_delete').set_property('sensitive',
|
||||
True)
|
||||
self.builder.get_object('whitelist_delete').set_property(
|
||||
'sensitive',
|
||||
True,
|
||||
)
|
||||
else:
|
||||
self.builder.get_object('whitelist_delete').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('whitelist_delete').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
|
||||
def on_add_button_clicked(self, widget, treeview):
|
||||
model = treeview.get_model()
|
||||
|
@ -228,5 +244,5 @@ class GtkUI(GtkPluginBase):
|
|||
self.whitelist_model.clear()
|
||||
for ip in whitelist:
|
||||
self.whitelist_model.set(
|
||||
self.whitelist_model.append(), 0, ip, 1, True
|
||||
self.whitelist_model.append(), 0, ip, 1, True,
|
||||
)
|
||||
|
|
|
@ -21,7 +21,7 @@ FORMAT_LIST = [
|
|||
('gzmule', _('Emule IP list (GZip)')),
|
||||
('spzip', _('SafePeer Text (Zipped)')),
|
||||
('pgtext', _('PeerGuardian Text (Uncompressed)')),
|
||||
('p2bgz', _('PeerGuardian P2B (GZip)'))
|
||||
('p2bgz', _('PeerGuardian P2B (GZip)')),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -39,5 +39,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -26,7 +26,7 @@ from deluge.plugins.pluginbase import CorePluginBase
|
|||
log = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'commands': []
|
||||
'commands': [],
|
||||
}
|
||||
|
||||
EXECUTE_ID = 0
|
||||
|
@ -36,7 +36,7 @@ EXECUTE_COMMAND = 2
|
|||
EVENT_MAP = {
|
||||
'complete': 'TorrentFinishedEvent',
|
||||
'added': 'TorrentAddedEvent',
|
||||
'removed': 'TorrentRemovedEvent'
|
||||
'removed': 'TorrentRemovedEvent',
|
||||
}
|
||||
|
||||
|
||||
|
@ -118,8 +118,10 @@ class Core(CorePluginBase):
|
|||
command = os.path.expandvars(command[EXECUTE_COMMAND])
|
||||
command = os.path.expanduser(command)
|
||||
|
||||
cmd_args = [torrent_id.encode('utf8'), torrent_name.encode('utf8'),
|
||||
download_location.encode('utf8')]
|
||||
cmd_args = [
|
||||
torrent_id.encode('utf8'), torrent_name.encode('utf8'),
|
||||
download_location.encode('utf8'),
|
||||
]
|
||||
if windows_check():
|
||||
# Escape ampersand on windows (see #2784)
|
||||
cmd_args = [cmd_arg.replace('&', '^^^&') for cmd_arg in cmd_args]
|
||||
|
|
|
@ -30,7 +30,7 @@ EXECUTE_COMMAND = 2
|
|||
EVENT_MAP = {
|
||||
'complete': _('Torrent Complete'),
|
||||
'added': _('Torrent Added'),
|
||||
'removed': _('Torrent Removed')
|
||||
'removed': _('Torrent Removed'),
|
||||
}
|
||||
|
||||
EVENTS = ['complete', 'added', 'removed']
|
||||
|
|
|
@ -40,5 +40,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -30,7 +30,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
DEFAULT_PREFS = {
|
||||
'extract_path': '',
|
||||
'use_name_folder': True
|
||||
'use_name_folder': True,
|
||||
}
|
||||
|
||||
if windows_check():
|
||||
|
|
|
@ -51,7 +51,7 @@ class GtkUI(GtkPluginBase):
|
|||
|
||||
config = {
|
||||
'extract_path': path,
|
||||
'use_name_folder': self.builder.get_object('chk_use_name').get_active()
|
||||
'use_name_folder': self.builder.get_object('chk_use_name').get_active(),
|
||||
}
|
||||
|
||||
client.extractor.set_config(config)
|
||||
|
|
|
@ -53,5 +53,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -57,7 +57,7 @@ OPTIONS_DEFAULTS = {
|
|||
'move_completed': False,
|
||||
'move_completed_path': '',
|
||||
'auto_add': False,
|
||||
'auto_add_trackers': []
|
||||
'auto_add_trackers': [],
|
||||
}
|
||||
|
||||
NO_LABEL = 'No Label'
|
||||
|
@ -108,7 +108,7 @@ class Core(CorePluginBase):
|
|||
pass
|
||||
|
||||
def init_filter_dict(self):
|
||||
filter_dict = dict([(label, 0) for label in self.labels])
|
||||
filter_dict = {label: 0 for label in self.labels}
|
||||
filter_dict['All'] = len(self.torrents)
|
||||
return filter_dict
|
||||
|
||||
|
@ -208,8 +208,8 @@ class Core(CorePluginBase):
|
|||
torrent.set_options(
|
||||
{
|
||||
'move_completed': options['move_completed'],
|
||||
'move_completed_path': options['move_completed_path']
|
||||
}
|
||||
'move_completed_path': options['move_completed_path'],
|
||||
},
|
||||
)
|
||||
|
||||
def _unset_torrent_options(self, torrent_id, label_id):
|
||||
|
@ -233,8 +233,8 @@ class Core(CorePluginBase):
|
|||
torrent.set_options(
|
||||
{
|
||||
'move_completed': self.core_cfg.config['move_completed'],
|
||||
'move_completed_path': self.core_cfg.config['move_completed_path']
|
||||
}
|
||||
'move_completed_path': self.core_cfg.config['move_completed_path'],
|
||||
},
|
||||
)
|
||||
|
||||
def _has_auto_match(self, torrent, label_options):
|
||||
|
@ -310,7 +310,10 @@ class Core(CorePluginBase):
|
|||
@export
|
||||
def get_config(self):
|
||||
"""see : label_set_config"""
|
||||
return dict((key, self.config[key]) for key in CORE_OPTIONS if key in self.config.config)
|
||||
return {
|
||||
key: self.config[key]
|
||||
for key in CORE_OPTIONS if key in self.config.config
|
||||
}
|
||||
|
||||
@export
|
||||
def set_config(self, options):
|
||||
|
|
|
@ -126,8 +126,10 @@ class AddDialog(object):
|
|||
class OptionsDialog(object):
|
||||
spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio']
|
||||
spin_int_ids = ['max_upload_slots', 'max_connections']
|
||||
chk_ids = ['apply_max', 'apply_queue', 'stop_at_ratio', 'apply_queue', 'remove_at_ratio',
|
||||
'apply_move_completed', 'move_completed', 'is_auto_managed', 'auto_add']
|
||||
chk_ids = [
|
||||
'apply_max', 'apply_queue', 'stop_at_ratio', 'apply_queue', 'remove_at_ratio',
|
||||
'apply_move_completed', 'move_completed', 'is_auto_managed', 'auto_add',
|
||||
]
|
||||
|
||||
# list of tuples, because order matters when nesting.
|
||||
sensitive_groups = [
|
||||
|
@ -136,7 +138,7 @@ class OptionsDialog(object):
|
|||
('stop_at_ratio', ['remove_at_ratio', 'stop_ratio']), # nested
|
||||
('apply_move_completed', ['move_completed']),
|
||||
('move_completed', ['move_completed_path']), # nested
|
||||
('auto_add', ['auto_add_trackers'])
|
||||
('auto_add', ['auto_add_trackers']),
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
|
|
@ -44,5 +44,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -37,7 +37,7 @@ class CustomNotifications(object):
|
|||
'email': {},
|
||||
'popup': {},
|
||||
'blink': {},
|
||||
'sound': {}
|
||||
'sound': {},
|
||||
}
|
||||
|
||||
def enable(self):
|
||||
|
@ -72,7 +72,7 @@ class CustomNotifications(object):
|
|||
wrapper, handler = self.custom_notifications[kind][eventtype]
|
||||
try:
|
||||
component.get('EventManager').register_event_handler(
|
||||
eventtype, wrapper
|
||||
eventtype, wrapper,
|
||||
)
|
||||
except KeyError:
|
||||
from deluge.ui.client import client
|
||||
|
@ -83,7 +83,7 @@ class CustomNotifications(object):
|
|||
wrapper, handler = self.custom_notifications[kind][eventtype]
|
||||
try:
|
||||
component.get('EventManager').deregister_event_handler(
|
||||
eventtype, wrapper
|
||||
eventtype, wrapper,
|
||||
)
|
||||
except KeyError:
|
||||
from deluge.ui.client import client
|
||||
|
@ -99,8 +99,10 @@ class CustomNotifications(object):
|
|||
if known_events[eventtype].__module__.startswith('deluge.event'):
|
||||
if handler.__self__ is self:
|
||||
return True
|
||||
log.error('You cannot register custom notification providers '
|
||||
'for built-in event types.')
|
||||
log.error(
|
||||
'You cannot register custom notification providers '
|
||||
'for built-in event types.',
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ DEFAULT_PREFS = {
|
|||
'smtp_recipients': [],
|
||||
# Subscriptions
|
||||
'subscriptions': {
|
||||
'email': []
|
||||
}
|
||||
'email': [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -53,8 +53,10 @@ class CoreNotifications(CustomNotifications):
|
|||
|
||||
def enable(self):
|
||||
CustomNotifications.enable(self)
|
||||
self.register_custom_email_notification('TorrentFinishedEvent',
|
||||
self._on_torrent_finished_event)
|
||||
self.register_custom_email_notification(
|
||||
'TorrentFinishedEvent',
|
||||
self._on_torrent_finished_event,
|
||||
)
|
||||
|
||||
def disable(self):
|
||||
self.deregister_custom_email_notification('TorrentFinishedEvent')
|
||||
|
@ -77,8 +79,10 @@ class CoreNotifications(CustomNotifications):
|
|||
if not self.config['smtp_enabled']:
|
||||
return defer.succeed('SMTP notification not enabled.')
|
||||
subject, message = result
|
||||
log.debug('Spawning new thread to send email with subject: %s: %s',
|
||||
subject, message)
|
||||
log.debug(
|
||||
'Spawning new thread to send email with subject: %s: %s',
|
||||
subject, message,
|
||||
)
|
||||
# Spawn thread because we don't want Deluge to lock up while we send the
|
||||
# email.
|
||||
return threads.deferToThread(self._notify_email, subject, message)
|
||||
|
@ -103,7 +107,8 @@ class CoreNotifications(CustomNotifications):
|
|||
'smtp_from': self.config['smtp_from'],
|
||||
'subject': subject,
|
||||
'smtp_recipients': to_addrs_str,
|
||||
'date': formatdate()}
|
||||
'date': formatdate(),
|
||||
}
|
||||
headers = """\
|
||||
From: %(smtp_from)s
|
||||
To: %(smtp_recipients)s
|
||||
|
@ -176,7 +181,7 @@ Date: %(date)s
|
|||
'downloading "%(name)s", which includes %(num_files)i files.'
|
||||
'\nTo stop receiving these alerts, simply turn off email '
|
||||
"notification in Deluge's preferences.\n\n"
|
||||
'Thank you,\nDeluge.'
|
||||
'Thank you,\nDeluge.',
|
||||
) % torrent_status
|
||||
return subject, message
|
||||
|
||||
|
@ -196,7 +201,8 @@ class Core(CorePluginBase, CoreNotifications):
|
|||
def enable(self):
|
||||
CoreNotifications.enable(self)
|
||||
self.config = deluge.configmanager.ConfigManager(
|
||||
'notifications-core.conf', DEFAULT_PREFS)
|
||||
'notifications-core.conf', DEFAULT_PREFS,
|
||||
)
|
||||
log.debug('ENABLING CORE NOTIFICATIONS')
|
||||
|
||||
def disable(self):
|
||||
|
|
|
@ -67,8 +67,10 @@ DEFAULT_PREFS = {
|
|||
}
|
||||
|
||||
RECIPIENT_FIELD, RECIPIENT_EDIT = list(range(2))
|
||||
(SUB_EVENT, SUB_EVENT_DOC, SUB_NOT_EMAIL, SUB_NOT_POPUP, SUB_NOT_BLINK,
|
||||
SUB_NOT_SOUND) = list(range(6))
|
||||
(
|
||||
SUB_EVENT, SUB_EVENT_DOC, SUB_NOT_EMAIL, SUB_NOT_POPUP, SUB_NOT_BLINK,
|
||||
SUB_NOT_SOUND,
|
||||
) = list(range(6))
|
||||
SND_EVENT, SND_EVENT_DOC, SND_NAME, SND_PATH = list(range(4))
|
||||
|
||||
|
||||
|
@ -80,13 +82,13 @@ class GtkUiNotifications(CustomNotifications):
|
|||
def enable(self):
|
||||
CustomNotifications.enable(self)
|
||||
self.register_custom_blink_notification(
|
||||
'TorrentFinishedEvent', self._on_torrent_finished_event_blink
|
||||
'TorrentFinishedEvent', self._on_torrent_finished_event_blink,
|
||||
)
|
||||
self.register_custom_sound_notification(
|
||||
'TorrentFinishedEvent', self._on_torrent_finished_event_sound
|
||||
'TorrentFinishedEvent', self._on_torrent_finished_event_sound,
|
||||
)
|
||||
self.register_custom_popup_notification(
|
||||
'TorrentFinishedEvent', self._on_torrent_finished_event_popup
|
||||
'TorrentFinishedEvent', self._on_torrent_finished_event_popup,
|
||||
)
|
||||
|
||||
def disable(self):
|
||||
|
@ -146,17 +148,22 @@ class GtkUiNotifications(CustomNotifications):
|
|||
def handle_custom_blink_notification(self, result, eventtype):
|
||||
if result:
|
||||
return defer.maybeDeferred(self.__blink)
|
||||
return defer.succeed('Will not blink. The returned value from the custom '
|
||||
'handler was: %s' % result)
|
||||
return defer.succeed(
|
||||
'Will not blink. The returned value from the custom '
|
||||
'handler was: %s' % result,
|
||||
)
|
||||
|
||||
def handle_custom_sound_notification(self, result, eventtype):
|
||||
if isinstance(result, ''.__class__):
|
||||
if not result and eventtype in self.config['custom_sounds']:
|
||||
return defer.maybeDeferred(
|
||||
self.__play_sound, self.config['custom_sounds'][eventtype])
|
||||
self.__play_sound, self.config['custom_sounds'][eventtype],
|
||||
)
|
||||
return defer.maybeDeferred(self.__play_sound, result)
|
||||
return defer.succeed('Will not play sound. The returned value from the '
|
||||
'custom handler was: %s' % result)
|
||||
return defer.succeed(
|
||||
'Will not play sound. The returned value from the '
|
||||
'custom handler was: %s' % result,
|
||||
)
|
||||
|
||||
def __blink(self):
|
||||
self.systray.blink(True)
|
||||
|
@ -219,12 +226,16 @@ class GtkUiNotifications(CustomNotifications):
|
|||
log.debug('Failed to get torrent status to be able to show the popup')
|
||||
|
||||
def _on_torrent_finished_event_got_torrent_status(self, torrent_status):
|
||||
log.debug('Handler for TorrentFinishedEvent GTKUI called. '
|
||||
'Got Torrent Status')
|
||||
log.debug(
|
||||
'Handler for TorrentFinishedEvent GTKUI called. '
|
||||
'Got Torrent Status',
|
||||
)
|
||||
title = _('Finished Torrent')
|
||||
torrent_status['num_files'] = torrent_status['file_progress'].count(1.0)
|
||||
message = _('The torrent "%(name)s" including %(num_files)i file(s) '
|
||||
'has finished downloading.') % torrent_status
|
||||
message = _(
|
||||
'The torrent "%(name)s" including %(num_files)i file(s) '
|
||||
'has finished downloading.',
|
||||
) % torrent_status
|
||||
return title, message
|
||||
|
||||
|
||||
|
@ -235,7 +246,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
|
||||
def enable(self):
|
||||
self.config = deluge.configmanager.ConfigManager(
|
||||
'notifications-gtk.conf', DEFAULT_PREFS
|
||||
'notifications-gtk.conf', DEFAULT_PREFS,
|
||||
)
|
||||
self.builder = gtk.Builder()
|
||||
self.builder.add_from_file(get_resource('config.ui'))
|
||||
|
@ -248,73 +259,97 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
self.build_notifications_model_populate_treeview()
|
||||
|
||||
client.notifications.get_handled_events().addCallback(
|
||||
self.popuplate_what_needs_handled_events
|
||||
self.popuplate_what_needs_handled_events,
|
||||
)
|
||||
|
||||
self.builder.connect_signals({
|
||||
'on_add_button_clicked': (self.on_add_button_clicked,
|
||||
self.recipients_treeview),
|
||||
'on_delete_button_clicked': (self.on_delete_button_clicked,
|
||||
self.recipients_treeview),
|
||||
'on_add_button_clicked': (
|
||||
self.on_add_button_clicked,
|
||||
self.recipients_treeview,
|
||||
),
|
||||
'on_delete_button_clicked': (
|
||||
self.on_delete_button_clicked,
|
||||
self.recipients_treeview,
|
||||
),
|
||||
'on_enabled_toggled': self.on_enabled_toggled,
|
||||
'on_sound_enabled_toggled': self.on_sound_enabled_toggled,
|
||||
'on_sounds_edit_button_clicked': self.on_sounds_edit_button_clicked,
|
||||
'on_sounds_revert_button_clicked': self.on_sounds_revert_button_clicked,
|
||||
'on_sound_path_update_preview': self.on_sound_path_update_preview
|
||||
'on_sound_path_update_preview': self.on_sound_path_update_preview,
|
||||
})
|
||||
|
||||
component.get('Preferences').add_page(_('Notifications'), self.prefs)
|
||||
|
||||
component.get('PluginManager').register_hook('on_apply_prefs',
|
||||
self.on_apply_prefs)
|
||||
component.get('PluginManager').register_hook('on_show_prefs',
|
||||
self.on_show_prefs)
|
||||
component.get('PluginManager').register_hook(
|
||||
'on_apply_prefs',
|
||||
self.on_apply_prefs,
|
||||
)
|
||||
component.get('PluginManager').register_hook(
|
||||
'on_show_prefs',
|
||||
self.on_show_prefs,
|
||||
)
|
||||
|
||||
if not POPUP_AVAILABLE:
|
||||
self.builder.get_object('popup_enabled').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('popup_enabled').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
if not SOUND_AVAILABLE:
|
||||
# for widget_name in ('sound_enabled', 'sound_path', 'sounds_page', 'sounds_page_label'):
|
||||
# self.builder.get_object(widget_name).set_property('sensitive', False)
|
||||
self.builder.get_object('sound_enabled').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('sound_enabled').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
self.builder.get_object('sound_path').set_property('sensitive', False)
|
||||
self.builder.get_object('sounds_page').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('sounds_page_label').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('sounds_page').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
self.builder.get_object('sounds_page_label').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
|
||||
self.systray = component.get('SystemTray')
|
||||
if not hasattr(self.systray, 'tray'):
|
||||
# Tray is not beeing used
|
||||
self.builder.get_object('blink_enabled').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('blink_enabled').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
|
||||
GtkUiNotifications.enable(self)
|
||||
|
||||
def disable(self):
|
||||
GtkUiNotifications.disable(self)
|
||||
component.get('Preferences').remove_page(_('Notifications'))
|
||||
component.get('PluginManager').deregister_hook('on_apply_prefs',
|
||||
self.on_apply_prefs)
|
||||
component.get('PluginManager').deregister_hook('on_show_prefs',
|
||||
self.on_show_prefs)
|
||||
component.get('PluginManager').deregister_hook(
|
||||
'on_apply_prefs',
|
||||
self.on_apply_prefs,
|
||||
)
|
||||
component.get('PluginManager').deregister_hook(
|
||||
'on_show_prefs',
|
||||
self.on_show_prefs,
|
||||
)
|
||||
|
||||
def build_recipients_model_populate_treeview(self):
|
||||
# SMTP Recipients treeview/model
|
||||
self.recipients_treeview = self.builder.get_object('smtp_recipients')
|
||||
treeview_selection = self.recipients_treeview.get_selection()
|
||||
treeview_selection.connect(
|
||||
'changed', self.on_recipients_treeview_selection_changed
|
||||
'changed', self.on_recipients_treeview_selection_changed,
|
||||
)
|
||||
self.recipients_model = gtk.ListStore(str, bool)
|
||||
|
||||
renderer = gtk.CellRendererText()
|
||||
renderer.connect('edited', self.on_cell_edited, self.recipients_model)
|
||||
renderer.set_data('recipient', RECIPIENT_FIELD)
|
||||
column = gtk.TreeViewColumn('Recipients', renderer,
|
||||
text=RECIPIENT_FIELD,
|
||||
editable=RECIPIENT_EDIT)
|
||||
column = gtk.TreeViewColumn(
|
||||
'Recipients', renderer,
|
||||
text=RECIPIENT_FIELD,
|
||||
editable=RECIPIENT_EDIT,
|
||||
)
|
||||
column.set_expand(True)
|
||||
self.recipients_treeview.append_column(column)
|
||||
self.recipients_treeview.set_model(self.recipients_model)
|
||||
|
@ -324,7 +359,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
self.sounds_treeview = self.builder.get_object('sounds_treeview')
|
||||
sounds_selection = self.sounds_treeview.get_selection()
|
||||
sounds_selection.connect(
|
||||
'changed', self.on_sounds_treeview_selection_changed
|
||||
'changed', self.on_sounds_treeview_selection_changed,
|
||||
)
|
||||
|
||||
self.sounds_treeview.set_tooltip_column(SND_EVENT_DOC)
|
||||
|
@ -360,7 +395,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
self.subscriptions_treeview = self.builder.get_object('subscriptions_treeview')
|
||||
subscriptions_selection = self.subscriptions_treeview.get_selection()
|
||||
subscriptions_selection.connect(
|
||||
'changed', self.on_subscriptions_treeview_selection_changed
|
||||
'changed', self.on_subscriptions_treeview_selection_changed,
|
||||
)
|
||||
self.subscriptions_treeview.set_tooltip_column(SUB_EVENT_DOC)
|
||||
self.subscriptions_model = gtk.ListStore(str, str, bool, bool, bool, bool)
|
||||
|
@ -426,7 +461,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
SND_EVENT, event_name,
|
||||
SND_EVENT_DOC, event_doc,
|
||||
SND_NAME, basename(snd_path),
|
||||
SND_PATH, snd_path
|
||||
SND_PATH, snd_path,
|
||||
)
|
||||
|
||||
def populate_subscriptions(self, handled_events, email_subscriptions=None):
|
||||
|
@ -443,7 +478,7 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
SUB_NOT_EMAIL, event_name in email_subscriptions,
|
||||
SUB_NOT_POPUP, event_name in subscriptions_dict['popup'],
|
||||
SUB_NOT_BLINK, event_name in subscriptions_dict['blink'],
|
||||
SUB_NOT_SOUND, event_name in subscriptions_dict['sound']
|
||||
SUB_NOT_SOUND, event_name in subscriptions_dict['sound'],
|
||||
)
|
||||
|
||||
def on_apply_prefs(self):
|
||||
|
@ -465,8 +500,10 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
|
||||
old_sound_file = self.config['sound_path']
|
||||
new_sound_file = self.builder.get_object('sound_path').get_filename()
|
||||
log.debug('Old Default sound file: %s New one: %s',
|
||||
old_sound_file, new_sound_file)
|
||||
log.debug(
|
||||
'Old Default sound file: %s New one: %s',
|
||||
old_sound_file, new_sound_file,
|
||||
)
|
||||
custom_sounds = {}
|
||||
for event_name, event_doc, filename, filepath in self.sounds_model:
|
||||
log.debug('Custom sound for event "%s": %s', event_name, filename)
|
||||
|
@ -482,9 +519,9 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
'subscriptions': {
|
||||
'popup': current_popup_subscriptions,
|
||||
'blink': current_blink_subscriptions,
|
||||
'sound': current_sound_subscriptions
|
||||
'sound': current_sound_subscriptions,
|
||||
},
|
||||
'custom_sounds': custom_sounds
|
||||
'custom_sounds': custom_sounds,
|
||||
})
|
||||
self.config.save()
|
||||
|
||||
|
@ -496,9 +533,11 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
'smtp_pass': self.builder.get_object('smtp_pass').get_text(),
|
||||
'smtp_from': self.builder.get_object('smtp_from').get_text(),
|
||||
'smtp_tls': self.builder.get_object('smtp_tls').get_active(),
|
||||
'smtp_recipients': [dest[0] for dest in self.recipients_model if
|
||||
dest[0] != 'USER@HOST'],
|
||||
'subscriptions': {'email': current_email_subscriptions}
|
||||
'smtp_recipients': [
|
||||
dest[0] for dest in self.recipients_model if
|
||||
dest[0] != 'USER@HOST'
|
||||
],
|
||||
'subscriptions': {'email': current_email_subscriptions},
|
||||
}
|
||||
|
||||
client.notifications.set_config(core_config)
|
||||
|
@ -517,20 +556,22 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
self.builder.get_object('smtp_tls').set_active(core_config['smtp_tls'])
|
||||
self.recipients_model.clear()
|
||||
for recipient in core_config['smtp_recipients']:
|
||||
self.recipients_model.set(self.recipients_model.append(),
|
||||
RECIPIENT_FIELD, recipient,
|
||||
RECIPIENT_EDIT, False)
|
||||
self.recipients_model.set(
|
||||
self.recipients_model.append(),
|
||||
RECIPIENT_FIELD, recipient,
|
||||
RECIPIENT_EDIT, False,
|
||||
)
|
||||
self.builder.get_object('smtp_enabled').set_active(
|
||||
core_config['smtp_enabled']
|
||||
core_config['smtp_enabled'],
|
||||
)
|
||||
self.builder.get_object('sound_enabled').set_active(
|
||||
self.config['sound_enabled']
|
||||
self.config['sound_enabled'],
|
||||
)
|
||||
self.builder.get_object('popup_enabled').set_active(
|
||||
self.config['popup_enabled']
|
||||
self.config['popup_enabled'],
|
||||
)
|
||||
self.builder.get_object('blink_enabled').set_active(
|
||||
self.config['blink_enabled']
|
||||
self.config['blink_enabled'],
|
||||
)
|
||||
if self.config['sound_path']:
|
||||
sound_path = self.config['sound_path']
|
||||
|
@ -543,19 +584,21 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
|
||||
client.notifications.get_handled_events().addCallback(
|
||||
self.popuplate_what_needs_handled_events,
|
||||
core_config['subscriptions']['email']
|
||||
core_config['subscriptions']['email'],
|
||||
)
|
||||
|
||||
def on_sound_path_update_preview(self, filechooser):
|
||||
client.notifications.get_handled_events().addCallback(
|
||||
self.populate_sounds
|
||||
self.populate_sounds,
|
||||
)
|
||||
|
||||
def on_add_button_clicked(self, widget, treeview):
|
||||
model = treeview.get_model()
|
||||
model.set(model.append(),
|
||||
RECIPIENT_FIELD, 'USER@HOST',
|
||||
RECIPIENT_EDIT, True)
|
||||
model.set(
|
||||
model.append(),
|
||||
RECIPIENT_FIELD, 'USER@HOST',
|
||||
RECIPIENT_EDIT, True,
|
||||
)
|
||||
|
||||
def on_delete_button_clicked(self, widget, treeview):
|
||||
selection = treeview.get_selection()
|
||||
|
@ -570,20 +613,28 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
def on_recipients_treeview_selection_changed(self, selection):
|
||||
model, selected_connection_iter = selection.get_selected()
|
||||
if selected_connection_iter:
|
||||
self.builder.get_object('delete_button').set_property('sensitive',
|
||||
True)
|
||||
self.builder.get_object('delete_button').set_property(
|
||||
'sensitive',
|
||||
True,
|
||||
)
|
||||
else:
|
||||
self.builder.get_object('delete_button').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('delete_button').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
|
||||
def on_subscriptions_treeview_selection_changed(self, selection):
|
||||
model, selected_connection_iter = selection.get_selected()
|
||||
if selected_connection_iter:
|
||||
self.builder.get_object('delete_button').set_property('sensitive',
|
||||
True)
|
||||
self.builder.get_object('delete_button').set_property(
|
||||
'sensitive',
|
||||
True,
|
||||
)
|
||||
else:
|
||||
self.builder.get_object('delete_button').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('delete_button').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
|
||||
def on_sounds_treeview_selection_changed(self, selection):
|
||||
model, selected_iter = selection.get_selected()
|
||||
|
@ -605,9 +656,11 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
model, selected_iter = selection.get_selected()
|
||||
if selected_iter:
|
||||
log.debug('on_sounds_revert_button_clicked: got iter')
|
||||
model.set(selected_iter,
|
||||
SND_PATH, self.config['sound_path'],
|
||||
SND_NAME, basename(self.config['sound_path']))
|
||||
model.set(
|
||||
selected_iter,
|
||||
SND_PATH, self.config['sound_path'],
|
||||
SND_NAME, basename(self.config['sound_path']),
|
||||
)
|
||||
|
||||
def on_sounds_edit_button_clicked(self, widget):
|
||||
log.debug('on_sounds_edit_button_clicked')
|
||||
|
@ -617,10 +670,12 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
path = model.get(selected_iter, SND_PATH)[0]
|
||||
dialog = gtk.FileChooserDialog(
|
||||
title=_('Choose Sound File'),
|
||||
buttons=(gtk.STOCK_CANCEL,
|
||||
gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN,
|
||||
gtk.RESPONSE_OK)
|
||||
buttons=(
|
||||
gtk.STOCK_CANCEL,
|
||||
gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN,
|
||||
gtk.RESPONSE_OK,
|
||||
),
|
||||
)
|
||||
dialog.set_filename(path)
|
||||
|
||||
|
@ -629,34 +684,48 @@ class GtkUI(GtkPluginBase, GtkUiNotifications):
|
|||
new_filename = dialog.get_filename()
|
||||
dialog.destroy()
|
||||
log.debug(new_filename)
|
||||
model.set(selected_iter,
|
||||
SND_PATH, new_filename,
|
||||
SND_NAME, basename(new_filename))
|
||||
model.set(
|
||||
selected_iter,
|
||||
SND_PATH, new_filename,
|
||||
SND_NAME, basename(new_filename),
|
||||
)
|
||||
d = defer.maybeDeferred(dialog.run)
|
||||
d.addCallback(update_model)
|
||||
|
||||
log.debug('dialog should have been shown')
|
||||
|
||||
def on_enabled_toggled(self, widget):
|
||||
for widget_name in ('smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass',
|
||||
'smtp_pass', 'smtp_tls', 'smtp_from',
|
||||
'smtp_recipients'):
|
||||
self.builder.get_object(widget_name).set_property('sensitive',
|
||||
widget.get_active())
|
||||
for widget_name in (
|
||||
'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass',
|
||||
'smtp_pass', 'smtp_tls', 'smtp_from',
|
||||
'smtp_recipients',
|
||||
):
|
||||
self.builder.get_object(widget_name).set_property(
|
||||
'sensitive',
|
||||
widget.get_active(),
|
||||
)
|
||||
|
||||
def on_sound_enabled_toggled(self, widget):
|
||||
if widget.get_active():
|
||||
self.builder.get_object('sound_path').set_property('sensitive', True)
|
||||
self.builder.get_object('sounds_page').set_property('sensitive',
|
||||
True)
|
||||
self.builder.get_object('sounds_page_label').set_property('sensitive',
|
||||
True)
|
||||
self.builder.get_object('sounds_page').set_property(
|
||||
'sensitive',
|
||||
True,
|
||||
)
|
||||
self.builder.get_object('sounds_page_label').set_property(
|
||||
'sensitive',
|
||||
True,
|
||||
)
|
||||
else:
|
||||
self.builder.get_object('sound_path').set_property('sensitive', False)
|
||||
self.builder.get_object('sounds_page').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('sounds_page_label').set_property('sensitive',
|
||||
False)
|
||||
self.builder.get_object('sounds_page').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
self.builder.get_object('sounds_page_label').set_property(
|
||||
'sensitive',
|
||||
False,
|
||||
)
|
||||
|
||||
# for widget_name in ('sounds_path', 'sounds_page', 'sounds_page_label'):
|
||||
# self.builder.get_object(widget_name).set_property('sensitive',
|
||||
|
|
|
@ -34,7 +34,7 @@ class TestEmailNotifications(component.Component):
|
|||
self.n = 1
|
||||
self.events = [
|
||||
FooEvent(),
|
||||
CustomEvent()
|
||||
CustomEvent(),
|
||||
]
|
||||
self.events_classes = []
|
||||
|
||||
|
@ -45,21 +45,21 @@ class TestEmailNotifications(component.Component):
|
|||
# component.get('CorePlugin.Notifications').register_custom_email_notification(
|
||||
component.get('Notifications').register_custom_email_notification(
|
||||
event.__class__.__name__,
|
||||
self.custom_email_message_provider
|
||||
self.custom_email_message_provider,
|
||||
)
|
||||
elif self.__imp == 'gtk':
|
||||
notifications_component = component.get('Notifications')
|
||||
notifications_component.register_custom_popup_notification(
|
||||
event.__class__.__name__,
|
||||
self.custom_popup_message_provider
|
||||
self.custom_popup_message_provider,
|
||||
)
|
||||
notifications_component.register_custom_blink_notification(
|
||||
event.__class__.__name__,
|
||||
self.custom_blink_message_provider
|
||||
self.custom_blink_message_provider,
|
||||
)
|
||||
notifications_component.register_custom_sound_notification(
|
||||
event.__class__.__name__,
|
||||
self.custom_sound_message_provider
|
||||
self.custom_sound_message_provider,
|
||||
)
|
||||
|
||||
self.lc.start(60, False)
|
||||
|
|
|
@ -52,5 +52,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -32,13 +32,13 @@ DEFAULT_PREFS = {
|
|||
'low_active': -1,
|
||||
'low_active_down': -1,
|
||||
'low_active_up': -1,
|
||||
'button_state': [[0] * 7 for dummy in range(24)]
|
||||
'button_state': [[0] * 7 for dummy in range(24)],
|
||||
}
|
||||
|
||||
STATES = {
|
||||
0: 'Green',
|
||||
1: 'Yellow',
|
||||
2: 'Red'
|
||||
2: 'Red',
|
||||
}
|
||||
|
||||
CONTROLLED_SETTINGS = [
|
||||
|
@ -46,7 +46,7 @@ CONTROLLED_SETTINGS = [
|
|||
'max_upload_speed',
|
||||
'max_active_limit',
|
||||
'max_active_downloading',
|
||||
'max_active_seeding'
|
||||
'max_active_seeding',
|
||||
]
|
||||
|
||||
|
||||
|
@ -127,7 +127,7 @@ class Core(CorePluginBase):
|
|||
'active_downloads': self.config['low_active_down'],
|
||||
'active_seeds': self.config['low_active_up'],
|
||||
'download_rate_limit': int(self.config['low_down'] * 1024),
|
||||
'upload_rate_limit': int(self.config['low_up'] * 1024)
|
||||
'upload_rate_limit': int(self.config['low_up'] * 1024),
|
||||
}
|
||||
component.get('Core').apply_session_settings(settings)
|
||||
# Resume the session if necessary
|
||||
|
|
|
@ -31,8 +31,10 @@ DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|||
class SchedulerSelectWidget(gtk.DrawingArea):
|
||||
def __init__(self, hover):
|
||||
super(SchedulerSelectWidget, self).__init__()
|
||||
self.set_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK |
|
||||
gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.LEAVE_NOTIFY_MASK)
|
||||
self.set_events(
|
||||
gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK |
|
||||
gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.LEAVE_NOTIFY_MASK,
|
||||
)
|
||||
|
||||
self.connect('expose_event', self.expose)
|
||||
self.connect('button_press_event', self.mouse_down)
|
||||
|
@ -40,9 +42,11 @@ class SchedulerSelectWidget(gtk.DrawingArea):
|
|||
self.connect('motion_notify_event', self.mouse_hover)
|
||||
self.connect('leave_notify_event', self.mouse_leave)
|
||||
|
||||
self.colors = [[115 / 255, 210 / 255, 22 / 255],
|
||||
[237 / 255, 212 / 255, 0 / 255],
|
||||
[204 / 255, 0 / 255, 0 / 255]]
|
||||
self.colors = [
|
||||
[115 / 255, 210 / 255, 22 / 255],
|
||||
[237 / 255, 212 / 255, 0 / 255],
|
||||
[204 / 255, 0 / 255, 0 / 255],
|
||||
]
|
||||
self.button_state = [[0] * 7 for dummy in range(24)]
|
||||
|
||||
self.start_point = [0, 0]
|
||||
|
@ -69,11 +73,15 @@ class SchedulerSelectWidget(gtk.DrawingArea):
|
|||
|
||||
for y in range(7):
|
||||
for x in range(24):
|
||||
context.set_source_rgba(self.colors[self.button_state[x][y]][0],
|
||||
self.colors[self.button_state[x][y]][1],
|
||||
self.colors[self.button_state[x][y]][2], 0.7)
|
||||
context.rectangle(width * (6 * x / 145 + 1 / 145), height * (6 * y / 43 + 1 / 43),
|
||||
5 * width / 145, 5 * height / 43)
|
||||
context.set_source_rgba(
|
||||
self.colors[self.button_state[x][y]][0],
|
||||
self.colors[self.button_state[x][y]][1],
|
||||
self.colors[self.button_state[x][y]][2], 0.7,
|
||||
)
|
||||
context.rectangle(
|
||||
width * (6 * x / 145 + 1 / 145), height * (6 * y / 43 + 1 / 43),
|
||||
5 * width / 145, 5 * height / 43,
|
||||
)
|
||||
context.fill_preserve()
|
||||
context.set_source_rgba(0.5, 0.5, 0.5, 0.5)
|
||||
context.stroke()
|
||||
|
@ -123,9 +131,11 @@ class SchedulerSelectWidget(gtk.DrawingArea):
|
|||
if self.get_point(event) != self.hover_point:
|
||||
self.hover_point = self.get_point(event)
|
||||
|
||||
self.hover_label.set_text(self.hover_days[self.hover_point[1]] +
|
||||
' ' + str(self.hover_point[0]) +
|
||||
':00 - ' + str(self.hover_point[0]) + ':59')
|
||||
self.hover_label.set_text(
|
||||
self.hover_days[self.hover_point[1]] +
|
||||
' ' + str(self.hover_point[0]) +
|
||||
':00 - ' + str(self.hover_point[0]) + ':59',
|
||||
)
|
||||
|
||||
if self.mouse_press:
|
||||
points = [[self.hover_point[0], self.start_point[0]], [self.hover_point[1], self.start_point[1]]]
|
||||
|
@ -153,7 +163,8 @@ class GtkUI(GtkPluginBase):
|
|||
image=get_resource('green.png'),
|
||||
text='',
|
||||
callback=self.on_status_item_clicked,
|
||||
tooltip='Scheduler')
|
||||
tooltip='Scheduler',
|
||||
)
|
||||
|
||||
def on_state_deferred(state):
|
||||
self.state = state
|
||||
|
|
|
@ -44,5 +44,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -32,7 +32,7 @@ DEFAULT_TOTALS = {
|
|||
'total_download': 0,
|
||||
'total_payload_upload': 0,
|
||||
'total_payload_download': 0,
|
||||
'stats': {}
|
||||
'stats': {},
|
||||
}
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -85,7 +85,7 @@ class Core(CorePluginBase):
|
|||
'dht_cache_nodes',
|
||||
'dht_torrents',
|
||||
'num_peers',
|
||||
'num_connections'
|
||||
'num_connections',
|
||||
)
|
||||
|
||||
self.update_stats()
|
||||
|
@ -121,9 +121,11 @@ class Core(CorePluginBase):
|
|||
pass
|
||||
stats['num_connections'] = stats['num_peers'] + stats['peer.num_peers_half_open']
|
||||
stats['dht_cache_nodes'] = stats['dht.dht_node_cache']
|
||||
stats.update(self.core.get_config_values(['max_download',
|
||||
'max_upload',
|
||||
'max_num_connections']))
|
||||
stats.update(self.core.get_config_values([
|
||||
'max_download',
|
||||
'max_upload',
|
||||
'max_num_connections',
|
||||
]))
|
||||
# status = self.core.session.status()
|
||||
# for stat in dir(status):
|
||||
# if not stat.startswith('_') and stat not in stats:
|
||||
|
@ -193,7 +195,8 @@ class Core(CorePluginBase):
|
|||
@export
|
||||
def get_session_totals(self):
|
||||
return self.core.get_session_status(
|
||||
['total_upload', 'total_download', 'total_payload_upload', 'total_payload_download'])
|
||||
['total_upload', 'total_download', 'total_payload_upload', 'total_payload_download'],
|
||||
)
|
||||
|
||||
@export
|
||||
def set_config(self, config):
|
||||
|
|
|
@ -69,7 +69,7 @@ class Graph(object):
|
|||
self.mean_selected = True
|
||||
self.legend_selected = True
|
||||
self.max_selected = True
|
||||
self.black = (0, 0, 0,)
|
||||
self.black = (0, 0, 0)
|
||||
self.interval = 2 # 2 secs
|
||||
self.text_bg = (255, 255, 255, 128) # prototyping
|
||||
self.set_left_axis()
|
||||
|
@ -83,7 +83,7 @@ class Graph(object):
|
|||
'label': label,
|
||||
'line': line,
|
||||
'fill': fill,
|
||||
'color': color
|
||||
'color': color,
|
||||
}
|
||||
|
||||
def set_stats(self, stats):
|
||||
|
@ -273,7 +273,8 @@ class Graph(object):
|
|||
|
||||
self.ctx.line_to(
|
||||
int(right - (len(values) - 1) * step),
|
||||
bottom)
|
||||
bottom,
|
||||
)
|
||||
self.ctx.close_path()
|
||||
|
||||
def draw_value_poly(self, values, color, max_value, bounds, fill=False):
|
||||
|
|
|
@ -45,7 +45,9 @@ DEFAULT_CONF = {
|
|||
},
|
||||
'seeds_graph': {
|
||||
'num_peers': str(gtk.gdk.Color('blue')),
|
||||
}}}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def neat_time(column, cell, model, data):
|
||||
|
@ -126,9 +128,11 @@ class GraphsTab(Tab):
|
|||
# set a clip region
|
||||
context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
|
||||
context.clip()
|
||||
self.graph.draw_to_context(context,
|
||||
self.graph_widget.allocation.width,
|
||||
self.graph_widget.allocation.height)
|
||||
self.graph.draw_to_context(
|
||||
context,
|
||||
self.graph_widget.allocation.width,
|
||||
self.graph_widget.allocation.height,
|
||||
)
|
||||
# Do not propagate the event
|
||||
return False
|
||||
|
||||
|
@ -153,12 +157,18 @@ class GraphsTab(Tab):
|
|||
self.graph_widget = self.bandwidth_graph
|
||||
self.graph = Graph()
|
||||
colors = self.colors['bandwidth_graph']
|
||||
self.graph.add_stat('download_rate', label='Download Rate',
|
||||
color=gtk_to_graph_color(colors['download_rate']))
|
||||
self.graph.add_stat('upload_rate', label='Upload Rate',
|
||||
color=gtk_to_graph_color(colors['upload_rate']))
|
||||
self.graph.set_left_axis(formatter=fspeed_shortform, min=10240,
|
||||
formatter_scale=size_formatter_scale)
|
||||
self.graph.add_stat(
|
||||
'download_rate', label='Download Rate',
|
||||
color=gtk_to_graph_color(colors['download_rate']),
|
||||
)
|
||||
self.graph.add_stat(
|
||||
'upload_rate', label='Upload Rate',
|
||||
color=gtk_to_graph_color(colors['upload_rate']),
|
||||
)
|
||||
self.graph.set_left_axis(
|
||||
formatter=fspeed_shortform, min=10240,
|
||||
formatter_scale=size_formatter_scale,
|
||||
)
|
||||
|
||||
def select_connections_graph(self):
|
||||
log.debug('Selecting connections graph')
|
||||
|
@ -183,9 +193,11 @@ class GraphsTab(Tab):
|
|||
def set_colors(self, colors):
|
||||
self.colors = colors
|
||||
# Fake switch page to update the graph colors (HACKY)
|
||||
self._on_notebook_switch_page(self.notebook,
|
||||
None, # This is unused
|
||||
self.notebook.get_current_page())
|
||||
self._on_notebook_switch_page(
|
||||
self.notebook,
|
||||
None, # This is unused
|
||||
self.notebook.get_current_page(),
|
||||
)
|
||||
|
||||
def _on_intervals_changed(self, intervals):
|
||||
liststore = gtk.ListStore(int)
|
||||
|
|
|
@ -46,10 +46,10 @@ class StatsTestCase(BaseTestCase):
|
|||
raise unittest.SkipTest('WebUi plugin not available for testing')
|
||||
|
||||
totals = yield client.stats.get_totals()
|
||||
self.assertEquals(totals['total_upload'], 0)
|
||||
self.assertEquals(totals['total_payload_upload'], 0)
|
||||
self.assertEquals(totals['total_payload_download'], 0)
|
||||
self.assertEquals(totals['total_download'], 0)
|
||||
self.assertEqual(totals['total_upload'], 0)
|
||||
self.assertEqual(totals['total_payload_upload'], 0)
|
||||
self.assertEqual(totals['total_payload_download'], 0)
|
||||
self.assertEqual(totals['total_download'], 0)
|
||||
# print_totals(totals)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -59,10 +59,10 @@ class StatsTestCase(BaseTestCase):
|
|||
raise unittest.SkipTest('WebUi plugin not available for testing')
|
||||
|
||||
totals = yield client.stats.get_session_totals()
|
||||
self.assertEquals(totals['total_upload'], 0)
|
||||
self.assertEquals(totals['total_payload_upload'], 0)
|
||||
self.assertEquals(totals['total_payload_download'], 0)
|
||||
self.assertEquals(totals['total_download'], 0)
|
||||
self.assertEqual(totals['total_upload'], 0)
|
||||
self.assertEqual(totals['total_payload_upload'], 0)
|
||||
self.assertEqual(totals['total_payload_download'], 0)
|
||||
self.assertEqual(totals['total_download'], 0)
|
||||
# print_totals(totals)
|
||||
|
||||
@pytest.mark.gtkui
|
||||
|
|
|
@ -48,5 +48,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -28,8 +28,10 @@ class GtkUI(GtkPluginBase):
|
|||
self.core = client.toggle
|
||||
self.plugin = component.get('PluginManager')
|
||||
self.separator = self.plugin.add_toolbar_separator()
|
||||
self.button = self.plugin.add_toolbar_button(self._on_button_clicked, label='Pause Session',
|
||||
stock='gtk-media-pause', tooltip='Pause the session')
|
||||
self.button = self.plugin.add_toolbar_button(
|
||||
self._on_button_clicked, label='Pause Session',
|
||||
stock='gtk-media-pause', tooltip='Pause the session',
|
||||
)
|
||||
|
||||
def disable(self):
|
||||
component.get('PluginManager').remove_toolbar_button(self.button)
|
||||
|
|
|
@ -45,5 +45,5 @@ setup(
|
|||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
[deluge.plugin.web]
|
||||
%s = deluge.plugins.%s:WebUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 3),
|
||||
)
|
||||
|
|
|
@ -33,7 +33,7 @@ log = logging.getLogger(__name__)
|
|||
DEFAULT_PREFS = {
|
||||
'enabled': False,
|
||||
'ssl': False,
|
||||
'port': 8112
|
||||
'port': 8112,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ class GtkUI(GtkPluginBase):
|
|||
config = {
|
||||
'enabled': self.builder.get_object('enabled_checkbutton').get_active(),
|
||||
'ssl': self.builder.get_object('ssl_checkbutton').get_active(),
|
||||
'port': self.builder.get_object('port_spinbutton').get_value_as_int()
|
||||
'port': self.builder.get_object('port_spinbutton').get_value_as_int(),
|
||||
}
|
||||
client.webui.set_config(config)
|
||||
|
||||
|
@ -75,8 +75,10 @@ class GtkUI(GtkPluginBase):
|
|||
icon.set_padding(5, 5)
|
||||
hbox.pack_start(icon, False, False)
|
||||
|
||||
label = gtk.Label(_('The Deluge web interface is not installed, '
|
||||
'please install the\ninterface and try again'))
|
||||
label = gtk.Label(_(
|
||||
'The Deluge web interface is not installed, '
|
||||
'please install the\ninterface and try again',
|
||||
))
|
||||
label.set_alignment(0, 0.5)
|
||||
label.set_padding(5, 5)
|
||||
hbox.pack_start(label)
|
||||
|
|
|
@ -42,5 +42,5 @@ setup(
|
|||
%s = deluge.plugins.%s:CorePlugin
|
||||
[deluge.plugin.gtkui]
|
||||
%s = deluge.plugins.%s:GtkUIPlugin
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 2)
|
||||
""" % ((__plugin_name__, __plugin_name__.lower()) * 2),
|
||||
)
|
||||
|
|
|
@ -73,8 +73,8 @@ __all__ = ['dumps', 'loads']
|
|||
|
||||
py3 = sys.version_info[0] >= 3
|
||||
if py3:
|
||||
long = int # pylint: disable=redefined-builtin
|
||||
unicode = str # pylint: disable=redefined-builtin
|
||||
long = int # noqa: A001, pylint: disable=redefined-builtin
|
||||
unicode = str # noqa: A001, pylint: disable=redefined-builtin
|
||||
|
||||
def int2byte(c):
|
||||
return bytes([c])
|
||||
|
@ -434,9 +434,13 @@ def test():
|
|||
f1 = struct.unpack('!f', struct.pack('!f', 25.5))[0]
|
||||
f2 = struct.unpack('!f', struct.pack('!f', 29.3))[0]
|
||||
f3 = struct.unpack('!f', struct.pack('!f', -0.6))[0]
|
||||
ld = (({b'a': 15, b'bb': f1, b'ccc': f2, b'': (f3, (), False, True, b'')}, (b'a', 10**20),
|
||||
tuple(range(-100000, 100000)), b'b' * 31, b'b' * 62, b'b' * 64, 2**30, 2**33, 2**62,
|
||||
2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0),)
|
||||
ld = (
|
||||
(
|
||||
{b'a': 15, b'bb': f1, b'ccc': f2, b'': (f3, (), False, True, b'')}, (b'a', 10**20),
|
||||
tuple(range(-100000, 100000)), b'b' * 31, b'b' * 62, b'b' * 64, 2**30, 2**33, 2**62,
|
||||
2**64, 2**30, 2**33, 2**62, 2**64, False, False, True, -1, 2, 0,
|
||||
),
|
||||
)
|
||||
assert loads(dumps(ld)) == ld
|
||||
d = dict(zip(range(-100000, 100000), range(-100000, 100000)))
|
||||
d.update({b'a': 20, 20: 40, 40: 41, f1: f2, f2: f3, f3: False, False: True, True: False})
|
||||
|
@ -444,15 +448,15 @@ def test():
|
|||
assert loads(dumps(ld)) == ld
|
||||
ld = (b'', b'a' * 10, b'a' * 100, b'a' * 1000, b'a' * 10000, b'a' * 100000, b'a' * 1000000, b'a' * 10000000)
|
||||
assert loads(dumps(ld)) == ld
|
||||
ld = tuple([dict(zip(range(n), range(n))) for n in range(100)]) + (b'b',)
|
||||
ld = tuple(dict(zip(range(n), range(n))) for n in range(100)) + (b'b',)
|
||||
assert loads(dumps(ld)) == ld
|
||||
ld = tuple([dict(zip(range(n), range(-n, 0))) for n in range(100)]) + (b'b',)
|
||||
ld = tuple(dict(zip(range(n), range(-n, 0))) for n in range(100)) + (b'b',)
|
||||
assert loads(dumps(ld)) == ld
|
||||
ld = tuple([tuple(range(n)) for n in range(100)]) + (b'b',)
|
||||
ld = tuple(tuple(range(n)) for n in range(100)) + (b'b',)
|
||||
assert loads(dumps(ld)) == ld
|
||||
ld = tuple([b'a' * n for n in range(1000)]) + (b'b',)
|
||||
ld = tuple(b'a' * n for n in range(1000)) + (b'b',)
|
||||
assert loads(dumps(ld)) == ld
|
||||
ld = tuple([b'a' * n for n in range(1000)]) + (None, True, None)
|
||||
ld = tuple(b'a' * n for n in range(1000)) + (None, True, None)
|
||||
assert loads(dumps(ld)) == ld
|
||||
assert loads(dumps(None)) is None
|
||||
assert loads(dumps({None: None})) == {None: None}
|
||||
|
|
|
@ -20,10 +20,14 @@ 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(
|
||||
'-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')
|
||||
|
||||
|
@ -71,7 +75,7 @@ def create_plugin():
|
|||
'python_path': python_path,
|
||||
'url': options.url,
|
||||
'configdir': options.configdir,
|
||||
'current_year': datetime.utcnow().year
|
||||
'current_year': datetime.utcnow().year,
|
||||
}
|
||||
|
||||
filename = os.path.join(path, filename)
|
||||
|
|
|
@ -33,20 +33,34 @@ def is_float_digit(string):
|
|||
# set up command-line options
|
||||
parser = OptionParser()
|
||||
parser.add_option('--port', help='port for deluge backend host (default: 58846)', default='58846', dest='port')
|
||||
parser.add_option('--host', help='hostname of deluge backend to connect to (default: localhost)',
|
||||
default='localhost', dest='host')
|
||||
parser.add_option('--max_active_limit', dest='max_active_limit',
|
||||
help='sets the absolute maximum number of active torrents on the deluge backend')
|
||||
parser.add_option('--max_active_downloading', dest='max_active_downloading',
|
||||
help='sets the maximum number of active downloading torrents on the deluge backend')
|
||||
parser.add_option('--max_active_seeding', dest='max_active_seeding',
|
||||
help='sets the maximum number of active seeding torrents on the deluge backend')
|
||||
parser.add_option('--max_download_speed', help='sets the maximum global download speed on the deluge backend',
|
||||
dest='max_download_speed')
|
||||
parser.add_option('--max_upload_speed', help='sets the maximum global upload speed on the deluge backend',
|
||||
dest='max_upload_speed')
|
||||
parser.add_option('--debug', help='outputs debug information to the console', default=False, action='store_true',
|
||||
dest='debug')
|
||||
parser.add_option(
|
||||
'--host', help='hostname of deluge backend to connect to (default: localhost)',
|
||||
default='localhost', dest='host',
|
||||
)
|
||||
parser.add_option(
|
||||
'--max_active_limit', dest='max_active_limit',
|
||||
help='sets the absolute maximum number of active torrents on the deluge backend',
|
||||
)
|
||||
parser.add_option(
|
||||
'--max_active_downloading', dest='max_active_downloading',
|
||||
help='sets the maximum number of active downloading torrents on the deluge backend',
|
||||
)
|
||||
parser.add_option(
|
||||
'--max_active_seeding', dest='max_active_seeding',
|
||||
help='sets the maximum number of active seeding torrents on the deluge backend',
|
||||
)
|
||||
parser.add_option(
|
||||
'--max_download_speed', help='sets the maximum global download speed on the deluge backend',
|
||||
dest='max_download_speed',
|
||||
)
|
||||
parser.add_option(
|
||||
'--max_upload_speed', help='sets the maximum global upload speed on the deluge backend',
|
||||
dest='max_upload_speed',
|
||||
)
|
||||
parser.add_option(
|
||||
'--debug', help='outputs debug information to the console', default=False, action='store_true',
|
||||
dest='debug',
|
||||
)
|
||||
|
||||
# grab command-line options
|
||||
(options, args) = parser.parse_args()
|
||||
|
@ -80,7 +94,8 @@ if options.max_active_seeding:
|
|||
|
||||
if options.max_download_speed:
|
||||
if is_float_digit(options.max_download_speed) and (
|
||||
float(options.max_download_speed) >= 0.0 or float(options.max_download_speed) == -1.0):
|
||||
float(options.max_download_speed) >= 0.0 or float(options.max_download_speed) == -1.0
|
||||
):
|
||||
settings['max_download_speed'] = float(options.max_download_speed)
|
||||
else:
|
||||
sys.stderr.write('ERROR: Invalid max_download_speed parameter!\n')
|
||||
|
@ -88,7 +103,8 @@ if options.max_download_speed:
|
|||
|
||||
if options.max_upload_speed:
|
||||
if is_float_digit(options.max_upload_speed) and (
|
||||
float(options.max_upload_speed) >= 0.0 or float(options.max_upload_speed) == -1.0):
|
||||
float(options.max_upload_speed) >= 0.0 or float(options.max_upload_speed) == -1.0
|
||||
):
|
||||
settings['max_upload_speed'] = float(options.max_upload_speed)
|
||||
else:
|
||||
sys.stderr.write('ERROR: Invalid max_upload_speed parameter!\n')
|
||||
|
|
|
@ -25,9 +25,11 @@ class BaseTestCase(unittest.TestCase):
|
|||
def setUp(self): # NOQA: N803
|
||||
|
||||
if len(component._ComponentRegistry.components) != 0:
|
||||
warnings.warn('The component._ComponentRegistry.components is not empty on test setup.\n'
|
||||
'This is probably caused by another test that did not clean up after finishing!: %s' %
|
||||
component._ComponentRegistry.components)
|
||||
warnings.warn(
|
||||
'The component._ComponentRegistry.components is not empty on test setup.\n'
|
||||
'This is probably caused by another test that did not clean up after finishing!: %s' %
|
||||
component._ComponentRegistry.components,
|
||||
)
|
||||
d = maybeDeferred(self.set_up)
|
||||
|
||||
def on_setup_error(error):
|
||||
|
|
|
@ -209,8 +209,10 @@ class ProcessOutputHandler(protocol.ProcessProtocol):
|
|||
print('\n%s' % prefixed)
|
||||
|
||||
|
||||
def start_core(listen_port=58846, logfile=None, timeout=10, timeout_msg=None,
|
||||
custom_script='', print_stdout=True, print_stderr=True, extra_callbacks=None):
|
||||
def start_core(
|
||||
listen_port=58846, logfile=None, timeout=10, timeout_msg=None,
|
||||
custom_script='', print_stdout=True, print_stderr=True, extra_callbacks=None,
|
||||
):
|
||||
"""Start the deluge core as a daemon.
|
||||
|
||||
Args:
|
||||
|
@ -242,7 +244,7 @@ try:
|
|||
daemon = deluge.core.daemon_entry.start_daemon(skip_start=True)
|
||||
%s
|
||||
daemon.start()
|
||||
except:
|
||||
except Exception:
|
||||
import traceback
|
||||
sys.stderr.write('Exception raised:\\n %%s' %% traceback.format_exc())
|
||||
""" % (config_directory, listen_port, custom_script)
|
||||
|
@ -254,11 +256,17 @@ except:
|
|||
# Specify the triggers for daemon log output
|
||||
default_core_cb['triggers'] = [
|
||||
{'expr': 'Finished loading ', 'value': lambda reader, data, data_all: reader},
|
||||
{'expr': 'Could not listen on localhost:%d' % (listen_port), 'type': 'errback', # Error from libtorrent
|
||||
'value': lambda reader, data, data_all: CannotListenError('localhost', listen_port,
|
||||
'Could not start deluge test client!\n%s' % data)},
|
||||
{'expr': 'Traceback', 'type': 'errback',
|
||||
'value': lambda reader, data, data_all: DelugeError('Traceback found when starting daemon:\n%s' % data)}
|
||||
{
|
||||
'expr': 'Could not listen on localhost:%d' % (listen_port), 'type': 'errback', # Error from libtorrent
|
||||
'value': lambda reader, data, data_all: CannotListenError(
|
||||
'localhost', listen_port,
|
||||
'Could not start deluge test client!\n%s' % data,
|
||||
),
|
||||
},
|
||||
{
|
||||
'expr': 'Traceback', 'type': 'errback',
|
||||
'value': lambda reader, data, data_all: DelugeError('Traceback found when starting daemon:\n%s' % data),
|
||||
},
|
||||
]
|
||||
|
||||
callbacks.append(default_core_cb)
|
||||
|
|
|
@ -36,8 +36,10 @@ class DaemonBase(object):
|
|||
return d
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def start_core(self, arg, custom_script='', logfile='', print_stdout=True, print_stderr=True, timeout=5,
|
||||
port_range=10, extra_callbacks=None):
|
||||
def start_core(
|
||||
self, arg, custom_script='', logfile='', print_stdout=True, print_stderr=True, timeout=5,
|
||||
port_range=10, extra_callbacks=None,
|
||||
):
|
||||
if logfile == '':
|
||||
logfile = 'daemon_%s.log' % self.id()
|
||||
|
||||
|
@ -52,12 +54,14 @@ class DaemonBase(object):
|
|||
|
||||
for dummy in range(port_range):
|
||||
try:
|
||||
d, self.core = common.start_core(listen_port=self.listen_port, logfile=logfile,
|
||||
timeout=timeout, timeout_msg='Timeout!',
|
||||
custom_script=custom_script,
|
||||
print_stdout=print_stdout,
|
||||
print_stderr=print_stderr,
|
||||
extra_callbacks=extra_callbacks)
|
||||
d, self.core = common.start_core(
|
||||
listen_port=self.listen_port, logfile=logfile,
|
||||
timeout=timeout, timeout_msg='Timeout!',
|
||||
custom_script=custom_script,
|
||||
print_stdout=print_stdout,
|
||||
print_stderr=print_stderr,
|
||||
extra_callbacks=extra_callbacks,
|
||||
)
|
||||
yield d
|
||||
except CannotListenError as ex:
|
||||
exception_error = ex
|
||||
|
|
|
@ -26,5 +26,5 @@ class AuthManagerTestCase(BaseTestCase):
|
|||
def test_authorize(self):
|
||||
self.assertEqual(
|
||||
self.auth.authorize(*get_localhost_auth()),
|
||||
AUTH_LEVEL_ADMIN
|
||||
AUTH_LEVEL_ADMIN,
|
||||
)
|
||||
|
|
|
@ -36,8 +36,10 @@ class NoVersionSendingDaemonSSLProxy(DaemonSSLProxy):
|
|||
|
||||
class NoVersionSendingClient(Client):
|
||||
|
||||
def connect(self, host='127.0.0.1', port=58846, username='', password='',
|
||||
skip_authentication=False):
|
||||
def connect(
|
||||
self, host='127.0.0.1', port=58846, username='', password='',
|
||||
skip_authentication=False,
|
||||
):
|
||||
self._daemon_proxy = NoVersionSendingDaemonSSLProxy()
|
||||
self._daemon_proxy.set_disconnect_callback(self.__on_disconnect)
|
||||
|
||||
|
@ -116,7 +118,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
|
|||
def on_failure(failure):
|
||||
self.assertEqual(
|
||||
failure.trap(error.BadLoginError),
|
||||
error.BadLoginError
|
||||
error.BadLoginError,
|
||||
)
|
||||
self.assertEqual(failure.value.message, 'Password does not match')
|
||||
self.addCleanup(client.disconnect)
|
||||
|
@ -131,7 +133,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
|
|||
def on_failure(failure):
|
||||
self.assertEqual(
|
||||
failure.trap(error.BadLoginError),
|
||||
error.BadLoginError
|
||||
error.BadLoginError,
|
||||
)
|
||||
self.assertEqual(failure.value.message, 'Username does not exist')
|
||||
self.addCleanup(client.disconnect)
|
||||
|
@ -146,7 +148,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
|
|||
def on_failure(failure):
|
||||
self.assertEqual(
|
||||
failure.trap(error.AuthenticationRequired),
|
||||
error.AuthenticationRequired
|
||||
error.AuthenticationRequired,
|
||||
)
|
||||
self.assertEqual(failure.value.username, username)
|
||||
self.addCleanup(client.disconnect)
|
||||
|
@ -179,13 +181,13 @@ class ClientTestCase(BaseTestCase, DaemonBase):
|
|||
username, password = get_localhost_auth()
|
||||
no_version_sending_client = NoVersionSendingClient()
|
||||
d = no_version_sending_client.connect(
|
||||
'localhost', self.listen_port, username=username, password=password
|
||||
'localhost', self.listen_port, username=username, password=password,
|
||||
)
|
||||
|
||||
def on_failure(failure):
|
||||
self.assertEqual(
|
||||
failure.trap(error.IncompatibleClient),
|
||||
error.IncompatibleClient
|
||||
error.IncompatibleClient,
|
||||
)
|
||||
self.addCleanup(no_version_sending_client.disconnect)
|
||||
|
||||
|
|
|
@ -116,17 +116,19 @@ class CommonTestCase(unittest.TestCase):
|
|||
|
||||
def test_parse_human_size(self):
|
||||
from deluge.common import parse_human_size
|
||||
sizes = [('1', 1),
|
||||
('10 bytes', 10),
|
||||
('2048 bytes', 2048),
|
||||
('1MiB', 2**(10 * 2)),
|
||||
('1 MiB', 2**(10 * 2)),
|
||||
('1 GiB', 2**(10 * 3)),
|
||||
('1 GiB', 2**(10 * 3)),
|
||||
('1M', 10**6),
|
||||
('1MB', 10**6),
|
||||
('1 GB', 10**9),
|
||||
('1 TB', 10**12)]
|
||||
sizes = [
|
||||
('1', 1),
|
||||
('10 bytes', 10),
|
||||
('2048 bytes', 2048),
|
||||
('1MiB', 2**(10 * 2)),
|
||||
('1 MiB', 2**(10 * 2)),
|
||||
('1 GiB', 2**(10 * 3)),
|
||||
('1 GiB', 2**(10 * 3)),
|
||||
('1M', 10**6),
|
||||
('1MB', 10**6),
|
||||
('1 GB', 10**9),
|
||||
('1 TB', 10**12),
|
||||
]
|
||||
|
||||
for human_size, byte_size in sizes:
|
||||
parsed = parse_human_size(human_size)
|
||||
|
@ -135,7 +137,8 @@ class CommonTestCase(unittest.TestCase):
|
|||
def test_archive_files(self):
|
||||
arc_filelist = [
|
||||
get_test_data_file('test.torrent'),
|
||||
get_test_data_file('deluge.png')]
|
||||
get_test_data_file('deluge.png'),
|
||||
]
|
||||
arc_filepath = archive_files('test-arc', arc_filelist)
|
||||
|
||||
with tarfile.open(arc_filepath, 'r') as tar:
|
||||
|
|
|
@ -137,7 +137,8 @@ class ComponentTestClass(BaseTestCase):
|
|||
self.assertRaises(
|
||||
component.ComponentAlreadyRegistered,
|
||||
ComponentTester,
|
||||
'test_register_exception_c1')
|
||||
'test_register_exception_c1',
|
||||
)
|
||||
|
||||
def test_stop_component(self):
|
||||
def on_stop(result, c):
|
||||
|
@ -230,11 +231,15 @@ class ComponentTestClass(BaseTestCase):
|
|||
result = yield component.start()
|
||||
self.assertEqual(
|
||||
[(result[0][0], result[0][1].value)],
|
||||
[(defer.FAILURE,
|
||||
[(
|
||||
defer.FAILURE,
|
||||
component.ComponentException(
|
||||
'Trying to start component "%s" but it is '
|
||||
'not in a stopped state. Current state: %s' %
|
||||
('test_pause_c1', 'Paused'), ''))])
|
||||
('test_pause_c1', 'Paused'), '',
|
||||
),
|
||||
)],
|
||||
)
|
||||
|
||||
def test_shutdown(self):
|
||||
def on_shutdown(result, c1):
|
||||
|
|
|
@ -75,8 +75,10 @@ class TopLevelResource(Resource):
|
|||
self.putChild('cookie', CookieResource())
|
||||
self.putChild('partial', PartialDownload())
|
||||
self.putChild('redirect', RedirectResource())
|
||||
self.putChild('ubuntu-9.04-desktop-i386.iso.torrent',
|
||||
File(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent')))
|
||||
self.putChild(
|
||||
'ubuntu-9.04-desktop-i386.iso.torrent',
|
||||
File(common.get_test_data_file('ubuntu-9.04-desktop-i386.iso.torrent')),
|
||||
)
|
||||
|
||||
|
||||
class CoreTestCase(BaseTestCase):
|
||||
|
@ -152,7 +154,7 @@ class CoreTestCase(BaseTestCase):
|
|||
from deluge.bencode import bdecode, bencode
|
||||
with open(filename, 'rb') as _file:
|
||||
info_hash = sha(bencode(bdecode(_file.read())[b'info'])).hexdigest()
|
||||
self.assertEquals(torrent_id, info_hash)
|
||||
self.assertEqual(torrent_id, info_hash)
|
||||
|
||||
def test_add_torrent_file_invalid_filedump(self):
|
||||
options = {}
|
||||
|
|
|
@ -31,8 +31,10 @@ class ErrorTestCase(unittest.TestCase):
|
|||
def test_incompatible_client(self):
|
||||
version = '1.3.6'
|
||||
e = deluge.error.IncompatibleClient(version)
|
||||
self.assertEqual(str(e), 'Your deluge client is not compatible with the daemon. \
|
||||
Please upgrade your client to %s' % version)
|
||||
self.assertEqual(
|
||||
str(e), 'Your deluge client is not compatible with the daemon. \
|
||||
Please upgrade your client to %s' % version,
|
||||
)
|
||||
|
||||
def test_not_authorized_error(self):
|
||||
current_level = 5
|
||||
|
|
|
@ -85,8 +85,10 @@ class FilesTabTestCase(BaseTestCase):
|
|||
return _verify_treestore(treestore.get_iter_root(), tree)
|
||||
|
||||
def test_files_tab(self):
|
||||
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14})
|
||||
self.filestab.files_list[self.t_id] = (
|
||||
{'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
|
||||
)
|
||||
self.filestab.update_files()
|
||||
self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '2/test_100.txt')
|
||||
|
||||
|
@ -96,8 +98,10 @@ class FilesTabTestCase(BaseTestCase):
|
|||
self.assertTrue(ret)
|
||||
|
||||
def test_files_tab2(self):
|
||||
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14})
|
||||
self.filestab.files_list[self.t_id] = (
|
||||
{'index': 0, 'path': '1/1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
|
||||
)
|
||||
self.filestab.update_files()
|
||||
self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/1/test_100.txt')
|
||||
|
||||
|
@ -107,8 +111,10 @@ class FilesTabTestCase(BaseTestCase):
|
|||
self.assertTrue(ret)
|
||||
|
||||
def test_files_tab3(self):
|
||||
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14})
|
||||
self.filestab.files_list[self.t_id] = (
|
||||
{'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': 'test_100.txt', 'offset': 13, 'size': 14},
|
||||
)
|
||||
self.filestab.update_files()
|
||||
self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/test_100.txt')
|
||||
|
||||
|
@ -118,20 +124,30 @@ class FilesTabTestCase(BaseTestCase):
|
|||
self.assertTrue(ret)
|
||||
|
||||
def test_files_tab4(self):
|
||||
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': '1/test_100.txt', 'offset': 13, 'size': 14})
|
||||
self.filestab.files_list[self.t_id] = (
|
||||
{'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': '1/test_100.txt', 'offset': 13, 'size': 14},
|
||||
)
|
||||
self.filestab.update_files()
|
||||
self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/2/test_100.txt')
|
||||
|
||||
ret = self.verify_treestore(self.filestab.treestore, [['1/', [['2/', [['test_100.txt']]],
|
||||
['test_10.txt']]]])
|
||||
ret = self.verify_treestore(
|
||||
self.filestab.treestore, [[
|
||||
'1/', [
|
||||
['2/', [['test_100.txt']]],
|
||||
['test_10.txt'],
|
||||
],
|
||||
]],
|
||||
)
|
||||
if not ret:
|
||||
self.print_treestore('Treestore not expected:', self.filestab.treestore)
|
||||
self.assertTrue(ret)
|
||||
|
||||
def test_files_tab5(self):
|
||||
self.filestab.files_list[self.t_id] = ({'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': '2/test_100.txt', 'offset': 13, 'size': 14})
|
||||
self.filestab.files_list[self.t_id] = (
|
||||
{'index': 0, 'path': '1/test_10.txt', 'offset': 0, 'size': 13},
|
||||
{'index': 1, 'path': '2/test_100.txt', 'offset': 13, 'size': 14},
|
||||
)
|
||||
self.filestab.update_files()
|
||||
self.filestab._on_torrentfilerenamed_event(self.t_id, self.index, '1/test_100.txt')
|
||||
|
||||
|
|
|
@ -43,8 +43,10 @@ class RenameResource(Resource):
|
|||
def render(self, request):
|
||||
filename = request.args.get('filename', ['renamed_file'])[0]
|
||||
request.setHeader(b'Content-Type', b'text/plain')
|
||||
request.setHeader(b'Content-Disposition', b'attachment; filename=' +
|
||||
filename)
|
||||
request.setHeader(
|
||||
b'Content-Disposition', b'attachment; filename=' +
|
||||
filename,
|
||||
)
|
||||
return b'This file should be called ' + filename
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class JSONBase(BaseTestCase, DaemonBase):
|
|||
def connect_client(self, *args, **kwargs):
|
||||
return client.connect(
|
||||
'localhost', self.listen_port, username=kwargs.get('user', ''),
|
||||
password=kwargs.get('password', '')
|
||||
password=kwargs.get('password', ''),
|
||||
)
|
||||
|
||||
def disconnect_client(self, *args):
|
||||
|
@ -219,18 +219,24 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
|
|||
daemon.rpcserver.register_object(test)
|
||||
"""
|
||||
from twisted.internet.defer import Deferred
|
||||
extra_callback = {'deferred': Deferred(), 'types': ['stderr'],
|
||||
'timeout': 10,
|
||||
'triggers': [{'expr': 'in test_raise_error',
|
||||
'value': lambda reader, data, data_all: 'Test'}]}
|
||||
extra_callback = {
|
||||
'deferred': Deferred(), 'types': ['stderr'],
|
||||
'timeout': 10,
|
||||
'triggers': [{
|
||||
'expr': 'in test_raise_error',
|
||||
'value': lambda reader, data, data_all: 'Test',
|
||||
}],
|
||||
}
|
||||
|
||||
def on_test_raise(*args):
|
||||
self.assertTrue('Unhandled error in Deferred:' in self.core.stderr_out)
|
||||
self.assertTrue('in test_raise_error' in self.core.stderr_out)
|
||||
|
||||
extra_callback['deferred'].addCallback(on_test_raise)
|
||||
d.addCallback(self.start_core, custom_script=custom_script, print_stdout=False, print_stderr=False,
|
||||
timeout=5, extra_callbacks=[extra_callback])
|
||||
d.addCallback(
|
||||
self.start_core, custom_script=custom_script, print_stdout=False, print_stderr=False,
|
||||
timeout=5, extra_callbacks=[extra_callback],
|
||||
)
|
||||
d.addCallbacks(self.connect_client, self.terminate_core)
|
||||
return d
|
||||
|
||||
|
@ -257,7 +263,8 @@ class JSONRequestFailedTestCase(JSONBase, WebServerMockBase):
|
|||
self.assertEqual(
|
||||
response['error']['message'],
|
||||
'Failure: [Failure instance: Traceback (failure with no frames):'
|
||||
" <class 'deluge.error.DelugeError'>: DelugeERROR\n]")
|
||||
" <class 'deluge.error.DelugeError'>: DelugeERROR\n]",
|
||||
)
|
||||
self.assertEqual(response['error']['code'], 4)
|
||||
|
||||
request.write = write
|
||||
|
|
|
@ -31,8 +31,10 @@ class TorrentTestCase(BaseTestCase):
|
|||
|
||||
def setup_config(self):
|
||||
config_dir = common.set_tmp_config_dir()
|
||||
core_config = deluge.config.Config('core.conf', defaults=deluge.core.preferencesmanager.DEFAULT_PREFS,
|
||||
config_dir=config_dir)
|
||||
core_config = deluge.config.Config(
|
||||
'core.conf', defaults=deluge.core.preferencesmanager.DEFAULT_PREFS,
|
||||
config_dir=config_dir,
|
||||
)
|
||||
core_config.save()
|
||||
|
||||
def set_up(self):
|
||||
|
@ -77,8 +79,10 @@ class TorrentTestCase(BaseTestCase):
|
|||
return atp
|
||||
|
||||
def test_set_prioritize_first_last_pieces(self):
|
||||
piece_indexes = [0, 1, 50, 51, 52, 110, 111, 112, 113, 200, 201, 202, 212,
|
||||
213, 214, 215, 216, 217, 457, 458, 459, 460, 461, 462]
|
||||
piece_indexes = [
|
||||
0, 1, 50, 51, 52, 110, 111, 112, 113, 200, 201, 202, 212,
|
||||
213, 214, 215, 216, 217, 457, 458, 459, 460, 461, 462,
|
||||
]
|
||||
self.run_test_set_prioritize_first_last_pieces('dir_with_6_files.torrent', piece_indexes)
|
||||
|
||||
def run_test_set_prioritize_first_last_pieces(self, torrent_file, prioritized_piece_indexes):
|
||||
|
@ -169,7 +173,7 @@ class TorrentTestCase(BaseTestCase):
|
|||
'num_seeds': 16777215, 'sequential_download': 0, 'announce_to_trackers': 1,
|
||||
'peers': '\n\x00\x02\x0f=\xc6SC\x17]\xd8}\x7f\x00\x00\x01=\xc6', 'finished_time': 13399,
|
||||
'last_upload': 13399, 'trackers': [[]], 'super_seeding': 0,
|
||||
'file sizes': [[512000, 1411826586]], 'last_download': 13399
|
||||
'file sizes': [[512000, 1411826586]], 'last_download': 13399,
|
||||
}
|
||||
torrent_state = TorrentState(
|
||||
torrent_id='2dc5d0e71a66fe69649a640d39cb00a259704973',
|
||||
|
@ -185,7 +189,8 @@ class TorrentTestCase(BaseTestCase):
|
|||
filedump = _file.read()
|
||||
resume_data = utf8_encode_structure(resume_data)
|
||||
torrent_id = self.core.torrentmanager.add(
|
||||
state=torrent_state, filedump=filedump, resume_data=lt.bencode(resume_data))
|
||||
state=torrent_state, filedump=filedump, resume_data=lt.bencode(resume_data),
|
||||
)
|
||||
torrent = self.core.torrentmanager.torrents[torrent_id]
|
||||
|
||||
def assert_resume_data():
|
||||
|
|
|
@ -42,26 +42,30 @@ setup_translations()
|
|||
@pytest.mark.gtkui
|
||||
class TorrentviewTestCase(BaseTestCase):
|
||||
|
||||
default_column_index = ['filter', 'torrent_id', 'dirty', '#',
|
||||
'Name',
|
||||
'Size', 'Downloaded', 'Uploaded', 'Remaining',
|
||||
'Progress',
|
||||
'Seeds', 'Peers', 'Seeds:Peers',
|
||||
'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit',
|
||||
'ETA', 'Ratio', 'Avail',
|
||||
'Added', 'Completed', 'Complete Seen',
|
||||
'Tracker', 'Download Folder', 'Owner', 'Shared']
|
||||
default_liststore_columns = [bool, str, bool, int,
|
||||
str, str, # Name
|
||||
TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64,
|
||||
float, str, # Progress
|
||||
int, int, int, int, float, # Seeds, Peers
|
||||
int, int, float, float,
|
||||
int, float, float, # ETA, Ratio, Avail
|
||||
int, int, int,
|
||||
str, str, # Tracker
|
||||
str, str,
|
||||
bool] # shared
|
||||
default_column_index = [
|
||||
'filter', 'torrent_id', 'dirty', '#',
|
||||
'Name',
|
||||
'Size', 'Downloaded', 'Uploaded', 'Remaining',
|
||||
'Progress',
|
||||
'Seeds', 'Peers', 'Seeds:Peers',
|
||||
'Down Speed', 'Up Speed', 'Down Limit', 'Up Limit',
|
||||
'ETA', 'Ratio', 'Avail',
|
||||
'Added', 'Completed', 'Complete Seen',
|
||||
'Tracker', 'Download Folder', 'Owner', 'Shared',
|
||||
]
|
||||
default_liststore_columns = [
|
||||
bool, str, bool, int,
|
||||
str, str, # Name
|
||||
TYPE_UINT64, TYPE_UINT64, TYPE_UINT64, TYPE_UINT64,
|
||||
float, str, # Progress
|
||||
int, int, int, int, float, # Seeds, Peers
|
||||
int, int, float, float,
|
||||
int, float, float, # ETA, Ratio, Avail
|
||||
int, int, int,
|
||||
str, str, # Tracker
|
||||
str, str,
|
||||
bool,
|
||||
] # shared
|
||||
|
||||
def set_up(self):
|
||||
if libs_available is False:
|
||||
|
@ -89,10 +93,14 @@ class TorrentviewTestCase(BaseTestCase):
|
|||
# Add a text column
|
||||
test_col = 'Test column'
|
||||
self.torrentview.add_text_column(test_col, status_field=['label'])
|
||||
self.assertEqual(len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 1)
|
||||
self.assertEqual(len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 1)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 1,
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 1,
|
||||
)
|
||||
self.assertEqual(self.torrentview.column_index[-1], test_col)
|
||||
self.assertEqual(self.torrentview.columns[test_col].column_indices, [32])
|
||||
|
||||
|
@ -106,10 +114,14 @@ class TorrentviewTestCase(BaseTestCase):
|
|||
test_col2 = 'Test column2'
|
||||
self.torrentview.add_text_column(test_col2, status_field=['label2'])
|
||||
|
||||
self.assertEqual(len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 2)
|
||||
self.assertEqual(len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 2)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 2,
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 2,
|
||||
)
|
||||
# test_col
|
||||
self.assertEqual(self.torrentview.column_index[-2], test_col)
|
||||
self.assertEqual(self.torrentview.columns[test_col].column_indices, [32])
|
||||
|
@ -140,10 +152,14 @@ class TorrentviewTestCase(BaseTestCase):
|
|||
|
||||
# Remove test_col
|
||||
self.torrentview.remove_column(test_col)
|
||||
self.assertEqual(len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 1)
|
||||
self.assertEqual(len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 1)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 1,
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 1,
|
||||
)
|
||||
self.assertEqual(self.torrentview.column_index[-1], test_col2)
|
||||
self.assertEqual(self.torrentview.columns[test_col2].column_indices, [32])
|
||||
|
||||
|
@ -159,10 +175,14 @@ class TorrentviewTestCase(BaseTestCase):
|
|||
# Add a column with multiple column types
|
||||
test_col3 = 'Test column3'
|
||||
self.torrentview.add_progress_column(test_col3, status_field=['progress', 'label3'], col_types=[float, str])
|
||||
self.assertEqual(len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 2)
|
||||
self.assertEqual(len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 1)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.liststore_columns),
|
||||
len(TorrentviewTestCase.default_liststore_columns) + 2,
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.torrentview.column_index),
|
||||
len(TorrentviewTestCase.default_column_index) + 1,
|
||||
)
|
||||
self.assertEqual(self.torrentview.column_index[-1], test_col3)
|
||||
self.assertEqual(self.torrentview.columns[test_col3].column_indices, [32, 33])
|
||||
|
||||
|
|
|
@ -118,10 +118,12 @@ class DelugeTransferProtocolTestCase(unittest.TestCase):
|
|||
"""
|
||||
self.transfer = TransferTestClass()
|
||||
self.msg1 = (0, 1, {'key_int': 1242429423}, {'key_str': b'some string'}, {'key_bool': True})
|
||||
self.msg2 = (2, 3, {'key_float': 12424.29423},
|
||||
{'key_unicode': 'some string'},
|
||||
{'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}},
|
||||
{'keylist': [4, '5', 6.7]})
|
||||
self.msg2 = (
|
||||
2, 3, {'key_float': 12424.29423},
|
||||
{'key_unicode': 'some string'},
|
||||
{'key_dict_with_tuple': {'key_tuple': (1, 2, 3)}},
|
||||
{'keylist': [4, '5', 6.7]},
|
||||
)
|
||||
|
||||
self.msg1_expected_compressed_base64 = 'RAAAADF4nDvKwJjenp1aGZ+ZV+Lgxfv9PYRXXFLU'\
|
||||
'XZyfm6oAZGTmpad3gAST8vNznAEAJhSQ'
|
||||
|
|
|
@ -33,11 +33,23 @@ class UICommonTestCase(unittest.TestCase):
|
|||
ti = TorrentInfo(filename)
|
||||
|
||||
files = ti.files_tree['unicode_filenames']
|
||||
self.assertTrue((b'\xe3\x83\x86\xe3\x82\xaf\xe3\x82\xb9\xe3\x83\xbb\xe3\x83'
|
||||
b'\x86\xe3\x82\xaf\xe3\x82\xb5\xe3\x83\xb3.mkv').decode('utf8') in files)
|
||||
self.assertTrue((b'\xd0\x9c\xd0\xb8\xd1\x85\xd0\xb0\xd0\xb8\xd0\xbb \xd0\x93'
|
||||
b'\xd0\xbe\xd1\x80\xd0\xb1\xd0\xb0\xd1\x87\xd1\x91\xd0\xb2.mkv').decode('utf8') in files)
|
||||
self.assertTrue(
|
||||
(
|
||||
b'\xe3\x83\x86\xe3\x82\xaf\xe3\x82\xb9\xe3\x83\xbb\xe3\x83'
|
||||
b'\x86\xe3\x82\xaf\xe3\x82\xb5\xe3\x83\xb3.mkv'
|
||||
).decode('utf8') in files,
|
||||
)
|
||||
self.assertTrue(
|
||||
(
|
||||
b'\xd0\x9c\xd0\xb8\xd1\x85\xd0\xb0\xd0\xb8\xd0\xbb \xd0\x93'
|
||||
b'\xd0\xbe\xd1\x80\xd0\xb1\xd0\xb0\xd1\x87\xd1\x91\xd0\xb2.mkv'
|
||||
).decode('utf8') in files,
|
||||
)
|
||||
self.assertTrue(b"Alisher ibn G'iyosiddin Navoiy.mkv".decode('utf8') in files)
|
||||
self.assertTrue(b'Ascii title.mkv'.decode('utf8') in files)
|
||||
self.assertTrue((b'\xe0\xa6\xb8\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x81\xe0\xa6\xae\xe0\xa6\xbe'
|
||||
b'\xe0\xa6\xb0 \xe0\xa6\xb0\xe0\xa6\xbe\xe0\xa7\x9f.mkv').decode('utf8') in files)
|
||||
self.assertTrue(
|
||||
(
|
||||
b'\xe0\xa6\xb8\xe0\xa7\x81\xe0\xa6\x95\xe0\xa7\x81\xe0\xa6\xae\xe0\xa6\xbe'
|
||||
b'\xe0\xa6\xb0 \xe0\xa6\xb0\xe0\xa6\xbe\xe0\xa7\x9f.mkv'
|
||||
).decode('utf8') in files,
|
||||
)
|
||||
|
|
|
@ -60,7 +60,7 @@ class StringFileDescriptor(object):
|
|||
class UIBaseTestCase(object):
|
||||
|
||||
def __init__(self):
|
||||
self.var = dict()
|
||||
self.var = {}
|
||||
|
||||
def set_up(self):
|
||||
common.set_tmp_config_dir()
|
||||
|
@ -339,8 +339,10 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
|
|||
@defer.inlineCallbacks
|
||||
def test_console_command_status(self):
|
||||
username, password = get_localhost_auth()
|
||||
self.patch(sys, 'argv', self.var['sys_arg_cmd'] + ['--port'] + ['58900'] + ['--username'] +
|
||||
[username] + ['--password'] + [password] + ['status'])
|
||||
self.patch(
|
||||
sys, 'argv', self.var['sys_arg_cmd'] + ['--port'] + ['58900'] + ['--username'] +
|
||||
[username] + ['--password'] + [password] + ['status'],
|
||||
)
|
||||
fd = StringFileDescriptor(sys.stdout)
|
||||
self.patch(sys, 'stdout', fd)
|
||||
|
||||
|
|
|
@ -72,8 +72,8 @@ class WebAPITestCase(WebServerTestBase):
|
|||
'233f23632af0a74748bc5dd1d8717564748877baa16420e6898e17e8aa365e6e': {
|
||||
'login': 'skrot',
|
||||
'expires': 1460030877.0,
|
||||
'level': 10
|
||||
}
|
||||
'level': 10,
|
||||
},
|
||||
}
|
||||
self.deluge_web.web_api.set_config(config)
|
||||
web_config = component.get('DelugeWeb').config.config
|
||||
|
@ -147,7 +147,10 @@ class WebAPITestCase(WebServerTestBase):
|
|||
ret['contents'], {
|
||||
'azcvsupdater_2.6.2.jar': {
|
||||
'priority': 4, 'index': 0, 'offset': 0, 'progress': 0.0, 'path':
|
||||
'azcvsupdater_2.6.2.jar', 'type': 'file', 'size': 307949}})
|
||||
'azcvsupdater_2.6.2.jar', 'type': 'file', 'size': 307949,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_download_torrent_from_url(self):
|
||||
|
@ -170,7 +173,10 @@ class WebAPITestCase(WebServerTestBase):
|
|||
d = yield agent.request(
|
||||
b'POST',
|
||||
b'http://127.0.0.1:%s/json' % self.webserver_listen_port,
|
||||
Headers({b'User-Agent': [b'Twisted Web Client Example'],
|
||||
b'Content-Type': [b'application/json']}),
|
||||
FileBodyProducer(BytesIO(bad_body)))
|
||||
Headers({
|
||||
b'User-Agent': [b'Twisted Web Client Example'],
|
||||
b'Content-Type': [b'application/json'],
|
||||
}),
|
||||
FileBodyProducer(BytesIO(bad_body)),
|
||||
)
|
||||
yield d
|
||||
|
|
|
@ -42,12 +42,16 @@ class WebServerTestCase(WebServerTestBase, WebServerMockBase):
|
|||
# UnicodeDecodeError: 'utf8' codec can't decode byte 0xe5 in position 0: invalid continuation byte
|
||||
filename = get_test_data_file('filehash_field.torrent')
|
||||
input_file = '{"params": ["%s"], "method": "web.get_torrent_info", "id": 22}' % filename
|
||||
headers = {'User-Agent': ['Twisted Web Client Example'],
|
||||
'Content-Type': ['application/json']}
|
||||
headers = {
|
||||
'User-Agent': ['Twisted Web Client Example'],
|
||||
'Content-Type': ['application/json'],
|
||||
}
|
||||
url = 'http://127.0.0.1:%s/json' % self.webserver_listen_port
|
||||
|
||||
d = yield agent.request(b'POST', url.encode('utf-8'), Headers(utf8_encode_structure(headers)),
|
||||
FileBodyProducer(BytesIO(input_file.encode('utf-8'))))
|
||||
d = yield agent.request(
|
||||
b'POST', url.encode('utf-8'), Headers(utf8_encode_structure(headers)),
|
||||
FileBodyProducer(BytesIO(input_file.encode('utf-8'))),
|
||||
)
|
||||
try:
|
||||
body = yield twisted.web.client.readBody(d)
|
||||
except AttributeError:
|
||||
|
|
|
@ -28,12 +28,14 @@ class _Reporter(object):
|
|||
self.klass = klass
|
||||
|
||||
|
||||
deluge = _Reporter('Deluge reporter that suppresses Stacktrace from TODO tests',
|
||||
'twisted.plugins.delugereporter',
|
||||
description='Deluge Reporter',
|
||||
longOpt='deluge-reporter',
|
||||
shortOpt=None,
|
||||
klass='DelugeReporter')
|
||||
deluge = _Reporter(
|
||||
'Deluge reporter that suppresses Stacktrace from TODO tests',
|
||||
'twisted.plugins.delugereporter',
|
||||
description='Deluge Reporter',
|
||||
longOpt='deluge-reporter',
|
||||
shortOpt=None,
|
||||
klass='DelugeReporter',
|
||||
)
|
||||
|
||||
|
||||
class DelugeReporter(TreeReporter):
|
||||
|
|
|
@ -100,7 +100,7 @@ class Win32IcoFile(object):
|
|||
|
||||
def sizes(self):
|
||||
"""Get a list of all available icon sizes and color depths."""
|
||||
return set((h['width'], h['height']) for h in self.entry)
|
||||
return {(h['width'], h['height']) for h in self.entry}
|
||||
|
||||
def get_image(self, size, bpp=False):
|
||||
"""Get an image from the icon
|
||||
|
@ -171,7 +171,7 @@ class Win32IcoFile(object):
|
|||
im.size, # (w, h)
|
||||
alpha_bytes, # source chars
|
||||
'raw', # raw decoder
|
||||
('L', 0, -1) # 8bpp inverted, unpadded, reversed
|
||||
('L', 0, -1), # 8bpp inverted, unpadded, reversed
|
||||
)
|
||||
|
||||
# apply mask image as alpha channel
|
||||
|
@ -198,7 +198,7 @@ class Win32IcoFile(object):
|
|||
im.size, # (w, h)
|
||||
mask_data, # source chars
|
||||
'raw', # raw decoder
|
||||
('1;I', w // 8, -1) # 1bpp inverted, padded, reversed
|
||||
('1;I', w // 8, -1), # 1bpp inverted, padded, reversed
|
||||
)
|
||||
|
||||
# now we have two images, im is XOR image and mask is AND image
|
||||
|
@ -215,7 +215,8 @@ class Win32IcoFile(object):
|
|||
def __repr__(self):
|
||||
s = 'Microsoft Icon: %d images (max %dx%d %dbpp)' % (
|
||||
len(self.entry), self.entry[0]['width'], self.entry[0]['height'],
|
||||
self.entry[0]['bpp'])
|
||||
self.entry[0]['bpp'],
|
||||
)
|
||||
return s
|
||||
# end Win32IcoFile
|
||||
|
||||
|
|
|
@ -170,26 +170,48 @@ class BaseArgParser(argparse.ArgumentParser):
|
|||
self.process_arg_group = False
|
||||
self.group = self.add_argument_group(_('Common Options'))
|
||||
if common_help:
|
||||
self.group.add_argument('-h', '--help', action=HelpAction,
|
||||
help=_('Print this help message'))
|
||||
self.group.add_argument('-V', '--version', action='version', version='%(prog)s ' + get_version(),
|
||||
help=_('Print version information'))
|
||||
self.group.add_argument('-v', action='version', version='%(prog)s ' + get_version(),
|
||||
help=argparse.SUPPRESS) # Deprecated arg
|
||||
self.group.add_argument('-c', '--config', metavar='<config>',
|
||||
help=_('Set the config directory path'))
|
||||
self.group.add_argument('-l', '--logfile', metavar='<logfile>',
|
||||
help=_('Output to specified logfile instead of stdout'))
|
||||
self.group.add_argument('-L', '--loglevel', choices=[l for k in deluge.log.levels for l in (k, k.upper())],
|
||||
help=_('Set the log level (none, error, warning, info, debug)'), metavar='<level>')
|
||||
self.group.add_argument('--logrotate', nargs='?', const='2M', metavar='<max-size>',
|
||||
help=_('Enable logfile rotation, with optional maximum logfile size, '
|
||||
'default: %(const)s (Logfile rotation count is 5)'))
|
||||
self.group.add_argument('-q', '--quiet', action='store_true',
|
||||
help=_('Quieten logging output (Same as `--loglevel none`)'))
|
||||
self.group.add_argument('--profile', metavar='<profile-file>', nargs='?', default=False,
|
||||
help=_('Profile %(prog)s with cProfile. Outputs to stdout '
|
||||
'unless a filename is specified'))
|
||||
self.group.add_argument(
|
||||
'-h', '--help', action=HelpAction,
|
||||
help=_('Print this help message'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-V', '--version', action='version', version='%(prog)s ' + get_version(),
|
||||
help=_('Print version information'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-v', action='version', version='%(prog)s ' + get_version(),
|
||||
help=argparse.SUPPRESS,
|
||||
) # Deprecated arg
|
||||
self.group.add_argument(
|
||||
'-c', '--config', metavar='<config>',
|
||||
help=_('Set the config directory path'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-l', '--logfile', metavar='<logfile>',
|
||||
help=_('Output to specified logfile instead of stdout'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-L', '--loglevel', choices=[l for k in deluge.log.levels for l in (k, k.upper())],
|
||||
help=_('Set the log level (none, error, warning, info, debug)'), metavar='<level>',
|
||||
)
|
||||
self.group.add_argument(
|
||||
'--logrotate', nargs='?', const='2M', metavar='<max-size>',
|
||||
help=_(
|
||||
'Enable logfile rotation, with optional maximum logfile size, '
|
||||
'default: %(const)s (Logfile rotation count is 5)',
|
||||
),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-q', '--quiet', action='store_true',
|
||||
help=_('Quieten logging output (Same as `--loglevel none`)'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'--profile', metavar='<profile-file>', nargs='?', default=False,
|
||||
help=_(
|
||||
'Profile %(prog)s with cProfile. Outputs to stdout '
|
||||
'unless a filename is specified',
|
||||
),
|
||||
)
|
||||
|
||||
def parse_args(self, args=None):
|
||||
"""Parse UI arguments and handle common and process group options.
|
||||
|
@ -251,8 +273,10 @@ class BaseArgParser(argparse.ArgumentParser):
|
|||
logrotate = common.parse_human_size(options.logrotate)
|
||||
|
||||
# Setup the logger
|
||||
deluge.log.setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode,
|
||||
logrotate=logrotate, output_stream=self.log_stream)
|
||||
deluge.log.setup_logger(
|
||||
level=options.loglevel, filename=options.logfile, filemode=logfile_mode,
|
||||
logrotate=logrotate, output_stream=self.log_stream,
|
||||
)
|
||||
|
||||
if options.config:
|
||||
if not set_config_dir(options.config):
|
||||
|
@ -300,14 +324,24 @@ class BaseArgParser(argparse.ArgumentParser):
|
|||
|
||||
self.process_arg_group = True
|
||||
self.group = self.add_argument_group(_('Process Control Options'))
|
||||
self.group.add_argument('-P', '--pidfile', metavar='<pidfile>', action='store',
|
||||
help=_('Pidfile to store the process id'))
|
||||
self.group.add_argument(
|
||||
'-P', '--pidfile', metavar='<pidfile>', action='store',
|
||||
help=_('Pidfile to store the process id'),
|
||||
)
|
||||
if not common.windows_check():
|
||||
self.group.add_argument('-d', '--do-not-daemonize', dest='donotdaemonize', action='store_true',
|
||||
help=_('Do not daemonize (fork) this process'))
|
||||
self.group.add_argument('-f', '--fork', dest='donotdaemonize', action='store_false',
|
||||
help=argparse.SUPPRESS) # Deprecated arg
|
||||
self.group.add_argument('-U', '--user', metavar='<user>', action='store',
|
||||
help=_('Change to this user on startup (Requires root)'))
|
||||
self.group.add_argument('-g', '--group', metavar='<group>', action='store',
|
||||
help=_('Change to this group on startup (Requires root)'))
|
||||
self.group.add_argument(
|
||||
'-d', '--do-not-daemonize', dest='donotdaemonize', action='store_true',
|
||||
help=_('Do not daemonize (fork) this process'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-f', '--fork', dest='donotdaemonize', action='store_false',
|
||||
help=argparse.SUPPRESS,
|
||||
) # Deprecated arg
|
||||
self.group.add_argument(
|
||||
'-U', '--user', metavar='<user>', action='store',
|
||||
help=_('Change to this user on startup (Requires root)'),
|
||||
)
|
||||
self.group.add_argument(
|
||||
'-g', '--group', metavar='<group>', action='store',
|
||||
help=_('Change to this group on startup (Requires root)'),
|
||||
)
|
||||
|
|
|
@ -98,8 +98,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
log.debug('Received invalid message: type is not tuple')
|
||||
return
|
||||
if len(request) < 3:
|
||||
log.debug('Received invalid message: number of items in '
|
||||
'response is %s', len(request))
|
||||
log.debug(
|
||||
'Received invalid message: number of items in '
|
||||
'response is %s', len(request),
|
||||
)
|
||||
return
|
||||
|
||||
message_type = request[0]
|
||||
|
@ -163,8 +165,10 @@ class DelugeRPCProtocol(DelugeTransferProtocol):
|
|||
log.debug(msg)
|
||||
except Exception:
|
||||
import traceback
|
||||
log.error('Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s',
|
||||
request[2], traceback.format_exc())
|
||||
log.error(
|
||||
'Failed to handle RPC_ERROR (Old daemon?): %s\nLocal error: %s',
|
||||
request[2], traceback.format_exc(),
|
||||
)
|
||||
d.errback(exception)
|
||||
del self.__rpc_requests[request_id]
|
||||
|
||||
|
@ -195,17 +199,23 @@ class DelugeRPCClientFactory(ClientFactory):
|
|||
self.event_handlers = event_handlers
|
||||
|
||||
def startedConnecting(self, connector): # NOQA: N802
|
||||
log.debug('Connecting to daemon at "%s:%s"...',
|
||||
connector.host, connector.port)
|
||||
log.debug(
|
||||
'Connecting to daemon at "%s:%s"...',
|
||||
connector.host, connector.port,
|
||||
)
|
||||
|
||||
def clientConnectionFailed(self, connector, reason): # NOQA: N802
|
||||
log.debug('Connection to daemon at "%s:%s" failed: %s',
|
||||
connector.host, connector.port, reason.value)
|
||||
log.debug(
|
||||
'Connection to daemon at "%s:%s" failed: %s',
|
||||
connector.host, connector.port, reason.value,
|
||||
)
|
||||
self.daemon.connect_deferred.errback(reason)
|
||||
|
||||
def clientConnectionLost(self, connector, reason): # NOQA: N802
|
||||
log.debug('Connection lost to daemon at "%s:%s" reason: %s',
|
||||
connector.host, connector.port, reason.value)
|
||||
log.debug(
|
||||
'Connection lost to daemon at "%s:%s" reason: %s',
|
||||
connector.host, connector.port, reason.value,
|
||||
)
|
||||
self.daemon.host = None
|
||||
self.daemon.port = None
|
||||
self.daemon.username = None
|
||||
|
@ -262,9 +272,11 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
log.debug('sslproxy.connect()')
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.__connector = reactor.connectSSL(self.host, self.port,
|
||||
self.__factory,
|
||||
ssl.ClientContextFactory())
|
||||
self.__connector = reactor.connectSSL(
|
||||
self.host, self.port,
|
||||
self.__factory,
|
||||
ssl.ClientContextFactory(),
|
||||
)
|
||||
self.connect_deferred = defer.Deferred()
|
||||
self.daemon_info_deferred = defer.Deferred()
|
||||
|
||||
|
@ -384,8 +396,10 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
log.debug('%s.authenticate: %s', self.__class__.__name__, username)
|
||||
login_deferred = defer.Deferred()
|
||||
d = self.call('daemon.login', username, password, client_version=get_version())
|
||||
d.addCallbacks(self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred],
|
||||
errbackArgs=[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, login_deferred):
|
||||
|
@ -396,7 +410,8 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
if self.__factory.event_handlers:
|
||||
self.call('daemon.set_event_interest', list(self.__factory.event_handlers))
|
||||
self.call('core.get_auth_levels_mappings').addCallback(
|
||||
self.__on_auth_levels_mappings)
|
||||
self.__on_auth_levels_mappings,
|
||||
)
|
||||
|
||||
login_deferred.callback(result)
|
||||
|
||||
|
@ -434,9 +449,11 @@ class DaemonStandaloneProxy(DaemonProxy):
|
|||
self.host = 'localhost'
|
||||
self.port = 58846
|
||||
# Running in standalone mode, it's safe to import auth level
|
||||
from deluge.core.authmanager import (AUTH_LEVEL_ADMIN,
|
||||
AUTH_LEVELS_MAPPING,
|
||||
AUTH_LEVELS_MAPPING_REVERSE)
|
||||
from deluge.core.authmanager import (
|
||||
AUTH_LEVEL_ADMIN,
|
||||
AUTH_LEVELS_MAPPING,
|
||||
AUTH_LEVELS_MAPPING_REVERSE,
|
||||
)
|
||||
self.username = 'localclient'
|
||||
self.authentication_level = AUTH_LEVEL_ADMIN
|
||||
self.auth_levels_mapping = AUTH_LEVELS_MAPPING
|
||||
|
@ -528,8 +545,10 @@ class Client(object):
|
|||
self.disconnect_callback = None
|
||||
self.__started_standalone = False
|
||||
|
||||
def connect(self, host='127.0.0.1', port=58846, username='', password='',
|
||||
skip_authentication=False):
|
||||
def connect(
|
||||
self, host='127.0.0.1', port=58846, username='', password='',
|
||||
skip_authentication=False,
|
||||
):
|
||||
"""
|
||||
Connects to a daemon process.
|
||||
|
||||
|
@ -635,8 +654,11 @@ class Client(object):
|
|||
from errno import ENOENT
|
||||
if ex.errno == ENOENT:
|
||||
log.error(
|
||||
_('Deluge cannot find the `deluged` executable, check that '
|
||||
'the deluged package is installed, or added to your PATH.'))
|
||||
_(
|
||||
'Deluge cannot find the `deluged` executable, check that '
|
||||
'the deluged package is installed, or added to your PATH.',
|
||||
),
|
||||
)
|
||||
else:
|
||||
log.exception(ex)
|
||||
except Exception as ex:
|
||||
|
|
|
@ -134,14 +134,14 @@ TORRENT_DATA_FIELD = {
|
|||
'pieces':
|
||||
{'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']},
|
||||
'seed_rank':
|
||||
{'name': _('Seed Rank'), 'status': ['seed_rank']}
|
||||
{'name': _('Seed Rank'), 'status': ['seed_rank']},
|
||||
}
|
||||
|
||||
TRACKER_STATUS_TRANSLATION = [
|
||||
_('Error'),
|
||||
_('Warning'),
|
||||
_('Announce OK'),
|
||||
_('Announce Sent')
|
||||
_('Announce Sent'),
|
||||
]
|
||||
|
||||
PREFS_CATOG_TRANS = {
|
||||
|
@ -154,7 +154,7 @@ PREFS_CATOG_TRANS = {
|
|||
'cache': _('Cache'),
|
||||
'other': _('Other'),
|
||||
'daemon': _('Daemon'),
|
||||
'plugins': _('Plugins')
|
||||
'plugins': _('Plugins'),
|
||||
}
|
||||
|
||||
FILE_PRIORITY = {
|
||||
|
@ -178,7 +178,7 @@ del _
|
|||
DISK_CACHE_KEYS = [
|
||||
'disk.num_blocks_read', 'disk.num_blocks_written', 'disk.num_read_ops', 'disk.num_write_ops',
|
||||
'disk.num_blocks_cache_hits', 'read_hit_ratio', 'write_hit_ratio', 'disk.disk_blocks_in_use',
|
||||
'disk.read_cache_blocks'
|
||||
'disk.read_cache_blocks',
|
||||
]
|
||||
|
||||
|
||||
|
@ -279,13 +279,13 @@ class TorrentInfo(object):
|
|||
'type': 'file',
|
||||
'index': 0,
|
||||
'length': self.__m_metadata['info']['length'],
|
||||
'download': True
|
||||
}
|
||||
}
|
||||
'download': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
else:
|
||||
self.__m_files_tree = {
|
||||
self.__m_name: (0, self.__m_metadata['info']['length'], True)
|
||||
self.__m_name: (0, self.__m_metadata['info']['length'], True),
|
||||
}
|
||||
|
||||
self.__m_files = []
|
||||
|
@ -298,13 +298,13 @@ class TorrentInfo(object):
|
|||
self.__m_files.append({
|
||||
'path': f['path'],
|
||||
'size': f['length'],
|
||||
'download': True
|
||||
'download': True,
|
||||
})
|
||||
else:
|
||||
self.__m_files.append({
|
||||
'path': self.__m_name,
|
||||
'size': self.__m_metadata['info']['length'],
|
||||
'download': True
|
||||
'download': True,
|
||||
})
|
||||
|
||||
def as_dict(self, *keys):
|
||||
|
@ -315,7 +315,7 @@ class TorrentInfo(object):
|
|||
:param keys: a number of key strings
|
||||
:type keys: string
|
||||
"""
|
||||
return dict([(key, getattr(self, key)) for key in keys])
|
||||
return {key: getattr(self, key) for key in keys}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -400,7 +400,7 @@ class FileTree2(object):
|
|||
if child is None:
|
||||
parent['contents'][directory] = {
|
||||
'type': 'dir',
|
||||
'contents': {}
|
||||
'contents': {},
|
||||
}
|
||||
parent = parent['contents'][directory]
|
||||
return parent, path
|
||||
|
@ -411,12 +411,12 @@ class FileTree2(object):
|
|||
parent, path = get_parent(path)
|
||||
parent['contents'][path] = {
|
||||
'type': 'dir',
|
||||
'contents': {}
|
||||
'contents': {},
|
||||
}
|
||||
else:
|
||||
parent, path = get_parent(path)
|
||||
parent['contents'][path] = {
|
||||
'type': 'file'
|
||||
'type': 'file',
|
||||
}
|
||||
|
||||
def get_tree(self):
|
||||
|
@ -443,13 +443,13 @@ class FileTree2(object):
|
|||
full_path = os.path.join(parent_path, path).replace('\\', '/')
|
||||
if directory['contents'][path]['type'] == 'dir':
|
||||
directory['contents'][path] = callback(
|
||||
full_path, directory['contents'][path]
|
||||
) or directory['contents'][path]
|
||||
full_path, directory['contents'][path],
|
||||
) or directory['contents'][path]
|
||||
walk(directory['contents'][path], full_path)
|
||||
else:
|
||||
directory['contents'][path] = callback(
|
||||
full_path, directory['contents'][path]
|
||||
) or directory['contents'][path]
|
||||
full_path, directory['contents'][path],
|
||||
) or directory['contents'][path]
|
||||
walk(self.tree, '')
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from deluge.ui.console.cmdline.command import BaseCommand
|
||||
|
|
|
@ -35,8 +35,10 @@ class Command(BaseCommand):
|
|||
|
||||
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'))
|
||||
parser.add_argument(
|
||||
'torrents', metavar='<torrent>', nargs='+',
|
||||
help=_('One or more torrent files, URLs or magnet URIs'),
|
||||
)
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get('ConsoleUI')
|
||||
|
@ -62,11 +64,13 @@ class Command(BaseCommand):
|
|||
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))
|
||||
on_fail,
|
||||
))
|
||||
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))
|
||||
on_fail,
|
||||
))
|
||||
else:
|
||||
# Just a file
|
||||
if urlparse(torrent).scheme == 'file':
|
||||
|
@ -85,7 +89,7 @@ class Command(BaseCommand):
|
|||
deferreds.append(
|
||||
client.core.add_torrent_file_async(
|
||||
filename, filedump, t_options,
|
||||
).addCallback(on_success).addErrback(on_fail)
|
||||
).addCallback(on_success).addErrback(on_fail),
|
||||
)
|
||||
|
||||
return defer.DeferredList(deferreds)
|
||||
|
|
|
@ -59,7 +59,7 @@ STATUS_KEYS = [
|
|||
'total_uploaded',
|
||||
'total_payload_download',
|
||||
'total_payload_upload',
|
||||
'time_added'
|
||||
'time_added',
|
||||
]
|
||||
|
||||
# Add filter specific state to torrent states
|
||||
|
@ -82,21 +82,33 @@ class Command(BaseCommand):
|
|||
"""
|
||||
|
||||
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(
|
||||
'-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'))
|
||||
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 add_subparser(self, subparsers):
|
||||
parser = subparsers.add_parser(self.name, prog=self.name, help=self.__doc__,
|
||||
description=self.__doc__, epilog=self.epilog)
|
||||
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):
|
||||
|
@ -251,7 +263,8 @@ class Command(BaseCommand):
|
|||
colors.state_color['Seeding'],
|
||||
fspeed(peer['up_speed']),
|
||||
colors.state_color['Downloading'],
|
||||
fspeed(peer['down_speed']))
|
||||
fspeed(peer['down_speed']),
|
||||
)
|
||||
s += '\n'
|
||||
|
||||
self.console.write(s[:-1])
|
||||
|
@ -280,10 +293,12 @@ class Command(BaseCommand):
|
|||
if status['state'] != 'Seeding':
|
||||
s += sep
|
||||
s += '{!info!}Down Speed: {!input!}%s' % fspeed(
|
||||
status['download_payload_rate'], shortform=True)
|
||||
status['download_payload_rate'], shortform=True,
|
||||
)
|
||||
s += sep
|
||||
s += '{!info!}Up Speed: {!input!}%s' % fspeed(
|
||||
status['upload_payload_rate'], shortform=True)
|
||||
status['upload_payload_rate'], shortform=True,
|
||||
)
|
||||
self.console.write(s)
|
||||
|
||||
if status['state'] in ('Seeding', 'Downloading', 'Queued'):
|
||||
|
@ -294,7 +309,8 @@ class Command(BaseCommand):
|
|||
s += '{!info!}Availability: {!input!}%.2f' % status['distributed_copies']
|
||||
s += sep
|
||||
s += '{!info!}Seed Rank: {!input!}%s' % f_seedrank_dash(
|
||||
status['seed_rank'], status['seeding_time'])
|
||||
status['seed_rank'], status['seeding_time'],
|
||||
)
|
||||
self.console.write(s)
|
||||
|
||||
total_done = fsize(status['total_done'], shortform=True)
|
||||
|
@ -321,7 +337,8 @@ class Command(BaseCommand):
|
|||
s = '{!info!}Last Transfer: {!input!}%s' % format_time(status['time_since_transfer'])
|
||||
s += sep
|
||||
s += '{!info!}Complete Seen: {!input!}%s' % format_date_never(
|
||||
status['last_seen_complete'])
|
||||
status['last_seen_complete'],
|
||||
)
|
||||
self.console.write(s)
|
||||
|
||||
s = '{!info!}Tracker: {!input!}%s' % status['tracker_host']
|
||||
|
@ -366,14 +383,18 @@ class Command(BaseCommand):
|
|||
dl_info += '%s' % ftotal_sized(status['all_time_download'], status['total_payload_download'])
|
||||
|
||||
if status['download_payload_rate'] > 0:
|
||||
dl_info += ' @ %s%s' % (down_color, fspeed(
|
||||
status['download_payload_rate'], shortform=True))
|
||||
dl_info += ' @ %s%s' % (
|
||||
down_color,
|
||||
fspeed(status['download_payload_rate'], shortform=True),
|
||||
)
|
||||
|
||||
ul_info = ' {!info!}UL: {!input!}'
|
||||
ul_info += '%s' % ftotal_sized(status['total_uploaded'], status['total_payload_upload'])
|
||||
if status['upload_payload_rate'] > 0:
|
||||
ul_info += ' @ %s%s' % (up_color, fspeed(
|
||||
status['upload_payload_rate'], shortform=True))
|
||||
ul_info += ' @ %s%s' % (
|
||||
up_color,
|
||||
fspeed(status['upload_payload_rate'], shortform=True),
|
||||
)
|
||||
|
||||
eta = ' {!info!}ETA: {!magenta!}%s' % format_time(status['eta'])
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ torrent_options = {
|
|||
'stop_ratio': float,
|
||||
'remove_at_ratio': bool,
|
||||
'move_completed': bool,
|
||||
'move_completed_path': str
|
||||
'move_completed_path': str,
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,8 +43,10 @@ class Command(BaseCommand):
|
|||
usage = _('Usage: manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]')
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('torrent', metavar='<torrent>',
|
||||
help=_('an expression matched against torrent ids and torrent names'))
|
||||
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'))
|
||||
|
|
|
@ -21,8 +21,10 @@ class Command(BaseCommand):
|
|||
usage = 'pause [ * | <torrent-id> [<torrent-id> ...] ]'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+',
|
||||
help=_('One or more torrent ids. Use "*" to pause all torrents'))
|
||||
parser.add_argument(
|
||||
'torrent_ids', metavar='<torrent-id>', nargs='+',
|
||||
help=_('One or more torrent ids. Use "*" to pause all torrents'),
|
||||
)
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get('ConsoleUI')
|
||||
|
|
|
@ -20,14 +20,20 @@ class Command(BaseCommand):
|
|||
"""Manage plugins"""
|
||||
|
||||
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(
|
||||
'-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(
|
||||
'-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):
|
||||
|
|
|
@ -21,8 +21,10 @@ class Command(BaseCommand):
|
|||
usage = _('Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]')
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+',
|
||||
help=_('One or more torrent ids. Use "*" to resume all torrents'))
|
||||
parser.add_argument(
|
||||
'torrent_ids', metavar='<torrent-id>', nargs='+',
|
||||
help=_('One or more torrent ids. Use "*" to resume all torrents'),
|
||||
)
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get('ConsoleUI')
|
||||
|
|
|
@ -25,10 +25,14 @@ class Command(BaseCommand):
|
|||
aliases = ['del']
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--remove_data', action='store_true', default=False,
|
||||
help=_('Also removes the torrent data'))
|
||||
parser.add_argument('-c', '--confirm', action='store_true', default=False,
|
||||
help=_('List the matching torrents without removing.'))
|
||||
parser.add_argument(
|
||||
'--remove_data', action='store_true', default=False,
|
||||
help=_('Also removes the torrent data'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--confirm', action='store_true', default=False,
|
||||
help=_('List the matching torrents without removing.'),
|
||||
)
|
||||
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+', help=_('One or more torrent ids'))
|
||||
|
||||
def handle(self, options):
|
||||
|
@ -36,9 +40,11 @@ class Command(BaseCommand):
|
|||
torrent_ids = self.console.match_torrents(options.torrent_ids)
|
||||
|
||||
if not options.confirm:
|
||||
self.console.write('{!info!}%d %s %s{!info!}' % (len(torrent_ids),
|
||||
_n('torrent', 'torrents', len(torrent_ids)),
|
||||
_n('match', 'matches', len(torrent_ids))))
|
||||
self.console.write('{!info!}%d %s %s{!info!}' % (
|
||||
len(torrent_ids),
|
||||
_n('torrent', 'torrents', len(torrent_ids)),
|
||||
_n('match', 'matches', len(torrent_ids)),
|
||||
))
|
||||
for t_id in torrent_ids:
|
||||
name = self.console.get_torrent_name(t_id)
|
||||
self.console.write('* %-50s (%s)' % (name, t_id))
|
||||
|
|
|
@ -26,11 +26,17 @@ class Command(BaseCommand):
|
|||
"""Shows various status information from the daemon"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-r', '--raw', action='store_true', default=False, dest='raw',
|
||||
help=_('Raw values for upload/download rates (without KiB/s suffix)'
|
||||
'(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=_('Do not show torrent status (Improves command speed)'))
|
||||
parser.add_argument(
|
||||
'-r', '--raw', action='store_true', default=False, dest='raw',
|
||||
help=_(
|
||||
'Raw values for upload/download rates (without KiB/s suffix)'
|
||||
'(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=_('Do not show torrent status (Improves command speed)'),
|
||||
)
|
||||
|
||||
def handle(self, options):
|
||||
self.console = component.get('ConsoleUI')
|
||||
|
|
|
@ -22,8 +22,10 @@ class Command(BaseCommand):
|
|||
aliases = ['reannounce']
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('torrent_ids', metavar='<torrent-id>', nargs='+',
|
||||
help='One or more torrent ids. "*" updates all torrents')
|
||||
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')
|
||||
|
|
|
@ -30,8 +30,12 @@ log = logging.getLogger(__name__)
|
|||
def load_commands(command_dir):
|
||||
|
||||
def get_command(name):
|
||||
command = getattr(__import__('deluge.ui.console.cmdline.commands.%s' % name,
|
||||
{}, {}, ['Command']), 'Command')()
|
||||
command = getattr(
|
||||
__import__(
|
||||
'deluge.ui.console.cmdline.commands.%s' % name,
|
||||
{}, {}, ['Command'],
|
||||
), 'Command',
|
||||
)()
|
||||
command._name = name
|
||||
return command
|
||||
|
||||
|
@ -67,29 +71,45 @@ class Console(UI):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(Console, self).__init__('console', *args, log_stream=LogStream(), **kwargs)
|
||||
|
||||
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', metavar='<ip_addr>', dest='daemon_addr',
|
||||
help=_('Deluge daemon IP address to connect to (default 127.0.0.1)'), default='127.0.0.1')
|
||||
group.add_argument('-p', '--port', metavar='<port>', dest='daemon_port', type=int,
|
||||
help=_('Deluge daemon port to connect to (default 58846)'), default='58846')
|
||||
group.add_argument('-U', '--username', metavar='<user>', dest='daemon_user',
|
||||
help=_('Deluge daemon username to use when connecting'))
|
||||
group.add_argument('-P', '--password', metavar='<pass>', dest='daemon_pass',
|
||||
help=_('Deluge daemon password to use when connecting'))
|
||||
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', metavar='<ip_addr>', dest='daemon_addr',
|
||||
help=_('Deluge daemon IP address to connect to (default 127.0.0.1)'), default='127.0.0.1',
|
||||
)
|
||||
group.add_argument(
|
||||
'-p', '--port', metavar='<port>', dest='daemon_port', type=int,
|
||||
help=_('Deluge daemon port to connect to (default 58846)'), default='58846',
|
||||
)
|
||||
group.add_argument(
|
||||
'-U', '--username', metavar='<user>', dest='daemon_user',
|
||||
help=_('Deluge daemon username to use when connecting'),
|
||||
)
|
||||
group.add_argument(
|
||||
'-P', '--password', metavar='<pass>', dest='daemon_pass',
|
||||
help=_('Deluge daemon password to use when connecting'),
|
||||
)
|
||||
# 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.parser import ConsoleCommandParser # import here because (see top)
|
||||
self.console_parser = ConsoleCommandParser(parents=[self.parser], add_help=False, prog=self.parser.prog,
|
||||
description='Starts the Deluge console interface',
|
||||
formatter_class=lambda prog:
|
||||
DelugeTextHelpFormatter(prog, max_help_position=33, width=90))
|
||||
self.console_parser = ConsoleCommandParser(
|
||||
parents=[self.parser], add_help=False, prog=self.parser.prog,
|
||||
description='Starts the Deluge console interface',
|
||||
formatter_class=lambda prog:
|
||||
DelugeTextHelpFormatter(prog, max_help_position=33, width=90),
|
||||
)
|
||||
self.parser.subparser = self.console_parser
|
||||
self.console_parser.base_parser = self.parser
|
||||
subparsers = self.console_parser.add_subparsers(title=_('Console Commands'), help=_('Description'),
|
||||
description=_('The following console commands are available:'),
|
||||
metavar=_('Command'), dest='command')
|
||||
subparsers = self.console_parser.add_subparsers(
|
||||
title=_('Console Commands'), help=_('Description'),
|
||||
description=_('The following console commands are available:'),
|
||||
metavar=_('Command'), dest='command',
|
||||
)
|
||||
from deluge.ui.console import UI_PATH # Must import here
|
||||
self.console_cmds = load_commands(os.path.join(UI_PATH, 'cmdline', 'commands'))
|
||||
for cmd in sorted(self.console_cmds):
|
||||
|
@ -116,5 +136,7 @@ class Console(UI):
|
|||
log.exception(ex)
|
||||
raise
|
||||
|
||||
return deluge.common.run_profiled(run, self.options, output_file=self.options.profile,
|
||||
do_profile=self.options.profile)
|
||||
return deluge.common.run_profiled(
|
||||
run, self.options, output_file=self.options.profile,
|
||||
do_profile=self.options.profile,
|
||||
)
|
||||
|
|
|
@ -49,7 +49,7 @@ DEFAULT_CONSOLE_PREFS = {
|
|||
'sidebar_width': 25,
|
||||
'separate_complete': True,
|
||||
'move_selection': True,
|
||||
'columns': {}
|
||||
'columns': {},
|
||||
},
|
||||
'addtorrents': {
|
||||
'show_misc_files': False, # TODO: Showing/hiding this
|
||||
|
@ -63,7 +63,7 @@ DEFAULT_CONSOLE_PREFS = {
|
|||
'third_tab_lists_all': False,
|
||||
'torrents_per_tab_press': 15,
|
||||
'save_command_history': True,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
@ -207,8 +207,10 @@ Please use commands from the command line, e.g.:\n
|
|||
if not self.interactive and options.parsed_cmds[0].command == 'connect':
|
||||
d = commander.exec_command(options.parsed_cmds.pop(0))
|
||||
else:
|
||||
log.info('connect: host=%s, port=%s, username=%s, password=%s',
|
||||
options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass)
|
||||
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)
|
||||
|
@ -279,8 +281,10 @@ Please use commands from the command line, e.g.:\n
|
|||
# which can cause issues as the popup's screen will not be destroyed.
|
||||
# This can lead to the popup border being visible for short periods
|
||||
# while the current modes' screen is repainted.
|
||||
log.error('Mode "%s" still has popups available after being paused.'
|
||||
' Ensure all popups are removed on pause!', mode.popup.title)
|
||||
log.error(
|
||||
'Mode "%s" still has popups available after being paused.'
|
||||
' Ensure all popups are removed on pause!', mode.popup.title,
|
||||
)
|
||||
d.addCallback(on_mode_paused, self.active_mode)
|
||||
reactor.removeReader(self.active_mode)
|
||||
|
||||
|
@ -418,8 +422,10 @@ Please use commands from the command line, e.g.:\n
|
|||
|
||||
def tab_complete_path(self, line, path_type='file', ext='', sort='name', dirs_first=True):
|
||||
if self.interactive and isinstance(self.active_mode, deluge.ui.console.modes.cmdline.CmdLine):
|
||||
return self.active_mode.tab_complete_path(line, path_type=path_type, ext=ext,
|
||||
sort=sort, dirs_first=dirs_first)
|
||||
return self.active_mode.tab_complete_path(
|
||||
line, path_type=path_type, ext=ext,
|
||||
sort=sort, dirs_first=dirs_first,
|
||||
)
|
||||
|
||||
def on_client_disconnect(self):
|
||||
component.stop()
|
||||
|
@ -474,10 +480,12 @@ Please use commands from the command line, e.g.:\n
|
|||
config['language'] = DEFAULT_CONSOLE_PREFS['language']
|
||||
|
||||
# Migrate column settings
|
||||
columns = ['queue', 'size', 'state', 'progress', 'seeds', 'peers', 'downspeed', 'upspeed',
|
||||
'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded',
|
||||
'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio',
|
||||
'complete_seen', 'down_limit', 'up_limit', 'shared', 'name']
|
||||
columns = [
|
||||
'queue', 'size', 'state', 'progress', 'seeds', 'peers', 'downspeed', 'upspeed',
|
||||
'eta', 'ratio', 'avail', 'added', 'tracker', 'savepath', 'downloaded', 'uploaded',
|
||||
'remaining', 'owner', 'downloading_time', 'seeding_time', 'completed', 'seeds_peers_ratio',
|
||||
'complete_seen', 'down_limit', 'up_limit', 'shared', 'name',
|
||||
]
|
||||
column_name_mapping = {
|
||||
'downspeed': 'download_speed',
|
||||
'upspeed': 'upload_speed',
|
||||
|
@ -487,7 +495,7 @@ Please use commands from the command line, e.g.:\n
|
|||
'complete_seen': 'last_seen_complete',
|
||||
'down_limit': 'max_download_speed',
|
||||
'up_limit': 'max_upload_speed',
|
||||
'downloading_time': 'active_time'
|
||||
'downloading_time': 'active_time',
|
||||
}
|
||||
|
||||
from deluge.ui.console.modes.torrentlist.torrentview import default_columns
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue