Update transmissionrpc to 0.11

Also updates:
- six-1.12.0
This commit is contained in:
Labrys of Knossos 2018-12-16 11:31:00 -05:00
commit 30a1789809
7 changed files with 394 additions and 456 deletions

View file

@ -2,25 +2,25 @@
# Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import re, time, operator, warnings, os
import base64 import base64
import json import json
import operator
import os
import re
import time
import warnings
from six import PY3, integer_types, iteritems, string_types from transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT
from six.moves.urllib_parse import urlparse from transmissionrpc.error import TransmissionError, HTTPHandlerError
from six.moves.urllib_request import urlopen from transmissionrpc.utils import LOGGER, get_arguments, make_rpc_name, argument_value_convert, rpc_bool
from transmissionrpc.httphandler import DefaultHTTPHandler
from transmissionrpc.torrent import Torrent
from transmissionrpc.session import Session
from .constants import DEFAULT_PORT, DEFAULT_TIMEOUT from six import PY3, integer_types, string_types, iteritems
from .error import HTTPHandlerError, TransmissionError
from .httphandler import DefaultHTTPHandler
from .session import Session
from .torrent import Torrent
from .utils import LOGGER, argument_value_convert, get_arguments, make_rpc_name, rpc_bool
if PY3:
from urllib.parse import urlparse
from urllib.request import urlopen
else:
from urlparse import urlparse
from urllib2 import urlopen
def debug_httperror(error): def debug_httperror(error):
""" """
@ -45,7 +45,6 @@ def debug_httperror(error):
) )
) )
def parse_torrent_id(arg): def parse_torrent_id(arg):
"""Parse an torrent id or torrent hashString.""" """Parse an torrent id or torrent hashString."""
torrent_id = None torrent_id = None
@ -59,7 +58,7 @@ def parse_torrent_id(arg):
elif isinstance(arg, string_types): elif isinstance(arg, string_types):
try: try:
torrent_id = int(arg) torrent_id = int(arg)
if torrent_id >= 2 ** 31: if torrent_id >= 2**31:
torrent_id = None torrent_id = None
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass
@ -72,7 +71,6 @@ def parse_torrent_id(arg):
pass pass
return torrent_id return torrent_id
def parse_torrent_ids(args): def parse_torrent_ids(args):
""" """
Take things and make them valid torrent identifiers Take things and make them valid torrent identifiers
@ -100,20 +98,19 @@ def parse_torrent_ids(args):
except ValueError: except ValueError:
pass pass
if not addition: if not addition:
raise ValueError('Invalid torrent id, {item!r}'.format(item=item)) raise ValueError('Invalid torrent id, \"%s\"' % item)
ids.extend(addition) ids.extend(addition)
elif isinstance(args, (list, tuple)): elif isinstance(args, (list, tuple)):
for item in args: for item in args:
ids.extend(parse_torrent_ids(item)) ids.extend(parse_torrent_ids(item))
else: else:
torrent_id = parse_torrent_id(args) torrent_id = parse_torrent_id(args)
if torrent_id is None: if torrent_id == None:
raise ValueError('Invalid torrent id') raise ValueError('Invalid torrent id')
else: else:
ids = [torrent_id] ids = [torrent_id]
return ids return ids
""" """
Torrent ids Torrent ids
@ -128,27 +125,26 @@ possible to provide a argument called ``timeout``. Timeout is only effective
when using Python 2.6 or later and the default timeout is 30 seconds. when using Python 2.6 or later and the default timeout is 30 seconds.
""" """
class Client(object): class Client(object):
""" """
Client is the class handling the Transmission JSON-RPC client protocol. Client is the class handling the Transmission JSON-RPC client protocol.
""" """
def __init__(self, address='localhost', port=DEFAULT_PORT, user=None, password=None, http_handler=None, def __init__(self, address='localhost', port=DEFAULT_PORT, user=None, password=None, http_handler=None, timeout=None):
timeout=None):
if isinstance(timeout, (integer_types, float)): if isinstance(timeout, (integer_types, float)):
self._query_timeout = float(timeout) self._query_timeout = float(timeout)
else: else:
self._query_timeout = DEFAULT_TIMEOUT self._query_timeout = DEFAULT_TIMEOUT
urlo = urlparse(address) urlo = urlparse(address)
if not urlo.scheme: if urlo.scheme == '':
self.url = 'http://{host}:{port}/transmission/rpc/'.format(host=address, port=port) base_url = 'http://' + address + ':' + str(port)
self.url = base_url + '/transmission/rpc'
else: else:
if urlo.port: if urlo.port:
self.url = '{url.scheme}://{url.hostname}:{url.port}{url.path}'.format(url=urlo) self.url = urlo.scheme + '://' + urlo.hostname + ':' + str(urlo.port) + urlo.path
else: else:
self.url = '{url.scheme}://{url.hostname}{url.path}'.format(url=urlo) self.url = urlo.scheme + '://' + urlo.hostname + urlo.path
LOGGER.info('Using custom URL {url!r}.'.format(url=self.url)) LOGGER.info('Using custom URL "' + self.url + '".')
if urlo.username and urlo.password: if urlo.username and urlo.password:
user = urlo.username user = urlo.username
password = urlo.password password = urlo.password
@ -204,8 +200,7 @@ class Client(object):
if timeout is None: if timeout is None:
timeout = self._query_timeout timeout = self._query_timeout
while True: while True:
LOGGER.debug( LOGGER.debug(json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2))
json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2))
try: try:
result = self.http_handler.request(self.url, query, headers, timeout) result = self.http_handler.request(self.url, query, headers, timeout)
break break
@ -245,25 +240,26 @@ class Client(object):
elif require_ids: elif require_ids:
raise ValueError('request require ids') raise ValueError('request require ids')
query = json.dumps({'tag': self._sequence, 'method': method, 'arguments': arguments}) query = json.dumps({'tag': self._sequence, 'method': method
, 'arguments': arguments})
self._sequence += 1 self._sequence += 1
start = time.time() start = time.time()
http_data = self._http_query(query, timeout) http_data = self._http_query(query, timeout)
elapsed = time.time() - start elapsed = time.time() - start
LOGGER.info('http request took {time:.3f} s'.format(time=elapsed)) LOGGER.info('http request took %.3f s' % (elapsed))
try: try:
data = json.loads(http_data) data = json.loads(http_data)
except ValueError as error: except ValueError as error:
LOGGER.error('Error: {msg}'.format(msg=error)) LOGGER.error('Error: ' + str(error))
LOGGER.error('Request: {request!r}'.format(request=query)) LOGGER.error('Request: \"%s\"' % (query))
LOGGER.error('HTTP data: {data!r}'.format(data=http_data)) LOGGER.error('HTTP data: \"%s\"' % (http_data))
raise raise
LOGGER.debug(json.dumps(data, indent=2)) LOGGER.debug(json.dumps(data, indent=2))
if 'result' in data: if 'result' in data:
if data['result'] != 'success': if data['result'] != 'success':
raise TransmissionError('Query failed with result {result!r}.'.format(result=data['result'])) raise TransmissionError('Query failed with result \"%s\".' % (data['result']))
else: else:
raise TransmissionError('Query failed without result.') raise TransmissionError('Query failed without result.')
@ -347,9 +343,8 @@ class Client(object):
Add a warning to the log if the Transmission RPC version is lower then the provided version. Add a warning to the log if the Transmission RPC version is lower then the provided version.
""" """
if self.rpc_version < version: if self.rpc_version < version:
LOGGER.warning('Using feature not supported by server. ' LOGGER.warning('Using feature not supported by server. RPC version for server %d, feature introduced in %d.'
'RPC version for server {x}, feature introduced in {y}.'.format % (self.rpc_version, version))
(x=self.rpc_version, y=version))
def add_torrent(self, torrent, timeout=None, **kwargs): def add_torrent(self, torrent, timeout=None, **kwargs):
""" """
@ -409,8 +404,11 @@ class Client(object):
pass pass
if might_be_base64: if might_be_base64:
torrent_data = torrent torrent_data = torrent
args = {}
args = {'metainfo': torrent_data} if torrent_data else {'filename': torrent} if torrent_data:
args = {'metainfo': torrent_data}
else:
args = {'filename': torrent}
for key, value in iteritems(kwargs): for key, value in iteritems(kwargs):
argument = make_rpc_name(key) argument = make_rpc_name(key)
(arg, val) = argument_value_convert('torrent-add', argument, value, self.rpc_version) (arg, val) = argument_value_convert('torrent-add', argument, value, self.rpc_version)
@ -474,7 +472,7 @@ class Client(object):
""" """
self._rpc_version_warning(3) self._rpc_version_warning(3)
self._request('torrent-remove', self._request('torrent-remove',
{'delete-local-data': rpc_bool(delete_data)}, ids, True, timeout=timeout) {'delete-local-data':rpc_bool(delete_data)}, ids, True, timeout=timeout)
def remove(self, ids, delete_data=False, timeout=None): def remove(self, ids, delete_data=False, timeout=None):
""" """
@ -604,34 +602,34 @@ class Client(object):
the new methods. list returns a dictionary indexed by torrent id. the new methods. list returns a dictionary indexed by torrent id.
""" """
warnings.warn('list has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning) warnings.warn('list has been deprecated, please use get_torrent or get_torrents instead.', DeprecationWarning)
fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone', fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone'
'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver', , 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver'
'downloadedEver', 'uploadRatio', 'queuePosition'] , 'downloadedEver', 'uploadRatio', 'queuePosition']
return self._request('torrent-get', {'fields': fields}, timeout=timeout) return self._request('torrent-get', {'fields': fields}, timeout=timeout)
def get_files(self, ids=None, timeout=None): def get_files(self, ids=None, timeout=None):
""" """
Get list of files for provided torrent id(s). If ids is empty, Get list of files for provided torrent id(s). If ids is empty,
information for all torrents are fetched. This function returns a dictionary information for all torrents are fetched. This function returns a dictionary
for each requested torrent id holding the information about the files. for each requested torrent id holding the information about the files.
:: ::
{ {
<torrent id>: { <torrent id>: {
<file id>: { <file id>: {
'name': <file name>, 'name': <file name>,
'size': <file size in bytes>, 'size': <file size in bytes>,
'completed': <bytes completed>, 'completed': <bytes completed>,
'priority': <priority ('high'|'normal'|'low')>, 'priority': <priority ('high'|'normal'|'low')>,
'selected': <selected for download (True|False)> 'selected': <selected for download (True|False)>
} }
... ...
} }
... ...
} }
""" """
fields = ['id', 'name', 'hashString', 'files', 'priorities', 'wanted'] fields = ['id', 'name', 'hashString', 'files', 'priorities', 'wanted']
request_result = self._request('torrent-get', {'fields': fields}, ids, timeout=timeout) request_result = self._request('torrent-get', {'fields': fields}, ids, timeout=timeout)
@ -643,22 +641,22 @@ class Client(object):
def set_files(self, items, timeout=None): def set_files(self, items, timeout=None):
""" """
Set file properties. Takes a dictionary with similar contents as the result Set file properties. Takes a dictionary with similar contents as the result
of `get_files`. of `get_files`.
:: ::
{ {
<torrent id>: { <torrent id>: {
<file id>: { <file id>: {
'priority': <priority ('high'|'normal'|'low')>, 'priority': <priority ('high'|'normal'|'low')>,
'selected': <selected for download (True|False)> 'selected': <selected for download (True|False)>
} }
... ...
} }
... ...
} }
""" """
if not isinstance(items, dict): if not isinstance(items, dict):
raise ValueError('Invalid file description') raise ValueError('Invalid file description')
@ -701,8 +699,8 @@ class Client(object):
def change_torrent(self, ids, timeout=None, **kwargs): def change_torrent(self, ids, timeout=None, **kwargs):
""" """
Change torrent parameters for the torrent(s) with the supplied id's. The Change torrent parameters for the torrent(s) with the supplied id's. The
parameters are: parameters are:
============================ ===== =============== ======================================================================================= ============================ ===== =============== =======================================================================================
Argument RPC Replaced by Description Argument RPC Replaced by Description
@ -734,13 +732,13 @@ class Client(object):
``uploadLimited`` 5 - Enable upload speed limiter. ``uploadLimited`` 5 - Enable upload speed limiter.
============================ ===== =============== ======================================================================================= ============================ ===== =============== =======================================================================================
.. NOTE:: .. NOTE::
transmissionrpc will try to automatically fix argument errors. transmissionrpc will try to automatically fix argument errors.
""" """
args = {} args = {}
for key, value in iteritems(kwargs): for key, value in iteritems(kwargs):
argument = make_rpc_name(key) argument = make_rpc_name(key)
(arg, val) = argument_value_convert('torrent-set', argument, value, self.rpc_version) (arg, val) = argument_value_convert('torrent-set' , argument, value, self.rpc_version)
args[arg] = val args[arg] = val
if len(args) > 0: if len(args) > 0:
@ -801,7 +799,7 @@ class Client(object):
raise ValueError("Target name cannot contain a path delimiter") raise ValueError("Target name cannot contain a path delimiter")
args = {'path': location, 'name': name} args = {'path': location, 'name': name}
result = self._request('torrent-rename-path', args, torrent_id, True, timeout=timeout) result = self._request('torrent-rename-path', args, torrent_id, True, timeout=timeout)
return result['path'], result['name'] return (result['path'], result['name'])
def queue_top(self, ids, timeout=None): def queue_top(self, ids, timeout=None):
"""Move transfer to the top of the queue.""" """Move transfer to the top of the queue."""
@ -886,14 +884,14 @@ class Client(object):
================================ ===== ================= ========================================================================================================================== ================================ ===== ================= ==========================================================================================================================
.. NOTE:: .. NOTE::
transmissionrpc will try to automatically fix argument errors. transmissionrpc will try to automatically fix argument errors.
""" """
args = {} args = {}
for key, value in iteritems(kwargs): for key, value in iteritems(kwargs):
if key == 'encryption' and value not in ['required', 'preferred', 'tolerated']: if key == 'encryption' and value not in ['required', 'preferred', 'tolerated']:
raise ValueError('Invalid encryption value') raise ValueError('Invalid encryption value')
argument = make_rpc_name(key) argument = make_rpc_name(key)
(arg, val) = argument_value_convert('session-set', argument, value, self.rpc_version) (arg, val) = argument_value_convert('session-set' , argument, value, self.rpc_version)
args[arg] = val args[arg] = val
if len(args) > 0: if len(args) > 0:
self._request('session-set', args, timeout=timeout) self._request('session-set', args, timeout=timeout)

