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