Merge branch 'master' into eden-pre

This commit is contained in:
Correl Roush 2012-01-03 20:48:38 -05:00
commit bc5abe6d06
8 changed files with 350 additions and 262 deletions

View file

@ -1,8 +1,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.
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.httphandler import HTTPHandler, DefaultHTTPHandler
from transmissionrpc.torrent import Torrent
@ -11,6 +11,6 @@ from transmissionrpc.client import Client
from transmissionrpc.utils import add_stdout_logger
__author__ = u'Erik Svensson <erik.public@gmail.com>'
__version__ = u'0.7'
__copyright__ = u'Copyright (c) 2008-2010 Erik Svensson'
__version__ = u'0.8'
__copyright__ = u'Copyright (c) 2008-2011 Erik Svensson'
__license__ = u'MIT'

View file

@ -1,10 +1,10 @@
# -*- 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.
import os, re, time
import warnings
import httplib, urllib2, urlparse, base64
import sys
import re, time
import urllib2, urlparse, base64
try:
import json
@ -20,7 +20,7 @@ from transmissionrpc.session import Session
def debug_httperror(error):
"""
Log the Transmission RPC HTTP error.
Log the Transmission RPC HTTP error.
"""
try:
data = json.loads(error.data)
@ -41,56 +41,60 @@ def debug_httperror(error):
)
)
def _urlparse(address):
if sys.version_info[:2] < (2, 5):
"""
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
_old_urlparse = urlparse.urlparse
def _urlparse(address):
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
if address:
self.parse(address)
def parse(self, address):
(
self.scheme,
self.netloc,
self.path,
self.params,
self.query,
self.fragment
) = _old_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
result = ParsedResult(address)
return result
urlparse.urlparse = _urlparse
"""
Torrent ids
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.
Timeouts
@ -110,7 +114,7 @@ class Client(object):
self._query_timeout = float(timeout)
else:
self._query_timeout = DEFAULT_TIMEOUT
urlo = _urlparse(address)
urlo = urlparse.urlparse(address)
if urlo.scheme == '':
base_url = 'http://' + address + ':' + str(port)
self.url = base_url + '/transmission/rpc'
@ -125,7 +129,7 @@ class Client(object):
password = urlo.password
elif urlo.username or urlo.password:
LOGGER.warning('Either user or password missing, not using authentication.')
if http_handler == None:
if http_handler is None:
self.http_handler = DefaultHTTPHandler()
else:
if hasattr(http_handler, 'set_authentication') and hasattr(http_handler, 'request'):
@ -150,19 +154,19 @@ class Client(object):
Get current timeout for HTTP queries.
"""
return self._query_timeout
def _set_timeout(self, value):
"""
Set timeout for HTTP queries.
"""
self._query_timeout = float(value)
def _del_timeout(self):
"""
Reset the HTTP query timeout to the default.
"""
self._query_timeout = DEFAULT_TIMEOUT
timeout = property(_get_timeout, _set_timeout, _del_timeout, doc="HTTP query timeout.")
def _http_query(self, query, timeout=None):
@ -170,8 +174,9 @@ class Client(object):
Query Transmission through HTTP.
"""
headers = {'x-transmission-session-id': str(self.session_id)}
result = {}
request_count = 0
if timeout == None:
if timeout is None:
timeout = self._query_timeout
while True:
LOGGER.debug(json.dumps({'url': self.url, 'headers': headers, 'query': query, 'timeout': timeout}, indent=2))
@ -192,7 +197,7 @@ class Client(object):
else:
debug_httperror(error)
raise TransmissionError('Request failed.', error)
request_count = request_count + 1
request_count += 1
return result
def _request(self, method, arguments=None, ids=None, require_ids=False, timeout=None):
@ -201,7 +206,7 @@ class Client(object):
"""
if not isinstance(method, (str, unicode)):
raise ValueError('request takes method as string')
if arguments == None:
if arguments is None:
arguments = {}
if not isinstance(arguments, dict):
raise ValueError('request takes arguments as dict')
@ -263,8 +268,8 @@ class Client(object):
Take things and make them valid torrent identifiers
"""
ids = []
if args == None:
if args is None:
pass
elif isinstance(args, (int, long)):
ids.append(args)
@ -298,7 +303,7 @@ class Client(object):
if not addition:
raise ValueError(u'Invalid torrent id, \"%s\"' % item)
ids.extend(addition)
elif isinstance(args, (list)):
elif isinstance(args, list):
for item in args:
ids.extend(self._format_ids(item))
else:
@ -312,14 +317,14 @@ class Client(object):
self.session.update(data)
def _update_server_version(self):
if self.server_version == None:
if self.server_version is None:
version_major = 1
version_minor = 30
version_changeset = 0
version_parser = re.compile('(\d).(\d+) \((\d+)\)')
if hasattr(self.session, 'version'):
match = version_parser.match(self.session.version)
if (match):
if match:
version_major = int(match.group(1))
version_minor = int(match.group(2))
version_changeset = match.group(3)
@ -330,9 +335,12 @@ class Client(object):
"""
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
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
elif hasattr(self.session, 'rpc_version'):
self.protocol_version = self.session.rpc_version
@ -354,21 +362,21 @@ class Client(object):
Add torrent to transfers list. Takes a base64 encoded .torrent file in data.
Additional arguments are:
===================== ==== =============================================================
Argument RPC Description
===================== ==== =============================================================
``bandwidthPriority`` 8 - Priority for this transfer.
``download_dir`` 1 - The directory where the downloaded contents will be saved in.
``filename`` 1 - A filepath or URL to a torrent file or a magnet link.
``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.
``metainfo`` 1 - The content of a torrent file, base64 encoded.
``paused`` 1 - If True, does not start the transfer when added.
``peer_limit`` 1 - Maximum number of peers allowed.
``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_normal`` 1 - A list of file id's that should have normal priority.
===================== ==== =============================================================
===================== ===== =========== =============================================================
Argument RPC Replaced by Description
===================== ===== =========== =============================================================
``bandwidthPriority`` 8 - Priority for this transfer.
``cookies`` 13 - One or more HTTP cookie(s).
``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_wanted`` 1 - A list of file id's that should be downloaded.
``paused`` 1 - If True, does not start the transfer when added.
``peer_limit`` 1 - Maximum number of peers allowed.
``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_normal`` 1 - A list of file id's that should have normal priority.
===================== ===== =========== =============================================================
"""
args = {}
if data:
@ -381,41 +389,42 @@ class Client(object):
argument, value, self.rpc_version)
args[arg] = val
return self._request('torrent-add', args, timeout=timeout)
def add_uri(self, uri, **kwargs):
"""
Add torrent to transfers list. Takes a uri to a torrent, supporting
all uri's supported by Transmissions torrent-add 'filename'
argument. Additional arguments are:
===================== ==== =============================================================
Argument RPC Description
===================== ==== =============================================================
``bandwidthPriority`` 8 - Priority for this transfer.
``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_wanted`` 1 - A list of file id's that should be downloaded.
``paused`` 1 - If True, does not start the transfer when added.
``peer_limit`` 1 - Maximum number of peers allowed.
``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_normal`` 1 - A list of file id's that should have normal priority.
===================== ==== =============================================================
===================== ===== =========== =============================================================
Argument RPC Replaced by Description
===================== ===== =========== =============================================================
``bandwidthPriority`` 8 - Priority for this transfer.
``cookies`` 13 - One or more HTTP cookie(s).
``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_wanted`` 1 - A list of file id's that should be downloaded.
``paused`` 1 - If True, does not start the transfer when added.
``peer_limit`` 1 - Maximum number of peers allowed.
``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_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.')
# there has been some problem with T's built in torrent fetcher,
# use a python one instead
parseduri = _urlparse(uri)
parsed_uri = urlparse.urlparse(uri)
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_data = base64.b64encode(torrent_file.read())
if torrent_data:
return self.add(torrent_data, **kwargs)
else:
return self.add(None, filename=uri, **kwargs)
def remove(self, ids, delete_data=False, timeout=None):
"""
remove torrent(s) with provided id(s). Local data is removed if
@ -425,9 +434,12 @@ class Client(object):
self._request('torrent-remove',
{'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)"""
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):
"""stop torrent(s) with provided id(s)"""
@ -451,7 +463,7 @@ class Client(object):
def get_files(self, ids=None, timeout=None):
"""
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.
::
@ -481,7 +493,7 @@ class Client(object):
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`.
::
@ -506,9 +518,9 @@ class Client(object):
continue
wanted = []
unwanted = []
priority_high = []
priority_normal = []
priority_low = []
high = []
normal = []
low = []
for fid, file_desc in files.iteritems():
if not isinstance(file_desc, dict):
continue
@ -518,22 +530,29 @@ class Client(object):
unwanted.append(fid)
if 'priority' in file_desc:
if file_desc['priority'] == 'high':
priority_high.append(fid)
high.append(fid)
elif file_desc['priority'] == 'normal':
priority_normal.append(fid)
normal.append(fid)
elif file_desc['priority'] == 'low':
priority_low.append(fid)
self.change([tid], files_wanted = wanted
, files_unwanted = unwanted
, priority_high = priority_high
, priority_normal = priority_normal
, priority_low = priority_low, timeout=timeout)
low.append(fid)
args = {
'timeout': timeout,
'files_wanted': wanted,
'files_unwanted': unwanted,
}
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):
"""list all torrents"""
fields = ['id', 'hashString', 'name', 'sizeWhenDone', 'leftUntilDone'
, 'eta', 'status', 'rateUpload', 'rateDownload', 'uploadedEver'
, 'downloadedEver']
, 'downloadedEver', 'uploadRatio']
return self._request('torrent-get', {'fields': fields}, timeout=timeout)
def change(self, ids, timeout=None, **kwargs):
@ -542,32 +561,33 @@ class Client(object):
parameters are:
============================ ===== =============== =======================================================================================
Argument RPC Replaced by Description
Argument RPC Replaced by Description
============================ ===== =============== =======================================================================================
``bandwidthPriority`` 5 - Priority for this transfer.
``downloadLimit`` 5 - Set the speed limit for download in Kib/s.
``downloadLimited`` 5 - Enable download speed limiter.
``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.
``honorsSessionLimits`` 5 - Enables or disables the transfer to honour the upload limit set in the session.
``ids`` 1 - Local download location.
``peer_limit`` 1 - The peer limit for the torrents.
``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_normal`` 1 - A list of file id's that should have low priority.
``seedIdleLimit`` 10 - Seed inactivity limit in minutes.
``bandwidthPriority`` 5 - Priority for this transfer.
``downloadLimit`` 5 - Set the speed limit for download in Kib/s.
``downloadLimited`` 5 - Enable download speed limiter.
``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.
``honorsSessionLimits`` 5 - Enables or disables the transfer to honour the upload limit set in the session.
``location`` 1 - Local download location.
``peer_limit`` 1 - The peer limit for the torrents.
``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_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.
``seedIdleMode`` 10 - Seed inactivity mode. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.
``seedRatioLimit`` 5 - Seeding ratio.
``seedRatioMode`` 5 - Which ratio to use. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.
``speed_limit_down`` 1 - 5 downloadLimit Set the speed limit for download in Kib/s.
``speed_limit_down_enabled`` 1 - 5 downloadLimited Enable download speed limiter.
``speed_limit_up`` 1 - 5 uploadLimit Set the speed limit for upload in Kib/s.
``speed_limit_up_enabled`` 1 - 5 uploadLimited Enable upload speed limiter.
``trackerAdd`` 10 - Array of string with announce URLs to add.
``trackerRemove`` 10 - Array of ids of trackers to remove.
``trackerReplace`` 10 - Array of (id, url) tuples where the announce URL should be replaced.
``uploadLimit`` 5 - Set the speed limit for upload in Kib/s.
``uploadLimited`` 5 - Enable upload speed limiter.
``seedRatioLimit`` 5 - Seeding ratio.
``seedRatioMode`` 5 - Which ratio to use. 0 = Use session limit, 1 = Use transfer limit, 2 = Disable limit.
``speed_limit_down`` 1 - 5 downloadLimit Set the speed limit for download in Kib/s.
``speed_limit_down_enabled`` 1 - 5 downloadLimited Enable download speed limiter.
``speed_limit_up`` 1 - 5 uploadLimit Set the speed limit for upload in Kib/s.
``speed_limit_up_enabled`` 1 - 5 uploadLimited Enable upload speed limiter.
``trackerAdd`` 10 - Array of string with announce URLs to add.
``trackerRemove`` 10 - Array of ids of trackers to remove.
``trackerReplace`` 10 - Array of (id, url) tuples where the announce URL should be replaced.
``uploadLimit`` 5 - Set the speed limit for upload in Kib/s.
``uploadLimited`` 5 - Enable upload speed limiter.
============================ ===== =============== =======================================================================================
.. NOTE::
@ -584,19 +604,39 @@ class Client(object):
self._request('torrent-set', args, ids, True, timeout=timeout)
else:
ValueError("No arguments to set")
def move(self, ids, location, timeout=None):
"""Move torrent data to the new location."""
self._rpc_version_warning(6)
args = {'location': location, 'move': True}
self._request('torrent-set-location', args, ids, True, timeout=timeout)
def locate(self, ids, location, timeout=None):
"""Locate torrent data at the location."""
self._rpc_version_warning(6)
args = {'location': location, 'move': False}
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):
"""Get session parameters"""
self._request('session-get', timeout=timeout)
@ -606,47 +646,55 @@ class Client(object):
def set_session(self, timeout=None, **kwargs):
"""
Set session parameters. The parameters are:
================================ ===== ================= ==========================================================================================================================
Argument RPC Replaced by Description
Argument RPC Replaced by Description
================================ ===== ================= ==========================================================================================================================
``alt_speed_down`` 5 - Alternate session download speed limit (in Kib/s).
``alt_speed_enabled`` 5 - Enables alternate global download speed limiter.
``alt_speed_time_begin`` 5 - Time when alternate speeds should be enabled. Minutes after midnight.
``alt_speed_time_day`` 5 - Enables alternate speeds scheduling these days.
``alt_speed_time_enabled`` 5 - Enables alternate speeds scheduling.
``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).
``blocklist_enabled`` 5 - Enables the block list
``cache_size_mb`` 10 - The maximum size of the disk cache in MB
``dht_enabled`` 6 - Enables DHT.
``download_dir`` 1 - Set the session download directory.
``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_enabled`` 10 - Enables the default seed inactivity limit
``incomplete_dir`` 7 - The path to the directory of incomplete transfer data.
``alt_speed_down`` 5 - Alternate session download speed limit (in Kib/s).
``alt_speed_enabled`` 5 - Enables alternate global download speed limiter.
``alt_speed_time_begin`` 5 - Time when alternate speeds should be enabled. Minutes after midnight.
``alt_speed_time_day`` 5 - Enables alternate speeds scheduling these days.
``alt_speed_time_enabled`` 5 - Enables alternate speeds scheduling.
``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).
``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
``dht_enabled`` 6 - Enables DHT.
``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``.
``idle_seeding_limit`` 10 - The default seed inactivity limit in minutes.
``idle_seeding_limit_enabled`` 10 - Enables the default seed inactivity limit
``incomplete_dir`` 7 - The path to the directory of incomplete transfer data.
``incomplete_dir_enabled`` 7 - Enables the incomplete transfer data directory. Otherwise data for incomplete transfers are stored in the download target.
``lpd_enabled`` 9 - Enables local peer discovery for public torrents.
``peer_limit`` 1 - 5 peer-limit-global Maximum number of peers
``peer_limit_global`` 5 - Maximum number of peers
``peer_limit_per_torrent`` 5 - Maximum number of peers per transfer
``peer_port`` 5 - Peer port.
``peer_port_random_on_start`` 5 - Enables randomized peer port on start of Transmission.
``pex_allowed`` 1 - 5 pex-enabled Allowing PEX in public torrents.
``pex_enabled`` 5 - Allowing PEX in public torrents.
``port`` 1 - 5 peer-port Peer port.
``port_forwarding_enabled`` 1 - Enables port forwarding.
``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_filename`` 9 - Filename of the script to run when the transfer is done.
``seedRatioLimit`` 5 - Seed ratio limit. 1.0 means 1:1 download and upload ratio.
``seedRatioLimited`` 5 - Enables seed ration limit.
``speed_limit_down`` 1 - Download speed limit (in Kib/s).
``speed_limit_down_enabled`` 1 - Enables download speed limiting.
``speed_limit_up`` 1 - Upload speed limit (in Kib/s).
``speed_limit_up_enabled`` 1 - Enables upload speed limiting.
``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.
``lpd_enabled`` 9 - Enables local peer discovery for public torrents.
``peer_limit`` 1 - 5 peer-limit-global Maximum number of peers
``peer_limit_global`` 5 - Maximum number of peers
``peer_limit_per_torrent`` 5 - Maximum number of peers per transfer
``peer_port`` 5 - Peer port.
``peer_port_random_on_start`` 5 - Enables randomized peer port on start of Transmission.
``pex_allowed`` 1 - 5 pex-enabled Allowing PEX in public torrents.
``pex_enabled`` 5 - Allowing PEX in public torrents.
``port`` 1 - 5 peer-port Peer port.
``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
``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.
``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.
``seedRatioLimited`` 5 - Enables seed ration limit.
``speed_limit_down`` 1 - Download speed limit (in Kib/s).
``speed_limit_down_enabled`` 1 - Enables download speed limiting.
``speed_limit_up`` 1 - Upload speed limit (in Kib/s).
``speed_limit_up_enabled`` 1 - Enables upload speed limiting.
``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.
``utp_enabled`` 13 - Enables Micro Transport Protocol (UTP).
================================ ===== ================= ==========================================================================================================================
.. NOTE::