View file

@ -3,13 +3,11 @@
# Licensed under the MIT license. # Licensed under the MIT license.
import logging import logging
from six import iteritems from six import iteritems
LOGGER = logging.getLogger('transmissionrpc') LOGGER = logging.getLogger('transmissionrpc')
LOGGER.setLevel(logging.ERROR) LOGGER.setLevel(logging.ERROR)
def mirror_dict(source): def mirror_dict(source):
""" """
Creates a dictionary with all values as keys and all keys as values. Creates a dictionary with all values as keys and all keys as values.
@ -17,39 +15,38 @@ def mirror_dict(source):
source.update(dict((value, key) for key, value in iteritems(source))) source.update(dict((value, key) for key, value in iteritems(source)))
return source return source
DEFAULT_PORT = 9091 DEFAULT_PORT = 9091
DEFAULT_TIMEOUT = 30.0 DEFAULT_TIMEOUT = 30.0
TR_PRI_LOW = -1 TR_PRI_LOW = -1
TR_PRI_NORMAL = 0 TR_PRI_NORMAL = 0
TR_PRI_HIGH = 1 TR_PRI_HIGH = 1
PRIORITY = mirror_dict({ PRIORITY = mirror_dict({
'low': TR_PRI_LOW, 'low' : TR_PRI_LOW,
'normal': TR_PRI_NORMAL, 'normal' : TR_PRI_NORMAL,
'high': TR_PRI_HIGH 'high' : TR_PRI_HIGH
}) })
TR_RATIOLIMIT_GLOBAL = 0 # follow the global settings TR_RATIOLIMIT_GLOBAL = 0 # follow the global settings
TR_RATIOLIMIT_SINGLE = 1 # override the global settings, seeding until a certain ratio TR_RATIOLIMIT_SINGLE = 1 # override the global settings, seeding until a certain ratio
TR_RATIOLIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of ratio TR_RATIOLIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of ratio
RATIO_LIMIT = mirror_dict({ RATIO_LIMIT = mirror_dict({
'global': TR_RATIOLIMIT_GLOBAL, 'global' : TR_RATIOLIMIT_GLOBAL,
'single': TR_RATIOLIMIT_SINGLE, 'single' : TR_RATIOLIMIT_SINGLE,
'unlimited': TR_RATIOLIMIT_UNLIMITED 'unlimited' : TR_RATIOLIMIT_UNLIMITED
}) })
TR_IDLELIMIT_GLOBAL = 0 # follow the global settings TR_IDLELIMIT_GLOBAL = 0 # follow the global settings
TR_IDLELIMIT_SINGLE = 1 # override the global settings, seeding until a certain idle time TR_IDLELIMIT_SINGLE = 1 # override the global settings, seeding until a certain idle time
TR_IDLELIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of activity TR_IDLELIMIT_UNLIMITED = 2 # override the global settings, seeding regardless of activity
IDLE_LIMIT = mirror_dict({ IDLE_LIMIT = mirror_dict({
'global': TR_RATIOLIMIT_GLOBAL, 'global' : TR_RATIOLIMIT_GLOBAL,
'single': TR_RATIOLIMIT_SINGLE, 'single' : TR_RATIOLIMIT_SINGLE,
'unlimited': TR_RATIOLIMIT_UNLIMITED 'unlimited' : TR_RATIOLIMIT_UNLIMITED
}) })
# A note on argument maps # A note on argument maps
@ -63,266 +60,236 @@ IDLE_LIMIT = mirror_dict({
# Arguments for torrent methods # Arguments for torrent methods
TORRENT_ARGS = { TORRENT_ARGS = {
'get': { 'get' : {
'activityDate': ('number', 1, None, None, None, 'Last time of upload or download activity.'), 'activityDate': ('number', 1, None, None, None, 'Last time of upload or download activity.'),
'addedDate': ('number', 1, None, None, None, 'The date when this torrent was first added.'), 'addedDate': ('number', 1, None, None, None, 'The date when this torrent was first added.'),
'announceResponse': ('string', 1, 7, None, None, 'The announce message from the tracker.'), 'announceResponse': ('string', 1, 7, None, None, 'The announce message from the tracker.'),
'announceURL': ('string', 1, 7, None, None, 'Current announce URL.'), 'announceURL': ('string', 1, 7, None, None, 'Current announce URL.'),
'bandwidthPriority': ('number', 5, None, None, None, 'Bandwidth priority. Low (-1), Normal (0) or High (1).'), 'bandwidthPriority': ('number', 5, None, None, None, 'Bandwidth priority. Low (-1), Normal (0) or High (1).'),
'comment': ('string', 1, None, None, None, 'Torrent comment.'), 'comment': ('string', 1, None, None, None, 'Torrent comment.'),
'corruptEver': ('number', 1, None, None, None, 'Number of bytes of corrupt data downloaded.'), 'corruptEver': ('number', 1, None, None, None, 'Number of bytes of corrupt data downloaded.'),
'creator': ('string', 1, None, None, None, 'Torrent creator.'), 'creator': ('string', 1, None, None, None, 'Torrent creator.'),
'dateCreated': ('number', 1, None, None, None, 'Torrent creation date.'), 'dateCreated': ('number', 1, None, None, None, 'Torrent creation date.'),
'desiredAvailable': ('number', 1, None, None, None, 'Number of bytes avalable and left to be downloaded.'), 'desiredAvailable': ('number', 1, None, None, None, 'Number of bytes avalable and left to be downloaded.'),
'doneDate': ('number', 1, None, None, None, 'The date when the torrent finished downloading.'), 'doneDate': ('number', 1, None, None, None, 'The date when the torrent finished downloading.'),
'downloadDir': ('string', 4, None, None, None, 'The directory path where the torrent is downloaded to.'), 'downloadDir': ('string', 4, None, None, None, 'The directory path where the torrent is downloaded to.'),
'downloadedEver': ('number', 1, None, None, None, 'Number of bytes of good data downloaded.'), 'downloadedEver': ('number', 1, None, None, None, 'Number of bytes of good data downloaded.'),
'downloaders': ('number', 4, 7, None, None, 'Number of downloaders.'), 'downloaders': ('number', 4, 7, None, None, 'Number of downloaders.'),
'downloadLimit': ('number', 1, None, None, None, 'Download limit in Kbps.'), 'downloadLimit': ('number', 1, None, None, None, 'Download limit in Kbps.'),
'downloadLimited': ('boolean', 5, None, None, None, 'Download limit is enabled'), 'downloadLimited': ('boolean', 5, None, None, None, 'Download limit is enabled'),
'downloadLimitMode': ( 'downloadLimitMode': ('number', 1, 5, None, None, 'Download limit mode. 0 means global, 1 means signle, 2 unlimited.'),
'number', 1, 5, None, None, 'Download limit mode. 0 means global, 1 means signle, 2 unlimited.'), 'error': ('number', 1, None, None, None, 'Kind of error. 0 means OK, 1 means tracker warning, 2 means tracker error, 3 means local error.'),
'error': ('number', 1, None, None, None, 'errorString': ('number', 1, None, None, None, 'Error message.'),
'Kind of error. 0 means OK, 1 means tracker warning, 2 means tracker error, 3 means local error.'), 'eta': ('number', 1, None, None, None, 'Estimated number of seconds left when downloading or seeding. -1 means not available and -2 means unknown.'),
'errorString': ('number', 1, None, None, None, 'Error message.'), 'etaIdle': ('number', 15, None, None, None, 'Estimated number of seconds left until the idle time limit is reached. -1 means not available and -2 means unknown.'),
'eta': ('number', 1, None, None, None, 'files': ('array', 1, None, None, None, 'Array of file object containing key, bytesCompleted, length and name.'),
'Estimated number of seconds left when downloading or seeding. -1 means not available and -2 means unknown.'), 'fileStats': ('array', 5, None, None, None, 'Aray of file statistics containing bytesCompleted, wanted and priority.'),
'etaIdle': ('number', 15, None, None, None, 'hashString': ('string', 1, None, None, None, 'Hashstring unique for the torrent even between sessions.'),
'Estimated number of seconds left until the idle time limit is reached. -1 means not available and -2 means unknown.'), 'haveUnchecked': ('number', 1, None, None, None, 'Number of bytes of partial pieces.'),
'files': ( 'haveValid': ('number', 1, None, None, None, 'Number of bytes of checksum verified data.'),
'array', 1, None, None, None, 'Array of file object containing key, bytesCompleted, length and name.'), 'honorsSessionLimits': ('boolean', 5, None, None, None, 'True if session upload limits are honored'),
'fileStats': ( 'id': ('number', 1, None, None, None, 'Session unique torrent id.'),
'array', 5, None, None, None, 'Aray of file statistics containing bytesCompleted, wanted and priority.'), 'isFinished': ('boolean', 9, None, None, None, 'True if the torrent is finished. Downloaded and seeded.'),
'hashString': ('string', 1, None, None, None, 'Hashstring unique for the torrent even between sessions.'), 'isPrivate': ('boolean', 1, None, None, None, 'True if the torrent is private.'),
'haveUnchecked': ('number', 1, None, None, None, 'Number of bytes of partial pieces.'), 'isStalled': ('boolean', 14, None, None, None, 'True if the torrent has stalled (been idle for a long time).'),
'haveValid': ('number', 1, None, None, None, 'Number of bytes of checksum verified data.'), 'lastAnnounceTime': ('number', 1, 7, None, None, 'The time of the last announcement.'),
'honorsSessionLimits': ('boolean', 5, None, None, None, 'True if session upload limits are honored'), 'lastScrapeTime': ('number', 1, 7, None, None, 'The time af the last successful scrape.'),
'id': ('number', 1, None, None, None, 'Session unique torrent id.'), 'leechers': ('number', 1, 7, None, None, 'Number of leechers.'),
'isFinished': ('boolean', 9, None, None, None, 'True if the torrent is finished. Downloaded and seeded.'), 'leftUntilDone': ('number', 1, None, None, None, 'Number of bytes left until the download is done.'),
'isPrivate': ('boolean', 1, None, None, None, 'True if the torrent is private.'), 'magnetLink': ('string', 7, None, None, None, 'The magnet link for this torrent.'),
'isStalled': ('boolean', 14, None, None, None, 'True if the torrent has stalled (been idle for a long time).'), 'manualAnnounceTime': ('number', 1, None, None, None, 'The time until you manually ask for more peers.'),
'lastAnnounceTime': ('number', 1, 7, None, None, 'The time of the last announcement.'), 'maxConnectedPeers': ('number', 1, None, None, None, 'Maximum of connected peers.'),
'lastScrapeTime': ('number', 1, 7, None, None, 'The time af the last successful scrape.'), 'metadataPercentComplete': ('number', 7, None, None, None, 'Download progress of metadata. 0.0 to 1.0.'),
'leechers': ('number', 1, 7, None, None, 'Number of leechers.'), 'name': ('string', 1, None, None, None, 'Torrent name.'),
'leftUntilDone': ('number', 1, None, None, None, 'Number of bytes left until the download is done.'), 'nextAnnounceTime': ('number', 1, 7, None, None, 'Next announce time.'),
'magnetLink': ('string', 7, None, None, None, 'The magnet link for this torrent.'), 'nextScrapeTime': ('number', 1, 7, None, None, 'Next scrape time.'),
'manualAnnounceTime': ('number', 1, None, None, None, 'The time until you manually ask for more peers.'), 'peer-limit': ('number', 5, None, None, None, 'Maximum number of peers.'),
'maxConnectedPeers': ('number', 1, None, None, None, 'Maximum of connected peers.'), 'peers': ('array', 2, None, None, None, 'Array of peer objects.'),
'metadataPercentComplete': ('number', 7, None, None, None, 'Download progress of metadata. 0.0 to 1.0.'), 'peersConnected': ('number', 1, None, None, None, 'Number of peers we are connected to.'),
'name': ('string', 1, None, None, None, 'Torrent name.'), 'peersFrom': ('object', 1, None, None, None, 'Object containing download peers counts for different peer types.'),
'nextAnnounceTime': ('number', 1, 7, None, None, 'Next announce time.'), 'peersGettingFromUs': ('number', 1, None, None, None, 'Number of peers we are sending data to.'),
'nextScrapeTime': ('number', 1, 7, None, None, 'Next scrape time.'), 'peersKnown': ('number', 1, 13, None, None, 'Number of peers that the tracker knows.'),
'peer-limit': ('number', 5, None, None, None, 'Maximum number of peers.'), 'peersSendingToUs': ('number', 1, None, None, None, 'Number of peers sending to us'),
'peers': ('array', 2, None, None, None, 'Array of peer objects.'), 'percentDone': ('double', 5, None, None, None, 'Download progress of selected files. 0.0 to 1.0.'),
'peersConnected': ('number', 1, None, None, None, 'Number of peers we are connected to.'), 'pieces': ('string', 5, None, None, None, 'String with base64 encoded bitfield indicating finished pieces.'),
'peersFrom': ( 'pieceCount': ('number', 1, None, None, None, 'Number of pieces.'),
'object', 1, None, None, None, 'Object containing download peers counts for different peer types.'), 'pieceSize': ('number', 1, None, None, None, 'Number of bytes in a piece.'),
'peersGettingFromUs': ('number', 1, None, None, None, 'Number of peers we are sending data to.'), 'priorities': ('array', 1, None, None, None, 'Array of file priorities.'),
'peersKnown': ('number', 1, 13, None, None, 'Number of peers that the tracker knows.'), 'queuePosition': ('number', 14, None, None, None, 'The queue position.'),
'peersSendingToUs': ('number', 1, None, None, None, 'Number of peers sending to us'), 'rateDownload': ('number', 1, None, None, None, 'Download rate in bps.'),
'percentDone': ('double', 5, None, None, None, 'Download progress of selected files. 0.0 to 1.0.'), 'rateUpload': ('number', 1, None, None, None, 'Upload rate in bps.'),
'pieces': ('string', 5, None, None, None, 'String with base64 encoded bitfield indicating finished pieces.'), 'recheckProgress': ('double', 1, None, None, None, 'Progress of recheck. 0.0 to 1.0.'),
'pieceCount': ('number', 1, None, None, None, 'Number of pieces.'), 'secondsDownloading': ('number', 15, None, None, None, ''),
'pieceSize': ('number', 1, None, None, None, 'Number of bytes in a piece.'), 'secondsSeeding': ('number', 15, None, None, None, ''),
'priorities': ('array', 1, None, None, None, 'Array of file priorities.'), 'scrapeResponse': ('string', 1, 7, None, None, 'Scrape response message.'),
'queuePosition': ('number', 14, None, None, None, 'The queue position.'), 'scrapeURL': ('string', 1, 7, None, None, 'Current scrape URL'),
'rateDownload': ('number', 1, None, None, None, 'Download rate in bps.'), 'seeders': ('number', 1, 7, None, None, 'Number of seeders reported by the tracker.'),
'rateUpload': ('number', 1, None, None, None, 'Upload rate in bps.'), 'seedIdleLimit': ('number', 10, None, None, None, 'Idle limit in minutes.'),
'recheckProgress': ('double', 1, None, None, None, 'Progress of recheck. 0.0 to 1.0.'), 'seedIdleMode': ('number', 10, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'),
'secondsDownloading': ('number', 15, None, None, None, ''), 'seedRatioLimit': ('double', 5, None, None, None, 'Seed ratio limit.'),
'secondsSeeding': ('number', 15, None, None, None, ''), 'seedRatioMode': ('number', 5, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'),
'scrapeResponse': ('string', 1, 7, None, None, 'Scrape response message.'), 'sizeWhenDone': ('number', 1, None, None, None, 'Size of the torrent download in bytes.'),
'scrapeURL': ('string', 1, 7, None, None, 'Current scrape URL'), 'startDate': ('number', 1, None, None, None, 'The date when the torrent was last started.'),
'seeders': ('number', 1, 7, None, None, 'Number of seeders reported by the tracker.'), 'status': ('number', 1, None, None, None, 'Current status, see source'),
'seedIdleLimit': ('number', 10, None, None, None, 'Idle limit in minutes.'), 'swarmSpeed': ('number', 1, 7, None, None, 'Estimated speed in Kbps in the swarm.'),
'seedIdleMode': ('number', 10, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'), 'timesCompleted': ('number', 1, 7, None, None, 'Number of successful downloads reported by the tracker.'),
'seedRatioLimit': ('double', 5, None, None, None, 'Seed ratio limit.'), 'trackers': ('array', 1, None, None, None, 'Array of tracker objects.'),
'seedRatioMode': ('number', 5, None, None, None, 'Use global (0), torrent (1), or unlimited (2) limit.'), 'trackerStats': ('object', 7, None, None, None, 'Array of object containing tracker statistics.'),
'sizeWhenDone': ('number', 1, None, None, None, 'Size of the torrent download in bytes.'), 'totalSize': ('number', 1, None, None, None, 'Total size of the torrent in bytes'),
'startDate': ('number', 1, None, None, None, 'The date when the torrent was last started.'), 'torrentFile': ('string', 5, None, None, None, 'Path to .torrent file.'),
'status': ('number', 1, None, None, None, 'Current status, see source'), 'uploadedEver': ('number', 1, None, None, None, 'Number of bytes uploaded, ever.'),
'swarmSpeed': ('number', 1, 7, None, None, 'Estimated speed in Kbps in the swarm.'), 'uploadLimit': ('number', 1, None, None, None, 'Upload limit in Kbps'),
'timesCompleted': ('number', 1, 7, None, None, 'Number of successful downloads reported by the tracker.'), 'uploadLimitMode': ('number', 1, 5, None, None, 'Upload limit mode. 0 means global, 1 means signle, 2 unlimited.'),
'trackers': ('array', 1, None, None, None, 'Array of tracker objects.'), 'uploadLimited': ('boolean', 5, None, None, None, 'Upload limit enabled.'),
'trackerStats': ('object', 7, None, None, None, 'Array of object containing tracker statistics.'), 'uploadRatio': ('double', 1, None, None, None, 'Seed ratio.'),
'totalSize': ('number', 1, None, None, None, 'Total size of the torrent in bytes'), 'wanted': ('array', 1, None, None, None, 'Array of booleans indicated wanted files.'),
'torrentFile': ('string', 5, None, None, None, 'Path to .torrent file.'), 'webseeds': ('array', 1, None, None, None, 'Array of webseeds objects'),
'uploadedEver': ('number', 1, None, None, None, 'Number of bytes uploaded, ever.'), 'webseedsSendingToUs': ('number', 1, None, None, None, 'Number of webseeds seeding to us.'),
'uploadLimit': ('number', 1, None, None, None, 'Upload limit in Kbps'),
'uploadLimitMode': (
'number', 1, 5, None, None, 'Upload limit mode. 0 means global, 1 means signle, 2 unlimited.'),
'uploadLimited': ('boolean', 5, None, None, None, 'Upload limit enabled.'),
'uploadRatio': ('double', 1, None, None, None, 'Seed ratio.'),
'wanted': ('array', 1, None, None, None, 'Array of booleans indicated wanted files.'),
'webseeds': ('array', 1, None, None, None, 'Array of webseeds objects'),
'webseedsSendingToUs': ('number', 1, None, None, None, 'Number of webseeds seeding to us.'),
}, },
'set': { 'set': {
'bandwidthPriority': ('number', 5, None, None, None, 'Priority for this transfer.'), 'bandwidthPriority': ('number', 5, None, None, None, 'Priority for this transfer.'),
'downloadLimit': ('number', 5, None, 'speed-limit-down', None, 'Set the speed limit for download in Kib/s.'), 'downloadLimit': ('number', 5, None, 'speed-limit-down', None, 'Set the speed limit for download in Kib/s.'),
'downloadLimited': ('boolean', 5, None, 'speed-limit-down-enabled', None, 'Enable download speed limiter.'), 'downloadLimited': ('boolean', 5, None, 'speed-limit-down-enabled', None, 'Enable download speed limiter.'),
'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."), 'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."),
'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."), 'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."),
'honorsSessionLimits': ('boolean', 5, None, None, None, 'honorsSessionLimits': ('boolean', 5, None, None, None, "Enables or disables the transfer to honour the upload limit set in the session."),
"Enables or disables the transfer to honour the upload limit set in the session."), 'location': ('array', 1, None, None, None, 'Local download location.'),
'location': ('array', 1, None, None, None, 'Local download location.'), 'peer-limit': ('number', 1, None, None, None, 'The peer limit for the torrents.'),
'peer-limit': ('number', 1, None, None, None, 'The peer limit for the torrents.'), 'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."),
'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."), 'priority-low': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
'priority-low': ('array', 1, None, None, None, "A list of file id's that should have normal priority."), 'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have low priority."),
'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have low priority."), 'queuePosition': ('number', 14, None, None, None, 'Position of this transfer in its queue.'),
'queuePosition': ('number', 14, None, None, None, 'Position of this transfer in its queue.'), 'seedIdleLimit': ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'),
'seedIdleLimit': ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'), 'seedIdleMode': ('number', 10, None, None, None, 'Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
'seedIdleMode': ('number', 10, None, None, None, 'seedRatioLimit': ('double', 5, None, None, None, 'Seeding ratio.'),
'Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'), 'seedRatioMode': ('number', 5, None, None, None, 'Which ratio to use. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
'seedRatioLimit': ('double', 5, None, None, None, 'Seeding ratio.'), 'speed-limit-down': ('number', 1, 5, None, 'downloadLimit', 'Set the speed limit for download in Kib/s.'),
'seedRatioMode': ('number', 5, None, None, None, 'speed-limit-down-enabled': ('boolean', 1, 5, None, 'downloadLimited', 'Enable download speed limiter.'),
'Which ratio to use. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'), 'speed-limit-up': ('number', 1, 5, None, 'uploadLimit', 'Set the speed limit for upload in Kib/s.'),
'speed-limit-down': ('number', 1, 5, None, 'downloadLimit', 'Set the speed limit for download in Kib/s.'), 'speed-limit-up-enabled': ('boolean', 1, 5, None, 'uploadLimited', 'Enable upload speed limiter.'),
'speed-limit-down-enabled': ('boolean', 1, 5, None, 'downloadLimited', 'Enable download speed limiter.'), 'trackerAdd': ('array', 10, None, None, None, 'Array of string with announce URLs to add.'),
'speed-limit-up': ('number', 1, 5, None, 'uploadLimit', 'Set the speed limit for upload in Kib/s.'), 'trackerRemove': ('array', 10, None, None, None, 'Array of ids of trackers to remove.'),
'speed-limit-up-enabled': ('boolean', 1, 5, None, 'uploadLimited', 'Enable upload speed limiter.'), 'trackerReplace': ('array', 10, None, None, None, 'Array of (id, url) tuples where the announce URL should be replaced.'),
'trackerAdd': ('array', 10, None, None, None, 'Array of string with announce URLs to add.'), 'uploadLimit': ('number', 5, None, 'speed-limit-up', None, 'Set the speed limit for upload in Kib/s.'),
'trackerRemove': ('array', 10, None, None, None, 'Array of ids of trackers to remove.'), 'uploadLimited': ('boolean', 5, None, 'speed-limit-up-enabled', None, 'Enable upload speed limiter.'),
'trackerReplace': (
'array', 10, None, None, None, 'Array of (id, url) tuples where the announce URL should be replaced.'),
'uploadLimit': ('number', 5, None, 'speed-limit-up', None, 'Set the speed limit for upload in Kib/s.'),
'uploadLimited': ('boolean', 5, None, 'speed-limit-up-enabled', None, 'Enable upload speed limiter.'),
}, },
'add': { 'add': {
'bandwidthPriority': ('number', 8, None, None, None, 'Priority for this transfer.'), 'bandwidthPriority': ('number', 8, None, None, None, 'Priority for this transfer.'),
'download-dir': ( 'download-dir': ('string', 1, None, None, None, 'The directory where the downloaded contents will be saved in.'),
'string', 1, None, None, None, 'The directory where the downloaded contents will be saved in.'), 'cookies': ('string', 13, None, None, None, 'One or more HTTP cookie(s).'),
'cookies': ('string', 13, None, None, None, 'One or more HTTP cookie(s).'), 'filename': ('string', 1, None, None, None, "A file path or URL to a torrent file or a magnet link."),
'filename': ('string', 1, None, None, None, "A file path or URL to a torrent file or a magnet link."), 'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."),
'files-wanted': ('array', 1, None, None, None, "A list of file id's that should be downloaded."), 'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."),
'files-unwanted': ('array', 1, None, None, None, "A list of file id's that shouldn't be downloaded."), 'metainfo': ('string', 1, None, None, None, 'The content of a torrent file, base64 encoded.'),
'metainfo': ('string', 1, None, None, None, 'The content of a torrent file, base64 encoded.'), 'paused': ('boolean', 1, None, None, None, 'If True, does not start the transfer when added.'),
'paused': ('boolean', 1, None, None, None, 'If True, does not start the transfer when added.'), 'peer-limit': ('number', 1, None, None, None, 'Maximum number of peers allowed.'),
'peer-limit': ('number', 1, None, None, None, 'Maximum number of peers allowed.'), 'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."),
'priority-high': ('array', 1, None, None, None, "A list of file id's that should have high priority."), 'priority-low': ('array', 1, None, None, None, "A list of file id's that should have low priority."),
'priority-low': ('array', 1, None, None, None, "A list of file id's that should have low priority."), 'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
'priority-normal': ('array', 1, None, None, None, "A list of file id's that should have normal priority."),
} }
} }
# Arguments for session methods # Arguments for session methods
SESSION_ARGS = { SESSION_ARGS = {
'get': { 'get': {
"alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'), "alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'),
"alt-speed-enabled": ( "alt-speed-enabled": ('boolean', 5, None, None, None, 'True if alternate global download speed limiter is ebabled.'),
'boolean', 5, None, None, None, 'True if alternate global download speed limiter is ebabled.'), "alt-speed-time-begin": ('number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'),
"alt-speed-time-begin": ( "alt-speed-time-enabled": ('boolean', 5, None, None, None, 'True if alternate speeds scheduling is enabled.'),
'number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'), "alt-speed-time-end": ('number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'),
"alt-speed-time-enabled": ('boolean', 5, None, None, None, 'True if alternate speeds scheduling is enabled.'), "alt-speed-time-day": ('number', 5, None, None, None, 'Days alternate speeds scheduling is enabled.'),
"alt-speed-time-end": ( "alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s)'),
'number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'), "blocklist-enabled": ('boolean', 5, None, None, None, 'True when blocklist is enabled.'),
"alt-speed-time-day": ('number', 5, None, None, None, 'Days alternate speeds scheduling is enabled.'), "blocklist-size": ('number', 5, None, None, None, 'Number of rules in the blocklist'),
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s)'), "blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
"blocklist-enabled": ('boolean', 5, None, None, None, 'True when blocklist is enabled.'), "cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
"blocklist-size": ('number', 5, None, None, None, 'Number of rules in the blocklist'), "config-dir": ('string', 8, None, None, None, 'location of transmissions configuration directory'),
"blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'), "dht-enabled": ('boolean', 6, None, None, None, 'True if DHT enabled.'),
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'), "download-dir": ('string', 1, None, None, None, 'The download directory.'),
"config-dir": ('string', 8, None, None, None, 'location of transmissions configuration directory'), "download-dir-free-space": ('number', 12, None, None, None, 'Free space in the download directory, in bytes'),
"dht-enabled": ('boolean', 6, None, None, None, 'True if DHT enabled.'), "download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'),
"download-dir": ('string', 1, None, None, None, 'The download directory.'), "download-queue-enabled": ('boolean', 14, None, None, None, 'True if the download queue is enabled.'),
"download-dir-free-space": ('number', 12, None, None, None, 'Free space in the download directory, in bytes'), "encryption": ('string', 1, None, None, None, 'Encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
"download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'), "idle-seeding-limit": ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'),
"download-queue-enabled": ('boolean', 14, None, None, None, 'True if the download queue is enabled.'), "idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'True if the seed activity limit is enabled.'),
"encryption": ( "incomplete-dir": ('string', 7, None, None, None, 'The path to the directory for incomplete torrent transfer data.'),
'string', 1, None, None, None, 'Encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'), "incomplete-dir-enabled": ('boolean', 7, None, None, None, 'True if the incomplete dir is enabled.'),
"idle-seeding-limit": ('number', 10, None, None, None, 'Seed inactivity limit in minutes.'), "lpd-enabled": ('boolean', 9, None, None, None, 'True if local peer discovery is enabled.'),
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'True if the seed activity limit is enabled.'), "peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'),
"incomplete-dir": ( "peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'),
'string', 7, None, None, None, 'The path to the directory for incomplete torrent transfer data.'), "peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'),
"incomplete-dir-enabled": ('boolean', 7, None, None, None, 'True if the incomplete dir is enabled.'), "pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'True if PEX is allowed.'),
"lpd-enabled": ('boolean', 9, None, None, None, 'True if local peer discovery is enabled.'), "pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'True if PEX is enabled.'),
"peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'), "port": ('number', 1, 5, None, 'peer-port', 'Peer port.'),
"peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'), "peer-port": ('number', 5, None, 'port', None, 'Peer port.'),
"peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'), "peer-port-random-on-start": ('boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
"pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'True if PEX is allowed.'), "port-forwarding-enabled": ('boolean', 1, None, None, None, 'True if port forwarding is enabled.'),
"pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'True if PEX is enabled.'), "queue-stalled-minutes": ('number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
"port": ('number', 1, 5, None, 'peer-port', 'Peer port.'), "queue-stalled-enabled": ('boolean', 14, None, None, None, 'True if stalled tracking of transfers is enabled.'),
"peer-port": ('number', 5, None, 'port', None, 'Peer port.'), "rename-partial-files": ('boolean', 8, None, None, None, 'True if ".part" is appended to incomplete files'),
"peer-port-random-on-start": ( "rpc-version": ('number', 4, None, None, None, 'Transmission RPC API Version.'),
'boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'), "rpc-version-minimum": ('number', 4, None, None, None, 'Minimum accepted RPC API Version.'),
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'True if port forwarding is enabled.'), "script-torrent-done-enabled": ('boolean', 9, None, None, None, 'True if the done script is enabled.'),
"queue-stalled-minutes": ( "script-torrent-done-filename": ('string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
'number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'), "seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'True if stalled tracking of transfers is enabled.'), "seedRatioLimited": ('boolean', 5, None, None, None, 'True if seed ration limit is enabled.'),
"rename-partial-files": ('boolean', 8, None, None, None, 'True if ".part" is appended to incomplete files'), "seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'),
"rpc-version": ('number', 4, None, None, None, 'Transmission RPC API Version.'), "seed-queue-enabled": ('boolean', 14, None, None, None, 'True if upload queue is enabled.'),
"rpc-version-minimum": ('number', 4, None, None, None, 'Minimum accepted RPC API Version.'), "speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'True if the done script is enabled.'), "speed-limit-down-enabled": ('boolean', 1, None, None, None, 'True if the download speed is limited.'),
"script-torrent-done-filename": ( "speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
'string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'), "speed-limit-up-enabled": ('boolean', 1, None, None, None, 'True if the upload speed is limited.'),
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'), "start-added-torrents": ('boolean', 9, None, None, None, 'When true uploaded torrents will start right away.'),
"seedRatioLimited": ('boolean', 5, None, None, None, 'True if seed ration limit is enabled.'), "trash-original-torrent-files": ('boolean', 9, None, None, None, 'When true added .torrent files will be deleted.'),
"seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'), 'units': ('object', 10, None, None, None, 'An object containing units for size and speed.'),
"seed-queue-enabled": ('boolean', 14, None, None, None, 'True if upload queue is enabled.'), 'utp-enabled': ('boolean', 13, None, None, None, 'True if Micro Transport Protocol (UTP) is enabled.'),
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'), "version": ('string', 3, None, None, None, 'Transmission version.'),
"speed-limit-down-enabled": ('boolean', 1, None, None, None, 'True if the download speed is limited.'),
"speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'True if the upload speed is limited.'),
"start-added-torrents": ('boolean', 9, None, None, None, 'When true uploaded torrents will start right away.'),
"trash-original-torrent-files": (
'boolean', 9, None, None, None, 'When true added .torrent files will be deleted.'),
'units': ('object', 10, None, None, None, 'An object containing units for size and speed.'),
'utp-enabled': ('boolean', 13, None, None, None, 'True if Micro Transport Protocol (UTP) is enabled.'),
"version": ('string', 3, None, None, None, 'Transmission version.'),
}, },
'set': { 'set': {
"alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'), "alt-speed-down": ('number', 5, None, None, None, 'Alternate session download speed limit (in Kib/s).'),
"alt-speed-enabled": ('boolean', 5, None, None, None, 'Enables alternate global download speed limiter.'), "alt-speed-enabled": ('boolean', 5, None, None, None, 'Enables alternate global download speed limiter.'),
"alt-speed-time-begin": ( "alt-speed-time-begin": ('number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'),
'number', 5, None, None, None, 'Time when alternate speeds should be enabled. Minutes after midnight.'), "alt-speed-time-enabled": ('boolean', 5, None, None, None, 'Enables alternate speeds scheduling.'),
"alt-speed-time-enabled": ('boolean', 5, None, None, None, 'Enables alternate speeds scheduling.'), "alt-speed-time-end": ('number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'),
"alt-speed-time-end": ( "alt-speed-time-day": ('number', 5, None, None, None, 'Enables alternate speeds scheduling these days.'),
'number', 5, None, None, None, 'Time when alternate speeds should be disabled. Minutes after midnight.'), "alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s).'),
"alt-speed-time-day": ('number', 5, None, None, None, 'Enables alternate speeds scheduling these days.'), "blocklist-enabled": ('boolean', 5, None, None, None, 'Enables the block list'),
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s).'), "blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
"blocklist-enabled": ('boolean', 5, None, None, None, 'Enables the block list'), "cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
"blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'), "dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'),
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'), "download-dir": ('string', 1, None, None, None, 'Set the session download directory.'),
"dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'), "download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'),
"download-dir": ('string', 1, None, None, None, 'Set the session download directory.'), "download-queue-enabled": ('boolean', 14, None, None, None, 'Enables download queue.'),
"download-queue-size": ('number', 14, None, None, None, 'Number of slots in the download queue.'), "encryption": ('string', 1, None, None, None, 'Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
"download-queue-enabled": ('boolean', 14, None, None, None, 'Enables download queue.'), "idle-seeding-limit": ('number', 10, None, None, None, 'The default seed inactivity limit in minutes.'),
"encryption": ('string', 1, None, None, None, "idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'Enables the default seed inactivity limit'),
'Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'), "incomplete-dir": ('string', 7, None, None, None, 'The path to the directory of incomplete transfer data.'),
"idle-seeding-limit": ('number', 10, None, None, None, 'The default seed inactivity limit in minutes.'), "incomplete-dir-enabled": ('boolean', 7, None, None, None, 'Enables the incomplete transfer data directory. Otherwise data for incomplete transfers are stored in the download target.'),
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'Enables the default seed inactivity limit'), "lpd-enabled": ('boolean', 9, None, None, None, 'Enables local peer discovery for public torrents.'),
"incomplete-dir": ('string', 7, None, None, None, 'The path to the directory of incomplete transfer data.'), "peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'),
"incomplete-dir-enabled": ('boolean', 7, None, None, None, "peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'),
'Enables the incomplete transfer data directory. Otherwise data for incomplete transfers are stored in the download target.'), "peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'),
"lpd-enabled": ('boolean', 9, None, None, None, 'Enables local peer discovery for public torrents.'), "pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'Allowing PEX in public torrents.'),
"peer-limit": ('number', 1, 5, None, 'peer-limit-global', 'Maximum number of peers.'), "pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'Allowing PEX in public torrents.'),
"peer-limit-global": ('number', 5, None, 'peer-limit', None, 'Maximum number of peers.'), "port": ('number', 1, 5, None, 'peer-port', 'Peer port.'),
"peer-limit-per-torrent": ('number', 5, None, None, None, 'Maximum number of peers per transfer.'), "peer-port": ('number', 5, None, 'port', None, 'Peer port.'),
"pex-allowed": ('boolean', 1, 5, None, 'pex-enabled', 'Allowing PEX in public torrents.'), "peer-port-random-on-start": ('boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
"pex-enabled": ('boolean', 5, None, 'pex-allowed', None, 'Allowing PEX in public torrents.'), "port-forwarding-enabled": ('boolean', 1, None, None, None, 'Enables port forwarding.'),
"port": ('number', 1, 5, None, 'peer-port', 'Peer port.'), "rename-partial-files": ('boolean', 8, None, None, None, 'Appends ".part" to incomplete files'),
"peer-port": ('number', 5, None, 'port', None, 'Peer port.'), "queue-stalled-minutes": ('number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
"peer-port-random-on-start": ( "queue-stalled-enabled": ('boolean', 14, None, None, None, 'Enable tracking of stalled transfers.'),
'boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'), "script-torrent-done-enabled": ('boolean', 9, None, None, None, 'Whether or not to call the "done" script.'),
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'Enables port forwarding.'), "script-torrent-done-filename": ('string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
"rename-partial-files": ('boolean', 8, None, None, None, 'Appends ".part" to incomplete files'), "seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'),
"queue-stalled-minutes": ( "seed-queue-enabled": ('boolean', 14, None, None, None, 'Enables upload queue.'),
'number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'), "seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'Enable tracking of stalled transfers.'), "seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'),
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'Whether or not to call the "done" script.'), "speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
"script-torrent-done-filename": ( "speed-limit-down-enabled": ('boolean', 1, None, None, None, 'Enables download speed limiting.'),
'string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'), "speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
"seed-queue-size": ('number', 14, None, None, None, 'Number of slots in the upload queue.'), "speed-limit-up-enabled": ('boolean', 1, None, None, None, 'Enables upload speed limiting.'),
"seed-queue-enabled": ('boolean', 14, None, None, None, 'Enables upload queue.'), "start-added-torrents": ('boolean', 9, None, None, None, 'Added torrents will be started right away.'),
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'), "trash-original-torrent-files": ('boolean', 9, None, None, None, 'The .torrent file of added torrents will be deleted.'),
"seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'), 'utp-enabled': ('boolean', 13, None, None, None, 'Enables Micro Transport Protocol (UTP).'),
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
"speed-limit-down-enabled": ('boolean', 1, None, None, None, 'Enables download speed limiting.'),
"speed-limit-up": ('number', 1, None, None, None, 'Upload speed limit (in Kib/s).'),
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'Enables upload speed limiting.'),
"start-added-torrents": ('boolean', 9, None, None, None, 'Added torrents will be started right away.'),
"trash-original-torrent-files": (
'boolean', 9, None, None, None, 'The .torrent file of added torrents will be deleted.'),
'utp-enabled': ('boolean', 13, None, None, None, 'Enables Micro Transport Protocol (UTP).'),
}, },
} }

View file

@ -2,15 +2,13 @@
# Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
from six import integer_types, string_types from six import string_types, integer_types
class TransmissionError(Exception): class TransmissionError(Exception):
""" """
This exception is raised when there has occurred an error related to This exception is raised when there has occurred an error related to
communication with Transmission. It is a subclass of Exception. communication with Transmission. It is a subclass of Exception.
""" """
def __init__(self, message='', original=None): def __init__(self, message='', original=None):
Exception.__init__(self) Exception.__init__(self)
self.message = message self.message = message
@ -19,17 +17,15 @@ class TransmissionError(Exception):
def __str__(self): def __str__(self):
if self.original: if self.original:
original_name = type(self.original).__name__ original_name = type(self.original).__name__
return '{0} Original exception: {1}, "{2}"'.format(self.message, original_name, str(self.original)) return '%s Original exception: %s, "%s"' % (self.message, original_name, str(self.original))
else: else:
return self.message return self.message
class HTTPHandlerError(Exception): class HTTPHandlerError(Exception):
""" """
This exception is raised when there has occurred an error related to This exception is raised when there has occurred an error related to
the HTTP handler. It is a subclass of Exception. the HTTP handler. It is a subclass of Exception.
""" """
def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None): def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None):
Exception.__init__(self) Exception.__init__(self)
self.url = '' self.url = ''
@ -49,10 +45,10 @@ class HTTPHandlerError(Exception):
self.data = httpdata self.data = httpdata
def __repr__(self): def __repr__(self):
return '<HTTPHandlerError {0:d}, {1}>'.format(self.code, self.message) return '<HTTPHandlerError %d, %s>' % (self.code, self.message)
def __str__(self): def __str__(self):
return 'HTTPHandlerError {0:d}: {1}'.format(self.code, self.message) return 'HTTPHandlerError %d: %s' % (self.code, self.message)
def __unicode__(self): def __unicode__(self):
return 'HTTPHandlerError {0:d}: {1}'.format(self.code, self.message) return 'HTTPHandlerError %d: %s' % (self.code, self.message)

View file

@ -4,24 +4,25 @@
import sys import sys
from six.moves.http_client import BadStatusLine from transmissionrpc.error import HTTPHandlerError
from six.moves.urllib_error import HTTPError, URLError
from six.moves.urllib_request import (
HTTPBasicAuthHandler,
HTTPDigestAuthHandler,
HTTPPasswordMgrWithDefaultRealm,
Request,
build_opener,
)
from .error import HTTPHandlerError from six import PY3
if PY3:
from urllib.request import Request, build_opener, \
HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, HTTPDigestAuthHandler
from urllib.error import HTTPError, URLError
from http.client import BadStatusLine
else:
from urllib2 import Request, build_opener, \
HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, HTTPDigestAuthHandler
from urllib2 import HTTPError, URLError
from httplib import BadStatusLine
class HTTPHandler(object): class HTTPHandler(object):
""" """
Prototype for HTTP handling. Prototype for HTTP handling.
""" """
def set_authentication(self, uri, login, password): def set_authentication(self, uri, login, password):
""" """
Transmission use basic authentication in earlier versions and digest Transmission use basic authentication in earlier versions and digest
@ -44,12 +45,10 @@ class HTTPHandler(object):
""" """
raise NotImplementedError("Bad HTTPHandler, failed to implement request.") raise NotImplementedError("Bad HTTPHandler, failed to implement request.")
class DefaultHTTPHandler(HTTPHandler): class DefaultHTTPHandler(HTTPHandler):
""" """
The default HTTP handler provided with transmissionrpc. The default HTTP handler provided with transmissionrpc.
""" """
def __init__(self): def __init__(self):
HTTPHandler.__init__(self) HTTPHandler.__init__(self)
self.http_opener = build_opener() self.http_opener = build_opener()
@ -77,7 +76,7 @@ class DefaultHTTPHandler(HTTPHandler):
if hasattr(error.reason, 'args') and isinstance(error.reason.args, tuple) and len(error.reason.args) == 2: if hasattr(error.reason, 'args') and isinstance(error.reason.args, tuple) and len(error.reason.args) == 2:
raise HTTPHandlerError(httpcode=error.reason.args[0], httpmsg=error.reason.args[1]) raise HTTPHandlerError(httpcode=error.reason.args[0], httpmsg=error.reason.args[1])
else: else:
raise HTTPHandlerError(httpmsg='urllib2.URLError: {error.reason}'.format(error=error)) raise HTTPHandlerError(httpmsg='urllib2.URLError: %s' % (error.reason))
except BadStatusLine as error: except BadStatusLine as error:
raise HTTPHandlerError(httpmsg='httplib.BadStatusLine: {error.line}'.format(error=error)) raise HTTPHandlerError(httpmsg='httplib.BadStatusLine: %s' % (error.line))
return response.read().decode('utf-8') return response.read().decode('utf-8')

View file

@ -2,10 +2,9 @@
# Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
from six import integer_types, iteritems from transmissionrpc.utils import Field
from .utils import Field
from six import iteritems, integer_types
class Session(object): class Session(object):
""" """
@ -27,12 +26,12 @@ class Session(object):
try: try:
return self._fields[name].value return self._fields[name].value
except KeyError: except KeyError:
raise AttributeError('No attribute {0}'.format(name)) raise AttributeError('No attribute %s' % name)
def __str__(self): def __str__(self):
text = '' text = ''
for key in sorted(self._fields.keys()): for key in sorted(self._fields.keys()):
text += "{0:32}: {1}\n".format(key[-32:], self._fields[key].value) text += "% 32s: %s\n" % (key[-32:], self._fields[key].value)
return text return text
def _update_fields(self, other): def _update_fields(self, other):

View file

@ -2,27 +2,25 @@
# Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import datetime import sys, datetime
import sys
from six import integer_types, iteritems, string_types, text_type from transmissionrpc.constants import PRIORITY, RATIO_LIMIT, IDLE_LIMIT
from transmissionrpc.utils import Field, format_timedelta
from .constants import IDLE_LIMIT, PRIORITY, RATIO_LIMIT from six import integer_types, string_types, text_type, iteritems
from .utils import Field, format_timedelta
def get_status_old(code): def get_status_old(code):
"""Get the torrent status using old status codes""" """Get the torrent status using old status codes"""
mapping = { mapping = {
(1 << 0): 'check pending', (1<<0): 'check pending',
(1 << 1): 'checking', (1<<1): 'checking',
(1 << 2): 'downloading', (1<<2): 'downloading',
(1 << 3): 'seeding', (1<<3): 'seeding',
(1 << 4): 'stopped', (1<<4): 'stopped',
} }
return mapping[code] return mapping[code]
def get_status_new(code): def get_status_new(code):
"""Get the torrent status using new status codes""" """Get the torrent status using new status codes"""
mapping = { mapping = {
@ -36,7 +34,6 @@ def get_status_new(code):
} }
return mapping[code] return mapping[code]
class Torrent(object): class Torrent(object):
""" """
Torrent is a class holding the data received from Transmission regarding a bittorrent transfer. Torrent is a class holding the data received from Transmission regarding a bittorrent transfer.
@ -74,14 +71,14 @@ class Torrent(object):
tid = self._fields['id'].value tid = self._fields['id'].value
name = self._get_name_string() name = self._get_name_string()
if isinstance(name, str): if isinstance(name, str):
return '<Torrent {0:d} \"{1}\">'.format(tid, name) return '<Torrent %d \"%s\">' % (tid, name)
else: else:
return '<Torrent {0:d}>'.format(tid) return '<Torrent %d>' % (tid)
def __str__(self): def __str__(self):
name = self._get_name_string() name = self._get_name_string()
if isinstance(name, str): if isinstance(name, str):
return 'Torrent \"{0}\"'.format(name) return 'Torrent \"%s\"' % (name)
else: else:
return 'Torrent' return 'Torrent'
@ -92,7 +89,7 @@ class Torrent(object):
try: try:
return self._fields[name].value return self._fields[name].value
except KeyError: except KeyError:
raise AttributeError('No attribute {0}'.format(name)) raise AttributeError('No attribute %s' % name)
def _rpc_version(self): def _rpc_version(self):
"""Get the Transmission RPC API version.""" """Get the Transmission RPC API version."""
@ -102,9 +99,8 @@ class Torrent(object):
def _dirty_fields(self): def _dirty_fields(self):
"""Enumerate changed fields""" """Enumerate changed fields"""
outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'peer_limit', 'queuePosition', outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'peer_limit', 'queuePosition'
'seedIdleLimit', 'seedIdleMode', 'seedRatioLimit', 'seedRatioMode', 'uploadLimit', , 'seedIdleLimit', 'seedIdleMode', 'seedRatioLimit', 'seedRatioMode', 'uploadLimit', 'uploadLimited']
'uploadLimited']
fields = [] fields = []
for key in outgoing_keys: for key in outgoing_keys:
if key in self._fields and self._fields[key].dirty: if key in self._fields and self._fields[key].dirty:
@ -125,6 +121,7 @@ class Torrent(object):
""" """
Update the torrent data from a Transmission JSON-RPC arguments dictionary Update the torrent data from a Transmission JSON-RPC arguments dictionary
""" """
fields = None
if isinstance(other, dict): if isinstance(other, dict):
for key, value in iteritems(other): for key, value in iteritems(other):
self._fields[key.replace('-', '_')] = Field(value, False) self._fields[key.replace('-', '_')] = Field(value, False)
@ -267,14 +264,13 @@ class Torrent(object):
self._fields['downloadLimited'] = Field(True, True) self._fields['downloadLimited'] = Field(True, True)
self._fields['downloadLimit'] = Field(limit, True) self._fields['downloadLimit'] = Field(limit, True)
self._push() self._push()
elif limit is None: elif limit == None:
self._fields['downloadLimited'] = Field(False, True) self._fields['downloadLimited'] = Field(False, True)
self._push() self._push()
else: else:
raise ValueError("Not a valid limit") raise ValueError("Not a valid limit")
download_limit = property(_get_download_limit, _set_download_limit, None, download_limit = property(_get_download_limit, _set_download_limit, None, "Download limit in Kbps or None. This is a mutator.")
"Download limit in Kbps or None. This is a mutator.")
def _get_peer_limit(self): def _get_peer_limit(self):
""" """
@ -311,7 +307,7 @@ class Torrent(object):
self._push() self._push()
priority = property(_get_priority, _set_priority, None priority = property(_get_priority, _set_priority, None
, "Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator.") , "Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator.")
def _get_seed_idle_limit(self): def _get_seed_idle_limit(self):
""" """
@ -330,7 +326,7 @@ class Torrent(object):
raise ValueError("Not a valid limit") raise ValueError("Not a valid limit")
seed_idle_limit = property(_get_seed_idle_limit, _set_seed_idle_limit, None seed_idle_limit = property(_get_seed_idle_limit, _set_seed_idle_limit, None
, "Torrent seed idle limit in minutes. Also see seed_idle_mode. This is a mutator.") , "Torrent seed idle limit in minutes. Also see seed_idle_mode. This is a mutator.")
def _get_seed_idle_mode(self): def _get_seed_idle_mode(self):
""" """
@ -349,7 +345,7 @@ class Torrent(object):
raise ValueError("Not a valid limit") raise ValueError("Not a valid limit")
seed_idle_mode = property(_get_seed_idle_mode, _set_seed_idle_mode, None, seed_idle_mode = property(_get_seed_idle_mode, _set_seed_idle_mode, None,
""" """
Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'. Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'.
* global, use session seed idle limit. * global, use session seed idle limit.
@ -358,7 +354,7 @@ class Torrent(object):
This is a mutator. This is a mutator.
""" """
) )
def _get_seed_ratio_limit(self): def _get_seed_ratio_limit(self):
""" """
@ -377,7 +373,7 @@ class Torrent(object):
raise ValueError("Not a valid limit") raise ValueError("Not a valid limit")
seed_ratio_limit = property(_get_seed_ratio_limit, _set_seed_ratio_limit, None seed_ratio_limit = property(_get_seed_ratio_limit, _set_seed_ratio_limit, None
, "Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator.") , "Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator.")
def _get_seed_ratio_mode(self): def _get_seed_ratio_mode(self):
""" """
@ -396,7 +392,7 @@ class Torrent(object):
raise ValueError("Not a valid limit") raise ValueError("Not a valid limit")
seed_ratio_mode = property(_get_seed_ratio_mode, _set_seed_ratio_mode, None, seed_ratio_mode = property(_get_seed_ratio_mode, _set_seed_ratio_mode, None,
""" """
Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'. Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
* global, use session seed ratio limit. * global, use session seed ratio limit.
@ -405,7 +401,7 @@ class Torrent(object):
This is a mutator. This is a mutator.
""" """
) )
def _get_upload_limit(self): def _get_upload_limit(self):
""" """
@ -426,14 +422,13 @@ class Torrent(object):
self._fields['uploadLimited'] = Field(True, True) self._fields['uploadLimited'] = Field(True, True)
self._fields['uploadLimit'] = Field(limit, True) self._fields['uploadLimit'] = Field(limit, True)
self._push() self._push()
elif limit is None: elif limit == None:
self._fields['uploadLimited'] = Field(False, True) self._fields['uploadLimited'] = Field(False, True)
self._push() self._push()
else: else:
raise ValueError("Not a valid limit") raise ValueError("Not a valid limit")
upload_limit = property(_get_upload_limit, _set_upload_limit, None, upload_limit = property(_get_upload_limit, _set_upload_limit, None, "Upload limit in Kbps or None. This is a mutator.")
"Upload limit in Kbps or None. This is a mutator.")
def _get_queue_position(self): def _get_queue_position(self):
"""Get the queue position for this torrent.""" """Get the queue position for this torrent."""

