From 4b8a27071436b229bcb533b197f16ad54528528a Mon Sep 17 00:00:00 2001 From: Correl Roush Date: Mon, 2 Jan 2012 10:46:22 -0500 Subject: [PATCH] Replaced transmissionrpc with stock 0.8 release --- resources/lib/transmissionrpc/__init__.py | 8 +- resources/lib/transmissionrpc/client.py | 358 +++++++++---------- resources/lib/transmissionrpc/constants.py | 43 ++- resources/lib/transmissionrpc/error.py | 20 +- resources/lib/transmissionrpc/httphandler.py | 12 +- resources/lib/transmissionrpc/session.py | 8 +- resources/lib/transmissionrpc/torrent.py | 110 +++--- resources/lib/transmissionrpc/utils.py | 18 +- 8 files changed, 305 insertions(+), 272 deletions(-) diff --git a/resources/lib/transmissionrpc/__init__.py b/resources/lib/transmissionrpc/__init__.py index 82f7e21..5d3301f 100644 --- a/resources/lib/transmissionrpc/__init__.py +++ b/resources/lib/transmissionrpc/__init__.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # 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 ' -__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' diff --git a/resources/lib/transmissionrpc/client.py b/resources/lib/transmissionrpc/client.py index 9d922a8..b8f2ebb 100644 --- a/resources/lib/transmissionrpc/client.py +++ b/resources/lib/transmissionrpc/client.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # Licensed under the MIT license. -import os, re, time -import warnings -import httplib, urllib2, urlparse, base64 +import re, time +import urllib2, urlparse, base64 try: import json @@ -20,7 +19,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 +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 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 +64,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 +79,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 +104,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 +124,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 +147,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 +156,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 +218,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 +253,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 +267,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 +285,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 +312,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 +339,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 +384,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 +413,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 +443,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 +468,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 +480,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 +511,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 +554,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 +596,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:: diff --git a/resources/lib/transmissionrpc/constants.py b/resources/lib/transmissionrpc/constants.py index 56fae51..8661da3 100644 --- a/resources/lib/transmissionrpc/constants.py +++ b/resources/lib/transmissionrpc/constants.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # 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).'), }, } diff --git a/resources/lib/transmissionrpc/error.py b/resources/lib/transmissionrpc/error.py index 8fb2a57..9b50e00 100644 --- a/resources/lib/transmissionrpc/error.py +++ b/resources/lib/transmissionrpc/error.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # 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 '' % (self.code, self.message) + return '' % (self.code, self._message) def __str__(self): - return '' % (self.code, self.message) + return 'HTTPHandlerError %d: %s' % (self.code, self._message) def __unicode__(self): - return u'' % (self.code, self.message) + return u'HTTPHandlerError %d: %s' % (self.code, self._message) diff --git a/resources/lib/transmissionrpc/httphandler.py b/resources/lib/transmissionrpc/httphandler.py index e4933d4..d34b781 100644 --- a/resources/lib/transmissionrpc/httphandler.py +++ b/resources/lib/transmissionrpc/httphandler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010 Erik Svensson +# Copyright (c) 2011 Erik Svensson # 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]) diff --git a/resources/lib/transmissionrpc/session.py b/resources/lib/transmissionrpc/session.py index 56e842c..3f71a2a 100644 --- a/resources/lib/transmissionrpc/session.py +++ b/resources/lib/transmissionrpc/session.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # 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): diff --git a/resources/lib/transmissionrpc/torrent.py b/resources/lib/transmissionrpc/torrent.py index ab12148..357d0a1 100644 --- a/resources/lib/transmissionrpc/torrent.py +++ b/resources/lib/transmissionrpc/torrent.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # 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,31 @@ 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: + :: - { - : { - 'name': , - 'size': , - 'completed': , - 'priority': , - 'selected': - } - - ... - } + { + : { + 'name': , + 'size': , + 'completed': , + 'priority': , + 'selected': + } + ... + } """ 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 = True if item[3] else False priority = PRIORITY[item[2]] result[item[0]] = { 'selected': selected, @@ -115,10 +148,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 +164,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 +197,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 ::. - """ + * If eta is -1 the result is 'not available' + * If eta is -2 the result is 'unknown' + * Otherwise eta is formatted as ::. + """ eta = self.fields['eta'] if eta == -1: return 'not available' @@ -180,11 +210,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']] \ No newline at end of file diff --git a/resources/lib/transmissionrpc/utils.py b/resources/lib/transmissionrpc/utils.py index a84e820..8a74c11 100644 --- a/resources/lib/transmissionrpc/utils.py +++ b/resources/lib/transmissionrpc/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2008-2010 Erik Svensson +# Copyright (c) 2008-2011 Erik Svensson # 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 ::. + Format datetime.timedelta into ::. """ 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 '-' @@ -88,10 +91,7 @@ def rpc_bool(arg): arg = bool(int(arg)) except ValueError: arg = arg.lower() in [u'true', u'yes'] - if bool(arg): - return 1 - else: - return 0 + return 1 if bool(arg) else 0 TR_TYPE_MAP = { 'number' : int, @@ -178,7 +178,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():