View file

@ -1,5 +1,5 @@
# -*- 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.
import logging
@ -18,20 +18,6 @@ DEFAULT_PORT = 9091
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_NORMAL = 0
TR_PRI_HIGH = 1
@ -103,6 +89,7 @@ TORRENT_ARGS = {
'id': ('number', 1, None, None, None, ''),
'isFinished': ('boolean', 9, None, None, None, ''),
'isPrivate': ('boolean', 1, None, None, None, ''),
'isStalled': ('boolean', 14, None, None, None, ''),
'lastAnnounceTime': ('number', 1, 7, None, None, ''),
'lastScrapeTime': ('number', 1, 7, None, None, ''),
'leechers': ('number', 1, 7, None, None, ''),
@ -119,13 +106,14 @@ TORRENT_ARGS = {
'peersConnected': ('number', 1, None, None, None, ''),
'peersFrom': ('object', 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, ''),
'percentDone': ('double', 5, None, None, None, ''),
'pieces': ('string', 5, None, None, None, ''),
'pieceCount': ('number', 1, None, None, None, ''),
'pieceSize': ('number', 1, None, None, None, ''),
'priorities': ('array', 1, None, None, None, ''),
'queuePosition': ('number', 14, None, None, None, ''),
'rateDownload': ('number', 1, None, None, None, ''),
'rateUpload': ('number', 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-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."),
'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.'),
'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-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.'),
'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.'),
@ -183,7 +172,8 @@ TORRENT_ARGS = {
'add': {
'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.'),
'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-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.'),
@ -212,6 +202,9 @@ SESSION_ARGS = {
"config-dir": ('string', 8, None, None, None, ''),
"dht-enabled": ('boolean', 6, 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, ''),
"idle-seeding-limit": ('number', 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-random-on-start": ('boolean', 5, 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, ''),
"rpc-version": ('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, ''),
"seedRatioLimit": ('double', 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-enabled": ('boolean', 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, ''),
"trash-original-torrent-files": ('boolean', 9, None, None, None, ''),
'units': ('object', 10, None, None, None, ''),
'utp-enabled': ('boolean', 13, None, None, None, ''),
"version": ('string', 3, None, None, None, ''),
},
'set': {
@ -252,10 +250,12 @@ SESSION_ARGS = {
"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).'),
"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'),
"dht-enabled": ('boolean', 6, None, None, None, 'Enables DHT.'),
"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``.'),
"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'),
@ -272,8 +272,12 @@ SESSION_ARGS = {
"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.'),
"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-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.'),
"seedRatioLimited": ('boolean', 5, None, None, None, 'Enables seed ration limit.'),
"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.'),
"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

@ -1,15 +1,15 @@
# -*- 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.
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.
"""
def __init__(self, message='', original=None):
Exception.__init__(self)
self.message = message
self._message = message
self.original = original
def __str__(self):
@ -21,14 +21,14 @@ class TransmissionError(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.
"""
def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None):
Exception.__init__(self)
self.url = ''
self.code = 600
self.message = ''
self._message = ''
self.headers = {}
self.data = ''
if isinstance(httpurl, (str, unicode)):
@ -36,17 +36,17 @@ class HTTPHandlerError(Exception):
if isinstance(httpcode, (int, long)):
self.code = httpcode
if isinstance(httpmsg, (str, unicode)):
self.message = httpmsg
if isinstance(httpheaders, (dict)):
self._message = httpmsg
if isinstance(httpheaders, dict):
self.headers = httpheaders
if isinstance(httpdata, (str, unicode)):
self.data = httpdata
def __repr__(self):
return '<HTTPHandlerError %d, %s>' % (self.code, self.message)
return '<HTTPHandlerError %d, %s>' % (self.code, self._message)
def __str__(self):
return '<HTTPHandlerError %d, %s>' % (self.code, self.message)
return 'HTTPHandlerError %d: %s' % (self.code, self._message)
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 -*-
# Copyright (c) 2010 Erik Svensson <erik.public@gmail.com>
# Copyright (c) 2011 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license.
import sys, httplib, urllib2
@ -14,17 +14,17 @@ class HTTPHandler(object):
"""
Transmission use basic authentication in earlier versions and digest
authentication in later versions.
* uri, the authentication realm URI.
* login, the authentication login.
* password, the authentication password.
"""
raise NotImplementedError("Bad HTTPHandler, failed to implement set_authentication.")
def request(self, url, query, headers, timeout):
"""
Implement a HTTP POST request here.
* url, The URL to request.
* query, The query data to send. This is a JSON data string.
* headers, a dictionary of headers to send.
@ -56,12 +56,12 @@ class DefaultHTTPHandler(HTTPHandler):
else:
response = urllib2.urlopen(request)
except urllib2.HTTPError, error:
if error.fp == None:
if error.fp is None:
raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs))
else:
raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs), error.read())
except urllib2.URLError, error:
# urllib2.URLError documentation is absymal!
# urllib2.URLError documentation is horrendous!
# 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:
raise HTTPHandlerError(httpcode=error.reason.args[0], httpmsg=error.reason.args[1])

View file

@ -1,5 +1,5 @@
# -*- 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.
class Session(object):
@ -8,17 +8,17 @@ class Session(object):
Access the session field can be done through attributes.
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``.
"""
def __init__(self, fields=None):
self.fields = {}
if fields != None:
if fields is not None:
self.update(fields)
def update(self, other):
"""Update the session data from a session arguments dictinary"""
"""Update the session data from a session arguments dictionary"""
fields = None
if isinstance(other, dict):

View file

@ -1,16 +1,16 @@
# -*- 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.
import sys, datetime
from transmissionrpc.constants import STATUS, PRIORITY
from transmissionrpc.constants import PRIORITY
from transmissionrpc.utils import format_timedelta
class Torrent(object):
"""
Torrent is a class holding the data raceived from Transmission regarding a bittorrent transfer.
All fetched torrent fields are accessable through this class using attributes.
Torrent is a class holding the data received from Transmission regarding a bittorrent transfer.
All fetched torrent fields are accessible through this class using attributes.
This class has a few convenience properties using the torrent data.
"""
@ -22,7 +22,7 @@ class Torrent(object):
self.client = client
def _getNameString(self, codec=None):
if codec == None:
if codec is None:
codec = sys.getdefaultencoding()
name = None
# try to find name
@ -50,13 +50,47 @@ class Torrent(object):
return 'Torrent \"%s\"' % (name)
else:
return 'Torrent'
def __copy__(self):
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):
"""
Update the torrent data from a Transmission JSON-RPC arguments dictinary
Update the torrent data from a Transmission JSON-RPC arguments dictionary
"""
fields = None
if isinstance(other, dict):
@ -70,32 +104,33 @@ class Torrent(object):
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.
The file information is has following fields:
::
This function returns a dictionary with file information for each file.
The file information is has following fields:
::
{
<file id>: {
'name': <file name>,
'size': <file size in bytes>,
'completed': <bytes completed>,
'priority': <priority ('high'|'normal'|'low')>,
'selected': <selected for download>
}
...
}
{
<file id>: {
'name': <file name>,
'size': <file size in bytes>,
'completed': <bytes completed>,
'priority': <priority ('high'|'normal'|'low')>,
'selected': <selected for download>
}
...
}
"""
result = {}
if 'files' in self.fields:
indicies = xrange(len(self.fields['files']))
indices = xrange(len(self.fields['files']))
files = self.fields['files']
priorities = self.fields['priorities']
wanted = self.fields['wanted']
for item in zip(indicies, files, priorities, wanted):
selected = bool(item[3])
for item in zip(indices, files, priorities, wanted):
selected = False
if item[3]:
selected = True
priority = PRIORITY[item[2]]
result[item[0]] = {
'selected': selected,
@ -115,10 +150,10 @@ class Torrent(object):
def status(self):
"""
Returns the torrent status. Is either one of 'check pending', 'checking',
'downloading', 'seeding' or 'stopped'. The first two is related to
verification.
"""
return STATUS[self.fields['status']]
'downloading', 'seeding' or 'stopped'. The first two is related to
verification.
"""
return self._status()
@property
def progress(self):
@ -131,10 +166,7 @@ class Torrent(object):
@property
def ratio(self):
"""Get the upload/download ratio."""
try:
return self.fields['uploadedEver'] / float(self.fields['downloadedEver'])
except ZeroDivisionError:
return 0.0
return float(self.fields['uploadRatio'])
@property
def eta(self):
@ -167,12 +199,12 @@ class Torrent(object):
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 -2 the result is 'unknown'
* Otherwise eta is formatted as <days> <hours>:<minutes>:<seconds>.
"""
* If eta is -1 the result is 'not available'
* If eta is -2 the result is 'unknown'
* Otherwise eta is formatted as <days> <hours>:<minutes>:<seconds>.
"""
eta = self.fields['eta']
if eta == -1:
return 'not available'
@ -180,11 +212,11 @@ class Torrent(object):
return 'unknown'
else:
return format_timedelta(self.eta)
@property
def priority(self):
"""
Get the priority as string.
Can be one of 'low', 'normal', 'high'.
"""
return PRIORITY[self.fields['bandwidthPriority']]
return PRIORITY[self.fields['bandwidthPriority']]

View file

@ -1,5 +1,5 @@
# -*- 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.
import socket, datetime, logging
@ -28,18 +28,21 @@ def format_speed(size):
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)
hours, minutes = divmod(minutes, 60)
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.
"""
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(' ')
else:
return '-'
@ -178,7 +181,7 @@ def add_stdout_logger(level='debug'):
Add a stdout target for the transmissionrpc logging.
"""
levels = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR}
trpc_logger = logging.getLogger('transmissionrpc')
loghandler = logging.StreamHandler()
if level in levels.keys():