Replaced transmissionrpc with stock 0.8 release

This commit is contained in:
Correl Roush 2012-01-02 10:46:22 -05:00
parent e357a254d6
commit 4b8a270714
8 changed files with 305 additions and 272 deletions

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
from transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT, STATUS, PRIORITY, RATIO_LIMIT, LOGGER from transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT, PRIORITY, RATIO_LIMIT, LOGGER
from transmissionrpc.error import TransmissionError, HTTPHandlerError from transmissionrpc.error import TransmissionError, HTTPHandlerError
from transmissionrpc.httphandler import HTTPHandler, DefaultHTTPHandler from transmissionrpc.httphandler import HTTPHandler, DefaultHTTPHandler
from transmissionrpc.torrent import Torrent from transmissionrpc.torrent import Torrent
@ -11,6 +11,6 @@ from transmissionrpc.client import Client
from transmissionrpc.utils import add_stdout_logger from transmissionrpc.utils import add_stdout_logger
__author__ = u'Erik Svensson <erik.public@gmail.com>' __author__ = u'Erik Svensson <erik.public@gmail.com>'
__version__ = u'0.7' __version__ = u'0.8'
__copyright__ = u'Copyright (c) 2008-2010 Erik Svensson' __copyright__ = u'Copyright (c) 2008-2011 Erik Svensson'
__license__ = u'MIT' __license__ = u'MIT'