View file

@ -2,19 +2,15 @@
# Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import datetime import socket, datetime, logging
import logging
import socket
from collections import namedtuple from collections import namedtuple
import transmissionrpc.constants as constants
from transmissionrpc.constants import LOGGER
from six import iteritems, string_types from six import string_types, iteritems
from . import constants
from .constants import LOGGER
UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'] UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
def format_size(size): def format_size(size):
""" """
Format byte size into IEC prefixes, B, KiB, MiB ... Format byte size into IEC prefixes, B, KiB, MiB ...
@ -24,16 +20,14 @@ def format_size(size):
while size >= 1024.0 and i < len(UNITS): while size >= 1024.0 and i < len(UNITS):
i += 1 i += 1
size /= 1024.0 size /= 1024.0
return size, UNITS[i] return (size, UNITS[i])
def format_speed(size): def format_speed(size):
""" """
Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ... Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ...
""" """
(size, unit) = format_size(size) (size, unit) = format_size(size)
return size, '{unit}/s'.format(unit=unit) return (size, unit + '/s')
def format_timedelta(delta): def format_timedelta(delta):
""" """
@ -41,8 +35,7 @@ def format_timedelta(delta):
""" """
minutes, seconds = divmod(delta.seconds, 60) minutes, seconds = divmod(delta.seconds, 60)
hours, minutes = divmod(minutes, 60) hours, minutes = divmod(minutes, 60)
return '{0:d} {1:02d}:{2:02d}:{3:02d}'.format(delta.days, hours, minutes, seconds) return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds)
def format_timestamp(timestamp, utc=False): def format_timestamp(timestamp, utc=False):
""" """
@ -57,14 +50,12 @@ def format_timestamp(timestamp, utc=False):
else: else:
return '-' return '-'
class INetAddressError(Exception): class INetAddressError(Exception):
""" """
Error parsing / generating a internet address. Error parsing / generating a internet address.
""" """
pass pass
def inet_address(address, default_port, default_address='localhost'): def inet_address(address, default_port, default_address='localhost'):
""" """
Parse internet address. Parse internet address.
@ -81,19 +72,18 @@ def inet_address(address, default_port, default_address='localhost'):
try: try:
port = int(addr[1]) port = int(addr[1])
except ValueError: except ValueError:
raise INetAddressError('Invalid address "{0}".'.format(address)) raise INetAddressError('Invalid address "%s".' % address)
if len(addr[0]) == 0: if len(addr[0]) == 0:
addr = default_address addr = default_address
else: else:
addr = addr[0] addr = addr[0]
else: else:
raise INetAddressError('Invalid address "{0}".'.format(address)) raise INetAddressError('Invalid address "%s".' % address)
try: try:
socket.getaddrinfo(addr, port, socket.AF_INET, socket.SOCK_STREAM) socket.getaddrinfo(addr, port, socket.AF_INET, socket.SOCK_STREAM)
except socket.gaierror: except socket.gaierror:
raise INetAddressError('Cannot look up address "{0}".'.format(address)) raise INetAddressError('Cannot look up address "%s".' % address)
return addr, port return (addr, port)
def rpc_bool(arg): def rpc_bool(arg):
""" """
@ -106,31 +96,27 @@ def rpc_bool(arg):
arg = arg.lower() in ['true', 'yes'] arg = arg.lower() in ['true', 'yes']
return 1 if bool(arg) else 0 return 1 if bool(arg) else 0
TR_TYPE_MAP = { TR_TYPE_MAP = {
'number': int, 'number' : int,
'string': str, 'string' : str,
'double': float, 'double': float,
'boolean': rpc_bool, 'boolean' : rpc_bool,
'array': list, 'array': list,
'object': dict 'object': dict
} }
def make_python_name(name): def make_python_name(name):
""" """
Convert Transmission RPC name to python compatible name. Convert Transmission RPC name to python compatible name.
""" """
return name.replace('-', '_') return name.replace('-', '_')
def make_rpc_name(name): def make_rpc_name(name):
""" """
Convert python compatible name to Transmission RPC name. Convert python compatible name to Transmission RPC name.
""" """
return name.replace('_', '-') return name.replace('_', '-')
def argument_value_convert(method, argument, value, rpc_version): def argument_value_convert(method, argument, value, rpc_version):
""" """
Check and fix Transmission RPC issues with regards to methods, arguments and values. Check and fix Transmission RPC issues with regards to methods, arguments and values.
@ -140,7 +126,7 @@ def argument_value_convert(method, argument, value, rpc_version):
elif method in ('session-get', 'session-set'): elif method in ('session-get', 'session-set'):
args = constants.SESSION_ARGS[method[-3:]] args = constants.SESSION_ARGS[method[-3:]]
else: else:
return ValueError('Method "{0}" not supported'.format(method)) return ValueError('Method "%s" not supported' % (method))
if argument in args: if argument in args:
info = args[argument] info = args[argument]
invalid_version = True invalid_version = True
@ -156,18 +142,19 @@ def argument_value_convert(method, argument, value, rpc_version):
if invalid_version: if invalid_version:
if replacement: if replacement:
LOGGER.warning( LOGGER.warning(
'Replacing requested argument "{0}" with "{1}".'.format(argument, replacement)) 'Replacing requested argument "%s" with "%s".'
% (argument, replacement))
argument = replacement argument = replacement
info = args[argument] info = args[argument]
else: else:
raise ValueError( raise ValueError(
'Method "{0}" Argument "{1}" does not exist in version {2:d}.'.format(method, argument, rpc_version)) 'Method "%s" Argument "%s" does not exist in version %d.'
return argument, TR_TYPE_MAP[info[0]](value) % (method, argument, rpc_version))
return (argument, TR_TYPE_MAP[info[0]](value))
else: else:
raise ValueError('Argument "%s" does not exists for method "%s".', raise ValueError('Argument "%s" does not exists for method "%s".',
(argument, method)) (argument, method))
def get_arguments(method, rpc_version): def get_arguments(method, rpc_version):
""" """
Get arguments for method in specified Transmission RPC version. Get arguments for method in specified Transmission RPC version.
@ -177,7 +164,7 @@ def get_arguments(method, rpc_version):
elif method in ('session-get', 'session-set'): elif method in ('session-get', 'session-set'):
args = constants.SESSION_ARGS[method[-3:]] args = constants.SESSION_ARGS[method[-3:]]
else: else:
return ValueError('Method "{0}" not supported'.format(method)) return ValueError('Method "%s" not supported' % (method))
accessible = [] accessible = []
for argument, info in iteritems(args): for argument, info in iteritems(args):
valid_version = True valid_version = True
@ -189,7 +176,6 @@ def get_arguments(method, rpc_version):
accessible.append(argument) accessible.append(argument)
return accessible return accessible
def add_stdout_logger(level='debug'): def add_stdout_logger(level='debug'):
""" """
Add a stdout target for the transmissionrpc logging. Add a stdout target for the transmissionrpc logging.
@ -204,7 +190,6 @@ def add_stdout_logger(level='debug'):
loghandler.setLevel(loglevel) loghandler.setLevel(loglevel)
trpc_logger.addHandler(loghandler) trpc_logger.addHandler(loghandler)
def add_file_logger(filepath, level='debug'): def add_file_logger(filepath, level='debug'):
""" """
Add a stdout target for the transmissionrpc logging. Add a stdout target for the transmissionrpc logging.
@ -219,5 +204,4 @@ def add_file_logger(filepath, level='debug'):
loghandler.setLevel(loglevel) loghandler.setLevel(loglevel)
trpc_logger.addHandler(loghandler) trpc_logger.addHandler(loghandler)
Field = namedtuple('Field', ['value', 'dirty']) Field = namedtuple('Field', ['value', 'dirty'])