View file

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import os, re, time import re, time
import warnings import urllib2, urlparse, base64
import httplib, urllib2, urlparse, base64
try: try:
import json import json
@ -41,56 +40,11 @@ def debug_httperror(error):
) )
) )
def _urlparse(address):
"""
Mimic python 2.5+ urlparse.urlparse
"""
class ParsedResult(tuple):
def __init__(self, address = None):
self.scheme = ''
self.netloc = ''
self.path = ''
self.params = ''
self.query = ''
self.fragment = ''
self.username = None
self.password = None
self.hostname = None
self.port = None
if address:
self.parse(address)
def parse(self, address):
(
self.scheme,
self.netloc,
self.path,
self.params,
self.query,
self.fragment
) = urlparse.urlparse(address)
self.hostname = self.netloc
if '@' in self.netloc:
(login, self.hostname) = self.netloc.split('@')
if ':' in login:
(self.username, self.password) = login.split(':')
else:
self.username = login
if ':' in self.hostname:
(self.hostname, self.port) = self.hostname.split(':')
try:
self.port = int(self.port)
except:
self.port = None
result = ParsedResult(address)
return result
""" """
Torrent ids Torrent ids
Many functions in Client takes torrent id. A torrent id can either be id or Many functions in Client takes torrent id. A torrent id can either be id or
hashString. When suppling multiple id's it is possible to use a list mixed hashString. When supplying multiple id's it is possible to use a list mixed
with both id and hashString. with both id and hashString.
Timeouts Timeouts
@ -110,7 +64,7 @@ class Client(object):
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.urlparse(address)
if urlo.scheme == '': if urlo.scheme == '':
base_url = 'http://' + address + ':' + str(port) base_url = 'http://' + address + ':' + str(port)
self.url = base_url + '/transmission/rpc' self.url = base_url + '/transmission/rpc'
@ -125,7 +79,7 @@ class Client(object):
password = urlo.password password = urlo.password
elif urlo.username or urlo.password: elif urlo.username or urlo.password:
LOGGER.warning('Either user or password missing, not using authentication.') LOGGER.warning('Either user or password missing, not using authentication.')
if http_handler == None: if http_handler is None:
self.http_handler = DefaultHTTPHandler() self.http_handler = DefaultHTTPHandler()
else: else:
if hasattr(http_handler, 'set_authentication') and hasattr(http_handler, 'request'): if hasattr(http_handler, 'set_authentication') and hasattr(http_handler, 'request'):
@ -170,8 +124,9 @@ class Client(object):
Query Transmission through HTTP. Query Transmission through HTTP.
""" """
headers = {'x-transmission-session-id': str(self.session_id)} headers = {'x-transmission-session-id': str(self.session_id)}
result = {}
request_count = 0 request_count = 0
if timeout == None: if timeout is None:
timeout = self._query_timeout timeout = self._query_timeout
while True: while True:
LOGGER.debug(json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2)) LOGGER.debug(json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2))
@ -192,7 +147,7 @@ class Client(object):
else: else:
debug_httperror(error) debug_httperror(error)
raise TransmissionError('Request failed.', error) raise TransmissionError('Request failed.', error)
request_count = request_count + 1 request_count += 1
return result return result
def _request(self, method, arguments=None, ids=None, require_ids=False, timeout=None): def _request(self, method, arguments=None, ids=None, require_ids=False, timeout=None):
@ -201,7 +156,7 @@ class Client(object):
""" """
if not isinstance(method, (str, unicode)): if not isinstance(method, (str, unicode)):
raise ValueError('request takes method as string') raise ValueError('request takes method as string')
if arguments == None: if arguments is None:
arguments = {} arguments = {}
if not isinstance(arguments, dict): if not isinstance(arguments, dict):
raise ValueError('request takes arguments as dict') raise ValueError('request takes arguments as dict')
@ -264,7 +219,7 @@ class Client(object):
""" """
ids = [] ids = []
if args == None: if args is None:
pass pass
elif isinstance(args, (int, long)): elif isinstance(args, (int, long)):
ids.append(args) ids.append(args)
@ -298,7 +253,7 @@ class Client(object):
if not addition: if not addition:
raise ValueError(u'Invalid torrent id, \"%s\"' % item) raise ValueError(u'Invalid torrent id, \"%s\"' % item)
ids.extend(addition) ids.extend(addition)
elif isinstance(args, (list)): elif isinstance(args, list):
for item in args: for item in args:
ids.extend(self._format_ids(item)) ids.extend(self._format_ids(item))
else: else:
@ -312,14 +267,14 @@ class Client(object):
self.session.update(data) self.session.update(data)
def _update_server_version(self): def _update_server_version(self):
if self.server_version == None: if self.server_version is None:
version_major = 1 version_major = 1
version_minor = 30 version_minor = 30
version_changeset = 0 version_changeset = 0
version_parser = re.compile('(\d).(\d+) \((\d+)\)') version_parser = re.compile('(\d).(\d+) \((\d+)\)')
if hasattr(self.session, 'version'): if hasattr(self.session, 'version'):
match = version_parser.match(self.session.version) match = version_parser.match(self.session.version)
if (match): if match:
version_major = int(match.group(1)) version_major = int(match.group(1))
version_minor = int(match.group(2)) version_minor = int(match.group(2))
version_changeset = match.group(3) version_changeset = match.group(3)
@ -330,9 +285,12 @@ class Client(object):
""" """
Get the Transmission RPC version. Trying to deduct if the server don't have a version value. Get the Transmission RPC version. Trying to deduct if the server don't have a version value.
""" """
if self.protocol_version == None: if self.protocol_version is None:
# Ugly fix for 2.20 - 2.22 reporting rpc-version 11, but having new arguments
if self.server_version and (self.server_version[0] == 2 and self.server_version[1] in [20, 21, 22]):
self.protocol_version = 12
# Ugly fix for 2.12 reporting rpc-version 10, but having new arguments # Ugly fix for 2.12 reporting rpc-version 10, but having new arguments
if (self.server_version and (self.server_version[0] == 2 and self.server_version[1] == 12)): elif self.server_version and (self.server_version[0] == 2 and self.server_version[1] == 12):
self.protocol_version = 11 self.protocol_version = 11
elif hasattr(self.session, 'rpc_version'): elif hasattr(self.session, 'rpc_version'):
self.protocol_version = self.session.rpc_version self.protocol_version = self.session.rpc_version
@ -354,21 +312,21 @@ class Client(object):
Add torrent to transfers list. Takes a base64 encoded .torrent file in data. Add torrent to transfers list. Takes a base64 encoded .torrent file in data.
Additional arguments are: Additional arguments are:
===================== ==== ============================================================= ===================== ===== =========== =============================================================
Argument RPC Description Argument RPC Replaced by Description
===================== ==== ============================================================= ===================== ===== =========== =============================================================
``bandwidthPriority`` 8 - Priority for this transfer. ``bandwidthPriority`` 8 - Priority for this transfer.
``download_dir`` 1 - The directory where the downloaded contents will be saved in. ``cookies`` 13 - One or more HTTP cookie(s).
``filename`` 1 - A filepath or URL to a torrent file or a magnet link. ``download_dir`` 1 - The directory where the downloaded contents will be saved in.
``files_unwanted`` 1 - A list of file id's that shouldn't be downloaded. ``files_unwanted`` 1 - A list of file id's that shouldn't be downloaded.
``files_wanted`` 1 - A list of file id's that should be downloaded. ``files_wanted`` 1 - A list of file id's that should be downloaded.
``metainfo`` 1 - The content of a torrent file, base64 encoded. ``paused`` 1 - If True, does not start the transfer when added.
``paused`` 1 - If True, does not start the transfer when added. ``peer_limit`` 1 - Maximum number of peers allowed.
``peer_limit`` 1 - Maximum number of peers allowed. ``priority_high`` 1 - A list of file id's that should have high priority.
``priority_high`` 1 - A list of file id's that should have high priority. ``priority_low`` 1 - A list of file id's that should have low priority.
``priority_low`` 1 - A list of file id's that should have low priority. ``priority_normal`` 1 - A list of file id's that should have normal priority.
``priority_normal`` 1 - A list of file id's that should have normal priority. ===================== ===== =========== =============================================================
===================== ==== =============================================================
""" """
args = {} args = {}
if data: if data:
@ -388,27 +346,28 @@ class Client(object):
all uri's supported by Transmissions torrent-add 'filename' all uri's supported by Transmissions torrent-add 'filename'
argument. Additional arguments are: argument. Additional arguments are:
===================== ==== ============================================================= ===================== ===== =========== =============================================================
Argument RPC Description Argument RPC Replaced by Description
===================== ==== ============================================================= ===================== ===== =========== =============================================================
``bandwidthPriority`` 8 - Priority for this transfer. ``bandwidthPriority`` 8 - Priority for this transfer.
``download_dir`` 1 - The directory where the downloaded contents will be saved in. ``cookies`` 13 - One or more HTTP cookie(s).
``files_unwanted`` 1 - A list of file id's that shouldn't be downloaded. ``download_dir`` 1 - The directory where the downloaded contents will be saved in.
``files_wanted`` 1 - A list of file id's that should be downloaded. ``files_unwanted`` 1 - A list of file id's that shouldn't be downloaded.
``paused`` 1 - If True, does not start the transfer when added. ``files_wanted`` 1 - A list of file id's that should be downloaded.
``peer_limit`` 1 - Maximum number of peers allowed. ``paused`` 1 - If True, does not start the transfer when added.
``priority_high`` 1 - A list of file id's that should have high priority. ``peer_limit`` 1 - Maximum number of peers allowed.
``priority_low`` 1 - A list of file id's that should have low priority. ``priority_high`` 1 - A list of file id's that should have high priority.
``priority_normal`` 1 - A list of file id's that should have normal priority. ``priority_low`` 1 - A list of file id's that should have low priority.
===================== ==== ============================================================= ``priority_normal`` 1 - A list of file id's that should have normal priority.
===================== ===== =========== =============================================================
""" """
if uri == None: if uri is None:
raise ValueError('add_uri requires a URI.') raise ValueError('add_uri requires a URI.')
# there has been some problem with T's built in torrent fetcher, # there has been some problem with T's built in torrent fetcher,
# use a python one instead # use a python one instead
parseduri = _urlparse(uri) parsed_uri = urlparse.urlparse(uri)
torrent_data = None torrent_data = None
if parseduri.scheme in ['file', 'ftp', 'ftps', 'http', 'https']: if parsed_uri.scheme in ['file', 'ftp', 'ftps', 'http', 'https']:
torrent_file = urllib2.urlopen(uri) torrent_file = urllib2.urlopen(uri)
torrent_data = base64.b64encode(torrent_file.read()) torrent_data = base64.b64encode(torrent_file.read())
if torrent_data: if torrent_data:
@ -425,9 +384,12 @@ class Client(object):
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 start(self, ids, timeout=None): def start(self, ids, bypass_queue=False, timeout=None):
"""start torrent(s) with provided id(s)""" """start torrent(s) with provided id(s)"""
self._request('torrent-start', {}, ids, True, timeout=timeout) method = 'torrent-start'
if bypass_queue and self.rpc_version >= 14:
method = 'torrent-start-now'
self._request(method, {}, ids, True, timeout=timeout)
def stop(self, ids, timeout=None): def stop(self, ids, timeout=None):
"""stop torrent(s) with provided id(s)""" """stop torrent(s) with provided id(s)"""
@ -451,7 +413,7 @@ class Client(object):
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 dictonary 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.
:: ::
@ -481,7 +443,7 @@ class Client(object):
def set_files(self, items, timeout=None): def set_files(self, items, timeout=None):
""" """
Set file properties. Takes a dictonary with similar contents as the result Set file properties. Takes a dictionary with similar contents as the result
of `get_files`. of `get_files`.
:: ::
@ -506,9 +468,9 @@ class Client(object):
continue continue
wanted = [] wanted = []
unwanted = [] unwanted = []
priority_high = [] high = []
priority_normal = [] normal = []
priority_low = [] low = []
for fid, file_desc in files.iteritems(): for fid, file_desc in files.iteritems():
if not isinstance(file_desc, dict): if not isinstance(file_desc, dict):
continue continue
@ -518,22 +480,29 @@ class Client(object):
unwanted.append(fid) unwanted.append(fid)
if 'priority' in file_desc: if 'priority' in file_desc:
if file_desc['priority'] == 'high': if file_desc['priority'] == 'high':
priority_high.append(fid) high.append(fid)
elif file_desc['priority'] == 'normal': elif file_desc['priority'] == 'normal':
priority_normal.append(fid) normal.append(fid)
elif file_desc['priority'] == 'low': elif file_desc['priority'] == 'low':
priority_low.append(fid) low.append(fid)
self.change([tid], files_wanted = wanted args = {
, files_unwanted = unwanted 'timeout': timeout,
, priority_high = priority_high 'files_wanted': wanted,
, priority_normal = priority_normal 'files_unwanted': unwanted,
, priority_low = priority_low, timeout=timeout) }
if len(high) > 0:
args['priority_high'] = high
if len(normal) > 0:
args['priority_normal'] = normal
if len(low) > 0:
args['priority_low'] = low
self.change([tid], **args)
def list(self, timeout=None): def list(self, timeout=None):
"""list all torrents""" """list all torrents"""
fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone' fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone'
, 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver' , 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver'
, 'downloadedEver'] , 'downloadedEver', 'uploadRatio']
return self._request('torrent-get', {'fields': fields}, timeout=timeout) return self._request('torrent-get', {'fields': fields}, timeout=timeout)
def change(self, ids, timeout=None, **kwargs): def change(self, ids, timeout=None, **kwargs):
@ -550,11 +519,12 @@ class Client(object):
``files_unwanted`` 1 - A list of file id's that shouldn't be downloaded. ``files_unwanted`` 1 - A list of file id's that shouldn't be downloaded.
``files_wanted`` 1 - A list of file id's that should be downloaded. ``files_wanted`` 1 - A list of file id's that should be downloaded.
``honorsSessionLimits`` 5 - Enables or disables the transfer to honour the upload limit set in the session. ``honorsSessionLimits`` 5 - Enables or disables the transfer to honour the upload limit set in the session.
``ids`` 1 - Local download location. ``location`` 1 - Local download location.
``peer_limit`` 1 - The peer limit for the torrents. ``peer_limit`` 1 - The peer limit for the torrents.
``priority_high`` 1 - A list of file id's that should have high priority. ``priority_high`` 1 - A list of file id's that should have high priority.
``priority_low`` 1 - A list of file id's that should have normal priority. ``priority_low`` 1 - A list of file id's that should have normal priority.
``priority_normal`` 1 - A list of file id's that should have low priority. ``priority_normal`` 1 - A list of file id's that should have low priority.
``queuePosition`` 14 - Position of this transfer in its queue.
``seedIdleLimit`` 10 - Seed inactivity limit in minutes. ``seedIdleLimit`` 10 - Seed inactivity limit in minutes.
``seedIdleMode`` 10 - Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit. ``seedIdleMode`` 10 - Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.
``seedRatioLimit`` 5 - Seeding ratio. ``seedRatioLimit`` 5 - Seeding ratio.
@ -597,6 +567,26 @@ class Client(object):
args = {'location': location, 'move': False} args = {'location': location, 'move': False}
self._request('torrent-set-location', args, ids, True, timeout=timeout) self._request('torrent-set-location', args, ids, True, timeout=timeout)
def queue_top(self, ids, timeout=None):
"""Move transfer to the top of the queue."""
self._rpc_version_warning(14)
self._request('queue-move-top', ids=ids, require_ids=True, timeout=timeout)
def queue_bottom(self, ids, timeout=None):
"""Move transfer to the bottom of the queue."""
self._rpc_version_warning(14)
self._request('queue-move-bottom', ids=ids, require_ids=True, timeout=timeout)
def queue_up(self, ids, timeout=None):
"""Move transfer up in the queue."""
self._rpc_version_warning(14)
self._request('queue-move-up', ids=ids, require_ids=True, timeout=timeout)
def queue_down(self, ids, timeout=None):
"""Move transfer down in the queue."""
self._rpc_version_warning(14)
self._request('queue-move-down', ids=ids, require_ids=True, timeout=timeout)
def get_session(self, timeout=None): def get_session(self, timeout=None):
"""Get session parameters""" """Get session parameters"""
self._request('session-get', timeout=timeout) self._request('session-get', timeout=timeout)
@ -618,9 +608,12 @@ class Client(object):
``alt_speed_time_end`` 5 - Time when alternate speeds should be disabled. Minutes after midnight. ``alt_speed_time_end`` 5 - Time when alternate speeds should be disabled. Minutes after midnight.
``alt_speed_up`` 5 - Alternate session upload speed limit (in Kib/s). ``alt_speed_up`` 5 - Alternate session upload speed limit (in Kib/s).
``blocklist_enabled`` 5 - Enables the block list ``blocklist_enabled`` 5 - Enables the block list
``blocklist_url`` 11 - Location of the block list. Updated with blocklist-update.
``cache_size_mb`` 10 - The maximum size of the disk cache in MB ``cache_size_mb`` 10 - The maximum size of the disk cache in MB
``dht_enabled`` 6 - Enables DHT. ``dht_enabled`` 6 - Enables DHT.
``download_dir`` 1 - Set the session download directory. ``download_dir`` 1 - Set the session download directory.
``download_queue_enabled`` 14 - Enable parallel download restriction.
``download_queue_size`` 14 - Number of parallel downloads.
``encryption`` 1 - Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``. ``encryption`` 1 - Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.
``idle_seeding_limit`` 10 - The default seed inactivity limit in minutes. ``idle_seeding_limit`` 10 - The default seed inactivity limit in minutes.
``idle_seeding_limit_enabled`` 10 - Enables the default seed inactivity limit ``idle_seeding_limit_enabled`` 10 - Enables the default seed inactivity limit
@ -636,9 +629,13 @@ class Client(object):
``pex_enabled`` 5 - Allowing PEX in public torrents. ``pex_enabled`` 5 - Allowing PEX in public torrents.
``port`` 1 - 5 peer-port Peer port. ``port`` 1 - 5 peer-port Peer port.
``port_forwarding_enabled`` 1 - Enables port forwarding. ``port_forwarding_enabled`` 1 - Enables port forwarding.
``queue_stalled_enabled`` 14 - Enable tracking of stalled transfers.
``queue_stalled_minutes`` 14 - Number of minutes of idle that marks a transfer as stalled.
``rename_partial_files`` 8 - Appends ".part" to incomplete files ``rename_partial_files`` 8 - Appends ".part" to incomplete files
``script_torrent_done_enabled`` 9 - Whether or not to call the "done" script. ``script_torrent_done_enabled`` 9 - Whether or not to call the "done" script.
``script_torrent_done_filename`` 9 - Filename of the script to run when the transfer is done. ``script_torrent_done_filename`` 9 - Filename of the script to run when the transfer is done.
``seed_queue_enabled`` 14 - Enable parallel upload restriction.
``seed_queue_size`` 14 - Number of parallel uploads.
``seedRatioLimit`` 5 - Seed ratio limit. 1.0 means 1:1 download and upload ratio. ``seedRatioLimit`` 5 - Seed ratio limit. 1.0 means 1:1 download and upload ratio.
``seedRatioLimited`` 5 - Enables seed ration limit. ``seedRatioLimited`` 5 - Enables seed ration limit.
``speed_limit_down`` 1 - Download speed limit (in Kib/s). ``speed_limit_down`` 1 - Download speed limit (in Kib/s).
@ -647,6 +644,7 @@ class Client(object):
``speed_limit_up_enabled`` 1 - Enables upload speed limiting. ``speed_limit_up_enabled`` 1 - Enables upload speed limiting.
``start_added_torrents`` 9 - Added torrents will be started right away. ``start_added_torrents`` 9 - Added torrents will be started right away.
``trash_original_torrent_files`` 9 - The .torrent file of added torrents will be deleted. ``trash_original_torrent_files`` 9 - The .torrent file of added torrents will be deleted.
``utp_enabled`` 13 - Enables Micro Transport Protocol (UTP).
================================ ===== ================= ========================================================================================================================== ================================ ===== ================= ==========================================================================================================================
.. NOTE:: .. NOTE::

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import logging import logging
@ -18,20 +18,6 @@ DEFAULT_PORT = 9091
DEFAULT_TIMEOUT = 30.0 DEFAULT_TIMEOUT = 30.0
TR_STATUS_CHECK_WAIT = (1<<0)
TR_STATUS_CHECK = (1<<1)
TR_STATUS_DOWNLOAD = (1<<2)
TR_STATUS_SEED = (1<<3)
TR_STATUS_STOPPED = (1<<4)
STATUS = mirror_dict({
'check pending' : TR_STATUS_CHECK_WAIT,
'checking' : TR_STATUS_CHECK,
'downloading' : TR_STATUS_DOWNLOAD,
'seeding' : TR_STATUS_SEED,
'stopped' : TR_STATUS_STOPPED,
})
TR_PRI_LOW = -1 TR_PRI_LOW = -1
TR_PRI_NORMAL = 0 TR_PRI_NORMAL = 0
TR_PRI_HIGH = 1 TR_PRI_HIGH = 1
@ -103,6 +89,7 @@ TORRENT_ARGS = {
'id': ('number', 1, None, None, None, ''), 'id': ('number', 1, None, None, None, ''),
'isFinished': ('boolean', 9, None, None, None, ''), 'isFinished': ('boolean', 9, None, None, None, ''),
'isPrivate': ('boolean', 1, None, None, None, ''), 'isPrivate': ('boolean', 1, None, None, None, ''),
'isStalled': ('boolean', 14, None, None, None, ''),
'lastAnnounceTime': ('number', 1, 7, None, None, ''), 'lastAnnounceTime': ('number', 1, 7, None, None, ''),
'lastScrapeTime': ('number', 1, 7, None, None, ''), 'lastScrapeTime': ('number', 1, 7, None, None, ''),
'leechers': ('number', 1, 7, None, None, ''), 'leechers': ('number', 1, 7, None, None, ''),
@ -119,13 +106,14 @@ TORRENT_ARGS = {
'peersConnected': ('number', 1, None, None, None, ''), 'peersConnected': ('number', 1, None, None, None, ''),
'peersFrom': ('object', 1, None, None, None, ''), 'peersFrom': ('object', 1, None, None, None, ''),
'peersGettingFromUs': ('number', 1, None, None, None, ''), 'peersGettingFromUs': ('number', 1, None, None, None, ''),
'peersKnown': ('number', 1, None, None, None, ''), 'peersKnown': ('number', 1, 13, None, None, ''),
'peersSendingToUs': ('number', 1, None, None, None, ''), 'peersSendingToUs': ('number', 1, None, None, None, ''),
'percentDone': ('double', 5, None, None, None, ''), 'percentDone': ('double', 5, None, None, None, ''),
'pieces': ('string', 5, None, None, None, ''), 'pieces': ('string', 5, None, None, None, ''),
'pieceCount': ('number', 1, None, None, None, ''), 'pieceCount': ('number', 1, None, None, None, ''),
'pieceSize': ('number', 1, None, None, None, ''), 'pieceSize': ('number', 1, None, None, None, ''),
'priorities': ('array', 1, None, None, None, ''), 'priorities': ('array', 1, None, None, None, ''),
'queuePosition': ('number', 14, None, None, None, ''),
'rateDownload': ('number', 1, None, None, None, ''), 'rateDownload': ('number', 1, None, None, None, ''),
'rateUpload': ('number', 1, None, None, None, ''), 'rateUpload': ('number', 1, None, None, None, ''),
'recheckProgress': ('double', 1, None, None, None, ''), 'recheckProgress': ('double', 1, None, None, None, ''),
@ -161,11 +149,12 @@ TORRENT_ARGS = {
'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, "Enables or disables the transfer to honour the upload limit set in the session."), 'honorsSessionLimits': ('boolean', 5, None, None, None, "Enables or disables the transfer to honour the upload limit set in the session."),
'ids': ('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.'),
'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, 'Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.'),
'seedRatioLimit': ('double', 5, None, None, None, 'Seeding ratio.'), 'seedRatioLimit': ('double', 5, None, None, None, 'Seeding ratio.'),
@ -183,7 +172,8 @@ TORRENT_ARGS = {
'add': { 'add': {
'bandwidthPriority': ('number', 8, None, None, None, 'Priority for this transfer.'), 'bandwidthPriority': ('number', 8, None, None, None, 'Priority for this transfer.'),
'download-dir': ('string', 1, None, None, None, 'The directory where the downloaded contents will be saved in.'), 'download-dir': ('string', 1, None, None, None, 'The directory where the downloaded contents will be saved in.'),
'filename': ('string', 1, None, None, None, "A filepath or URL to a torrent file or a magnet link."), '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."),
'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.'),
@ -212,6 +202,9 @@ SESSION_ARGS = {
"config-dir": ('string', 8, None, None, None, ''), "config-dir": ('string', 8, None, None, None, ''),
"dht-enabled": ('boolean', 6, None, None, None, ''), "dht-enabled": ('boolean', 6, None, None, None, ''),
"download-dir": ('string', 1, None, None, None, ''), "download-dir": ('string', 1, None, None, None, ''),
"download-dir-free-space": ('number', 12, None, None, None, ''),
"download-queue-size": ('number', 14, None, None, None, ''),
"download-queue-enabled": ('boolean', 14, None, None, None, ''),
"encryption": ('string', 1, None, None, None, ''), "encryption": ('string', 1, None, None, None, ''),
"idle-seeding-limit": ('number', 10, None, None, None, ''), "idle-seeding-limit": ('number', 10, None, None, None, ''),
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, ''), "idle-seeding-limit-enabled": ('boolean', 10, None, None, None, ''),
@ -227,6 +220,8 @@ SESSION_ARGS = {
"peer-port": ('number', 5, None, None, None, ''), "peer-port": ('number', 5, None, None, None, ''),
"peer-port-random-on-start": ('boolean', 5, None, None, None, ''), "peer-port-random-on-start": ('boolean', 5, None, None, None, ''),
"port-forwarding-enabled": ('boolean', 1, None, None, None, ''), "port-forwarding-enabled": ('boolean', 1, None, None, None, ''),
"queue-stalled-minutes": ('number', 14, None, None, None, ''),
"queue-stalled-enabled": ('boolean', 14, None, None, None, ''),
"rename-partial-files": ('boolean', 8, None, None, None, ''), "rename-partial-files": ('boolean', 8, None, None, None, ''),
"rpc-version": ('number', 4, None, None, None, ''), "rpc-version": ('number', 4, None, None, None, ''),
"rpc-version-minimum": ('number', 4, None, None, None, ''), "rpc-version-minimum": ('number', 4, None, None, None, ''),
@ -234,6 +229,8 @@ SESSION_ARGS = {
"script-torrent-done-filename": ('string', 9, None, None, None, ''), "script-torrent-done-filename": ('string', 9, None, None, None, ''),
"seedRatioLimit": ('double', 5, None, None, None, ''), "seedRatioLimit": ('double', 5, None, None, None, ''),
"seedRatioLimited": ('boolean', 5, None, None, None, ''), "seedRatioLimited": ('boolean', 5, None, None, None, ''),
"seed-queue-size": ('number', 14, None, None, None, ''),
"seed-queue-enabled": ('boolean', 14, None, None, None, ''),
"speed-limit-down": ('number', 1, None, None, None, ''), "speed-limit-down": ('number', 1, None, None, None, ''),
"speed-limit-down-enabled": ('boolean', 1, None, None, None, ''), "speed-limit-down-enabled": ('boolean', 1, None, None, None, ''),
"speed-limit-up": ('number', 1, None, None, None, ''), "speed-limit-up": ('number', 1, None, None, None, ''),
@ -241,6 +238,7 @@ SESSION_ARGS = {
"start-added-torrents": ('boolean', 9, None, None, None, ''), "start-added-torrents": ('boolean', 9, None, None, None, ''),
"trash-original-torrent-files": ('boolean', 9, None, None, None, ''), "trash-original-torrent-files": ('boolean', 9, None, None, None, ''),
'units': ('object', 10, None, None, None, ''), 'units': ('object', 10, None, None, None, ''),
'utp-enabled': ('boolean', 13, None, None, None, ''),
"version": ('string', 3, None, None, None, ''), "version": ('string', 3, None, None, None, ''),
}, },
'set': { 'set': {
@ -252,10 +250,12 @@ SESSION_ARGS = {
"alt-speed-time-day": ('number', 5, None, None, None, 'Enables alternate speeds scheduling these days.'), "alt-speed-time-day": ('number', 5, None, None, None, 'Enables alternate speeds scheduling these days.'),
"alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s).'), "alt-speed-up": ('number', 5, None, None, None, 'Alternate session upload speed limit (in Kib/s).'),
"blocklist-enabled": ('boolean', 5, None, None, None, 'Enables the block list'), "blocklist-enabled": ('boolean', 5, None, None, None, 'Enables the block list'),
"blocklist-url": ('string', 11, None, None, None, 'Location of the blocklist. Updated with blocklist-update.'), "blocklist-url": ('string', 11, None, None, None, 'Location of the block list. Updated with blocklist-update.'),
"cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'), "cache-size-mb": ('number', 10, None, None, None, 'The maximum size of the disk cache in MB'),
"dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'), "dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'),
"download-dir": ('string', 1, None, None, None, 'Set the session download directory.'), "download-dir": ('string', 1, None, None, None, 'Set the session download directory.'),
"download-queue-size": ('number', 14, None, None, None, 'Number of parallel downloads.'),
"download-queue-enabled": ('boolean', 14, None, None, None, 'Enable parallel download restriction.'),
"encryption": ('string', 1, None, None, None, 'Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'), "encryption": ('string', 1, None, None, None, 'Set the session encryption mode, one of ``required``, ``preferred`` or ``tolerated``.'),
"idle-seeding-limit": ('number', 10, None, None, None, 'The default seed inactivity limit in minutes.'), "idle-seeding-limit": ('number', 10, None, None, None, 'The default seed inactivity limit in minutes.'),
"idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'Enables the default seed inactivity limit'), "idle-seeding-limit-enabled": ('boolean', 10, None, None, None, 'Enables the default seed inactivity limit'),
@ -272,8 +272,12 @@ SESSION_ARGS = {
"peer-port-random-on-start": ('boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'), "peer-port-random-on-start": ('boolean', 5, None, None, None, 'Enables randomized peer port on start of Transmission.'),
"port-forwarding-enabled": ('boolean', 1, None, None, None, 'Enables port forwarding.'), "port-forwarding-enabled": ('boolean', 1, None, None, None, 'Enables port forwarding.'),
"rename-partial-files": ('boolean', 8, None, None, None, 'Appends ".part" to incomplete files'), "rename-partial-files": ('boolean', 8, None, None, None, 'Appends ".part" to incomplete files'),
"queue-stalled-minutes": ('number', 14, None, None, None, 'Number of minutes of idle that marks a transfer as stalled.'),
"queue-stalled-enabled": ('boolean', 14, None, None, None, 'Enable tracking of stalled transfers.'),
"script-torrent-done-enabled": ('boolean', 9, None, None, None, 'Whether or not to call the "done" script.'), "script-torrent-done-enabled": ('boolean', 9, None, None, None, 'Whether or not to call the "done" script.'),
"script-torrent-done-filename": ('string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'), "script-torrent-done-filename": ('string', 9, None, None, None, 'Filename of the script to run when the transfer is done.'),
"seed-queue-size": ('number', 14, None, None, None, 'Number of parallel uploads.'),
"seed-queue-enabled": ('boolean', 14, None, None, None, 'Enable parallel upload restriction.'),
"seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'), "seedRatioLimit": ('double', 5, None, None, None, 'Seed ratio limit. 1.0 means 1:1 download and upload ratio.'),
"seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'), "seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'),
"speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'), "speed-limit-down": ('number', 1, None, None, None, 'Download speed limit (in Kib/s).'),
@ -282,5 +286,6 @@ SESSION_ARGS = {
"speed-limit-up-enabled": ('boolean', 1, None, None, None, 'Enables upload speed limiting.'), "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.'), "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.'), "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

@ -1,15 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
class TransmissionError(Exception): class TransmissionError(Exception):
""" """
This exception is raised when there has occured 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
self.original = original self.original = original
def __str__(self): def __str__(self):
@ -21,14 +21,14 @@ class TransmissionError(Exception):
class HTTPHandlerError(Exception): class HTTPHandlerError(Exception):
""" """
This exception is raised when there has occured 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 = ''
self.code = 600 self.code = 600
self.message = '' self._message = ''
self.headers = {} self.headers = {}
self.data = '' self.data = ''
if isinstance(httpurl, (str, unicode)): if isinstance(httpurl, (str, unicode)):
@ -36,17 +36,17 @@ class HTTPHandlerError(Exception):
if isinstance(httpcode, (int, long)): if isinstance(httpcode, (int, long)):
self.code = httpcode self.code = httpcode
if isinstance(httpmsg, (str, unicode)): if isinstance(httpmsg, (str, unicode)):
self.message = httpmsg self._message = httpmsg
if isinstance(httpheaders, (dict)): if isinstance(httpheaders, dict):
self.headers = httpheaders self.headers = httpheaders
if isinstance(httpdata, (str, unicode)): if isinstance(httpdata, (str, unicode)):
self.data = httpdata self.data = httpdata
def __repr__(self): def __repr__(self):
return '<HTTPHandlerError %d, %s>' % (self.code, self.message) return '<HTTPHandlerError %d, %s>' % (self.code, self._message)
def __str__(self): def __str__(self):
return '<HTTPHandlerError %d, %s>' % (self.code, self.message) return 'HTTPHandlerError %d: %s' % (self.code, self._message)
def __unicode__(self): def __unicode__(self):
return u'<HTTPHandlerError %d, %s>' % (self.code, self.message) return u'HTTPHandlerError %d: %s' % (self.code, self._message)

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import sys, httplib, urllib2 import sys, httplib, urllib2
@ -56,12 +56,12 @@ class DefaultHTTPHandler(HTTPHandler):
else: else:
response = urllib2.urlopen(request) response = urllib2.urlopen(request)
except urllib2.HTTPError, error: except urllib2.HTTPError, error:
if error.fp == None: if error.fp is None:
raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs)) raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs))
else: else:
raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs), error.read()) raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs), error.read())
except urllib2.URLError, error: except urllib2.URLError, error:
# urllib2.URLError documentation is absymal! # urllib2.URLError documentation is horrendous!
# Try to get the tuple arguments of URLError # Try to get the tuple arguments of URLError
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])

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
class Session(object): class Session(object):
@ -8,17 +8,17 @@ class Session(object):
Access the session field can be done through attributes. Access the session field can be done through attributes.
The attributes available are the same as the session arguments in the The attributes available are the same as the session arguments in the
Transmission RPC specification, but with underscore instead of hypen. Transmission RPC specification, but with underscore instead of hyphen.
``download-dir`` -> ``download_dir``. ``download-dir`` -> ``download_dir``.
""" """
def __init__(self, fields=None): def __init__(self, fields=None):
self.fields = {} self.fields = {}
if fields != None: if fields is not None:
self.update(fields) self.update(fields)
def update(self, other): def update(self, other):
"""Update the session data from a session arguments dictinary""" """Update the session data from a session arguments dictionary"""
fields = None fields = None
if isinstance(other, dict): if isinstance(other, dict):

View file

@ -1,16 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import sys, datetime import sys, datetime
from transmissionrpc.constants import STATUS, PRIORITY from transmissionrpc.constants import PRIORITY
from transmissionrpc.utils import format_timedelta from transmissionrpc.utils import format_timedelta
class Torrent(object): class Torrent(object):
""" """
Torrent is a class holding the data raceived from Transmission regarding a bittorrent transfer. Torrent is a class holding the data received from Transmission regarding a bittorrent transfer.
All fetched torrent fields are accessable through this class using attributes. All fetched torrent fields are accessible through this class using attributes.
This class has a few convenience properties using the torrent data. This class has a few convenience properties using the torrent data.
""" """
@ -22,7 +22,7 @@ class Torrent(object):
self.client = client self.client = client
def _getNameString(self, codec=None): def _getNameString(self, codec=None):
if codec == None: if codec is None:
codec = sys.getdefaultencoding() codec = sys.getdefaultencoding()
name = None name = None
# try to find name # try to find name
@ -54,9 +54,43 @@ class Torrent(object):
def __copy__(self): def __copy__(self):
return Torrent(self.client, self.fields) return Torrent(self.client, self.fields)
def _rpc_version(self):
if self.client:
return self.client.rpc_version
return 2
def _status_old(self, code):
mapping = {
(1<<0): 'check pending',
(1<<1): 'checking',
(1<<2): 'downloading',
(1<<3): 'seeding',
(1<<4): 'stopped',
}
return mapping[code]
def _status_new(self, code):
mapping = {
0: 'stopped',
1: 'check pending',
2: 'checking',
3: 'download pending',
4: 'downloading',
5: 'seed pending',
6: 'seeding',
}
return mapping[code]
def _status(self):
code = self.fields['status']
if self._rpc_version() >= 14:
return self._status_new(code)
else:
return self._status_old(code)
def update(self, other): def update(self, other):
""" """
Update the torrent data from a Transmission JSON-RPC arguments dictinary Update the torrent data from a Transmission JSON-RPC arguments dictionary
""" """
fields = None fields = None
if isinstance(other, dict): if isinstance(other, dict):
@ -70,32 +104,31 @@ class Torrent(object):
def files(self): def files(self):
""" """
Get list of files for this torrent. Get list of files for this torrent.
This function returns a dictionary with file information for each file. This function returns a dictionary with file information for each file.
The file information is has following fields: The file information is has following fields:
:: ::
{ {
<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> 'selected': <selected for download>
} }
...
... }
}
""" """
result = {} result = {}
if 'files' in self.fields: if 'files' in self.fields:
indicies = xrange(len(self.fields['files'])) indices = xrange(len(self.fields['files']))
files = self.fields['files'] files = self.fields['files']
priorities = self.fields['priorities'] priorities = self.fields['priorities']
wanted = self.fields['wanted'] wanted = self.fields['wanted']
for item in zip(indicies, files, priorities, wanted): for item in zip(indices, files, priorities, wanted):
selected = bool(item[3]) selected = True if item[3] else False
priority = PRIORITY[item[2]] priority = PRIORITY[item[2]]
result[item[0]] = { result[item[0]] = {
'selected': selected, 'selected': selected,
@ -115,10 +148,10 @@ class Torrent(object):
def status(self): def status(self):
""" """
Returns the torrent status. Is either one of 'check pending', 'checking', Returns the torrent status. Is either one of 'check pending', 'checking',
'downloading', 'seeding' or 'stopped'. The first two is related to 'downloading', 'seeding' or 'stopped'. The first two is related to
verification. verification.
""" """
return STATUS[self.fields['status']] return self._status()
@property @property
def progress(self): def progress(self):
@ -131,10 +164,7 @@ class Torrent(object):
@property @property
def ratio(self): def ratio(self):
"""Get the upload/download ratio.""" """Get the upload/download ratio."""
try: return float(self.fields['uploadRatio'])
return self.fields['uploadedEver'] / float(self.fields['downloadedEver'])
except ZeroDivisionError:
return 0.0
@property @property
def eta(self): def eta(self):
@ -167,12 +197,12 @@ class Torrent(object):
def format_eta(self): def format_eta(self):
""" """
Returns the attribute *eta* formatted as a string. Returns the attribute *eta* formatted as a string.
* If eta is -1 the result is 'not available' * If eta is -1 the result is 'not available'
* If eta is -2 the result is 'unknown' * If eta is -2 the result is 'unknown'
* Otherwise eta is formatted as <days> <hours>:<minutes>:<seconds>. * Otherwise eta is formatted as <days> <hours>:<minutes>:<seconds>.
""" """
eta = self.fields['eta'] eta = self.fields['eta']
if eta == -1: if eta == -1:
return 'not available' return 'not available'

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2010 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import socket, datetime, logging import socket, datetime, logging
@ -28,18 +28,21 @@ def format_speed(size):
def format_timedelta(delta): def format_timedelta(delta):
""" """
Format datetime.timedelta into <days> <hours>:<mminutes>:<seconds>. Format datetime.timedelta into <days> <hours>:<minutes>:<seconds>.
""" """
minutes, seconds = divmod(delta.seconds, 60) minutes, seconds = divmod(delta.seconds, 60)
hours, minutes = divmod(minutes, 60) hours, minutes = divmod(minutes, 60)
return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds) return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds)
def format_timestamp(timestamp): def format_timestamp(timestamp, utc=False):
""" """
Format unix timestamp into ISO date format. Format unix timestamp into ISO date format.
""" """
if timestamp > 0: if timestamp > 0:
dt_timestamp = datetime.datetime.fromtimestamp(timestamp) if utc:
dt_timestamp = datetime.datetime.utcfromtimestamp(timestamp)
else:
dt_timestamp = datetime.datetime.fromtimestamp(timestamp)
return dt_timestamp.isoformat(' ') return dt_timestamp.isoformat(' ')
else: else:
return '-' return '-'
@ -88,10 +91,7 @@ def rpc_bool(arg):
arg = bool(int(arg)) arg = bool(int(arg))
except ValueError: except ValueError:
arg = arg.lower() in [u'true', u'yes'] arg = arg.lower() in [u'true', u'yes']
if bool(arg): return 1 if bool(arg) else 0
return 1
else:
return 0
TR_TYPE_MAP = { TR_TYPE_MAP = {
'number' : int, 'number' : int,