Update transmissionrpc to version 0.11

This commit is contained in:
Correl Roush 2014-07-26 20:39:57 -04:00
parent f341a8a125
commit 13dc11f94d
10 changed files with 2179 additions and 1625 deletions

View file

@ -7,6 +7,7 @@
<import addon="xbmc.python" version="2.14.0"/> <import addon="xbmc.python" version="2.14.0"/>
<import addon="script.module.simplejson" version="3.3.0"/> <import addon="script.module.simplejson" version="3.3.0"/>
<import addon="script.module.beautifulsoup" version="3.0.8" /> <import addon="script.module.beautifulsoup" version="3.0.8" />
<import addon="script.module.six" version="1.5.2" />
</requires> </requires>
<extension point="xbmc.python.script" <extension point="xbmc.python.script"
library="default.py" /> library="default.py" />

View file

@ -149,7 +149,7 @@ class TransmissionGUI(xbmcgui.WindowXMLDialog):
if selected < 0: if selected < 0:
return return
try: try:
self.transmission.add_uri(results[selected]['url']) self.transmission.add_torrent(results[selected]['url'])
except: except:
xbmcgui.Dialog().ok(_(0), _(293)) xbmcgui.Dialog().ok(_(0), _(293))
return return

34
resources/lib/transmissionrpc/__init__.py Normal file → Executable file
View file

@ -1,16 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
from transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT, PRIORITY, RATIO_LIMIT, LOGGER from transmissionrpc.constants import DEFAULT_PORT, DEFAULT_TIMEOUT, PRIORITY, RATIO_LIMIT, LOGGER
from transmissionrpc.error import TransmissionError, HTTPHandlerError from transmissionrpc.error import TransmissionError, HTTPHandlerError
from transmissionrpc.httphandler import HTTPHandler, DefaultHTTPHandler from transmissionrpc.httphandler import HTTPHandler, DefaultHTTPHandler
from transmissionrpc.torrent import Torrent from transmissionrpc.torrent import Torrent
from transmissionrpc.session import Session from transmissionrpc.session import Session
from transmissionrpc.client import Client from transmissionrpc.client import Client
from transmissionrpc.utils import add_stdout_logger from transmissionrpc.utils import add_stdout_logger, add_file_logger
__author__ = u'Erik Svensson <erik.public@gmail.com>' __author__ = 'Erik Svensson <erik.public@gmail.com>'
__version__ = u'0.8' __version_major__ = 0
__copyright__ = u'Copyright (c) 2008-2011 Erik Svensson' __version_minor__ = 11
__license__ = u'MIT' __version__ = '{0}.{1}'.format(__version_major__, __version_minor__)
__copyright__ = 'Copyright (c) 2008-2013 Erik Svensson'
__license__ = 'MIT'

1667
resources/lib/transmissionrpc/client.py Normal file → Executable file

File diff suppressed because it is too large Load diff

586
resources/lib/transmissionrpc/constants.py Normal file → Executable file
View file

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

View file

@ -1,52 +1,54 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
class TransmissionError(Exception): from six import string_types, integer_types
"""
This exception is raised when there has occurred an error related to class TransmissionError(Exception):
communication with Transmission. It is a subclass of Exception. """
""" This exception is raised when there has occurred an error related to
def __init__(self, message='', original=None): communication with Transmission. It is a subclass of Exception.
Exception.__init__(self) """
self._message = message def __init__(self, message='', original=None):
self.original = original Exception.__init__(self)
self.message = message
def __str__(self): self.original = original
if self.original:
original_name = type(self.original).__name__ def __str__(self):
return '%s Original exception: %s, "%s"' % (self.message, original_name, str(self.original)) if self.original:
else: original_name = type(self.original).__name__
return self.message return '%s Original exception: %s, "%s"' % (self.message, original_name, str(self.original))
else:
class HTTPHandlerError(Exception): return self.message
"""
This exception is raised when there has occurred an error related to class HTTPHandlerError(Exception):
the HTTP handler. It is a subclass of Exception. """
""" This exception is raised when there has occurred an error related to
def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None): the HTTP handler. It is a subclass of Exception.
Exception.__init__(self) """
self.url = '' def __init__(self, httpurl=None, httpcode=None, httpmsg=None, httpheaders=None, httpdata=None):
self.code = 600 Exception.__init__(self)
self._message = '' self.url = ''
self.headers = {} self.code = 600
self.data = '' self.message = ''
if isinstance(httpurl, (str, unicode)): self.headers = {}
self.url = httpurl self.data = ''
if isinstance(httpcode, (int, long)): if isinstance(httpurl, string_types):
self.code = httpcode self.url = httpurl
if isinstance(httpmsg, (str, unicode)): if isinstance(httpcode, integer_types):
self._message = httpmsg self.code = httpcode
if isinstance(httpheaders, dict): if isinstance(httpmsg, string_types):
self.headers = httpheaders self.message = httpmsg
if isinstance(httpdata, (str, unicode)): if isinstance(httpheaders, dict):
self.data = httpdata self.headers = httpheaders
if isinstance(httpdata, string_types):
def __repr__(self): self.data = httpdata
return '<HTTPHandlerError %d, %s>' % (self.code, self._message)
def __repr__(self):
def __str__(self): return '<HTTPHandlerError %d, %s>' % (self.code, self.message)
return 'HTTPHandlerError %d: %s' % (self.code, self._message)
def __str__(self):
def __unicode__(self): return 'HTTPHandlerError %d: %s' % (self.code, self.message)
return u'HTTPHandlerError %d: %s' % (self.code, self._message)
def __unicode__(self):
return 'HTTPHandlerError %d: %s' % (self.code, self.message)

View file

@ -1,72 +1,82 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2011 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2011-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import sys, httplib, urllib2 import sys
from transmissionrpc.error import HTTPHandlerError from transmissionrpc.error import HTTPHandlerError
class HTTPHandler(object): from six import PY3
"""
Prototype for HTTP handling. if PY3:
""" from urllib.request import Request, build_opener, \
def set_authentication(self, uri, login, password): HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, HTTPDigestAuthHandler
""" from urllib.error import HTTPError, URLError
Transmission use basic authentication in earlier versions and digest from http.client import BadStatusLine
authentication in later versions. else:
from urllib2 import Request, build_opener, \
* uri, the authentication realm URI. HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler, HTTPDigestAuthHandler
* login, the authentication login. from urllib2 import HTTPError, URLError
* password, the authentication password. from httplib import BadStatusLine
"""
raise NotImplementedError("Bad HTTPHandler, failed to implement set_authentication.") class HTTPHandler(object):
"""
def request(self, url, query, headers, timeout): Prototype for HTTP handling.
""" """
Implement a HTTP POST request here. def set_authentication(self, uri, login, password):
"""
* url, The URL to request. Transmission use basic authentication in earlier versions and digest
* query, The query data to send. This is a JSON data string. authentication in later versions.
* headers, a dictionary of headers to send.
* timeout, requested request timeout in seconds. * uri, the authentication realm URI.
""" * login, the authentication login.
raise NotImplementedError("Bad HTTPHandler, failed to implement request.") * password, the authentication password.
"""
class DefaultHTTPHandler(HTTPHandler): raise NotImplementedError("Bad HTTPHandler, failed to implement set_authentication.")
"""
The default HTTP handler provided with transmissionrpc. def request(self, url, query, headers, timeout):
""" """
def __init__(self): Implement a HTTP POST request here.
HTTPHandler.__init__(self)
* url, The URL to request.
def set_authentication(self, uri, login, password): * query, The query data to send. This is a JSON data string.
password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() * headers, a dictionary of headers to send.
password_manager.add_password(realm=None, uri=uri, user=login, passwd=password) * timeout, requested request timeout in seconds.
opener = urllib2.build_opener( """
urllib2.HTTPBasicAuthHandler(password_manager) raise NotImplementedError("Bad HTTPHandler, failed to implement request.")
, urllib2.HTTPDigestAuthHandler(password_manager)
) class DefaultHTTPHandler(HTTPHandler):
urllib2.install_opener(opener) """
The default HTTP handler provided with transmissionrpc.
def request(self, url, query, headers, timeout): """
request = urllib2.Request(url, query, headers) def __init__(self):
try: HTTPHandler.__init__(self)
if (sys.version_info[0] == 2 and sys.version_info[1] > 5) or sys.version_info[0] > 2: self.http_opener = build_opener()
response = urllib2.urlopen(request, timeout=timeout)
else: def set_authentication(self, uri, login, password):
response = urllib2.urlopen(request) password_manager = HTTPPasswordMgrWithDefaultRealm()
except urllib2.HTTPError, error: password_manager.add_password(realm=None, uri=uri, user=login, passwd=password)
if error.fp is None: self.http_opener = build_opener(HTTPBasicAuthHandler(password_manager), HTTPDigestAuthHandler(password_manager))
raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs))
else: def request(self, url, query, headers, timeout):
raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs), error.read()) request = Request(url, query.encode('utf-8'), headers)
except urllib2.URLError, error: try:
# urllib2.URLError documentation is horrendous! if (sys.version_info[0] == 2 and sys.version_info[1] > 5) or sys.version_info[0] > 2:
# Try to get the tuple arguments of URLError response = self.http_opener.open(request, timeout=timeout)
if hasattr(error.reason, 'args') and isinstance(error.reason.args, tuple) and len(error.reason.args) == 2: else:
raise HTTPHandlerError(httpcode=error.reason.args[0], httpmsg=error.reason.args[1]) response = self.http_opener.open(request)
else: except HTTPError as error:
raise HTTPHandlerError(httpmsg='urllib2.URLError: %s' % (error.reason)) if error.fp is None:
except httplib.BadStatusLine, error: raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs))
raise HTTPHandlerError(httpmsg='httplib.BadStatusLine: %s' % (error.line)) else:
return response.read() raise HTTPHandlerError(error.filename, error.code, error.msg, dict(error.hdrs), error.read())
except URLError as error:
# 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])
else:
raise HTTPHandlerError(httpmsg='urllib2.URLError: %s' % (error.reason))
except BadStatusLine as error:
raise HTTPHandlerError(httpmsg='httplib.BadStatusLine: %s' % (error.line))
return response.read().decode('utf-8')

View file

@ -1,44 +1,111 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
class Session(object): from transmissionrpc.utils import Field
"""
Session is a class holding the session data for a Transmission daemon. from six import iteritems, integer_types
Access the session field can be done through attributes. class Session(object):
The attributes available are the same as the session arguments in the """
Transmission RPC specification, but with underscore instead of hyphen. Session is a class holding the session data for a Transmission daemon.
``download-dir`` -> ``download_dir``.
""" Access the session field can be done through attributes.
The attributes available are the same as the session arguments in the
def __init__(self, fields=None): Transmission RPC specification, but with underscore instead of hyphen.
self.fields = {} ``download-dir`` -> ``download_dir``.
if fields is not None: """
self.update(fields)
def __init__(self, client=None, fields=None):
def update(self, other): self._client = client
"""Update the session data from a session arguments dictionary""" self._fields = {}
if fields is not None:
fields = None self._update_fields(fields)
if isinstance(other, dict):
fields = other def __getattr__(self, name):
elif isinstance(other, Session): try:
fields = other.fields return self._fields[name].value
else: except KeyError:
raise ValueError('Cannot update with supplied data') raise AttributeError('No attribute %s' % name)
for key, value in fields.iteritems(): def __str__(self):
self.fields[key.replace('-', '_')] = value text = ''
for key in sorted(self._fields.keys()):
def __getattr__(self, name): text += "% 32s: %s\n" % (key[-32:], self._fields[key].value)
try: return text
return self.fields[name]
except KeyError: def _update_fields(self, other):
raise AttributeError('No attribute %s' % name) """
Update the session data from a Transmission JSON-RPC arguments dictionary
def __str__(self): """
text = '' if isinstance(other, dict):
for key in sorted(self.fields.keys()): for key, value in iteritems(other):
text += "% 32s: %s\n" % (key[-32:], self.fields[key]) self._fields[key.replace('-', '_')] = Field(value, False)
return text elif isinstance(other, Session):
for key in list(other._fields.keys()):
self._fields[key] = Field(other._fields[key].value, False)
else:
raise ValueError('Cannot update with supplied data')
def _dirty_fields(self):
"""Enumerate changed fields"""
outgoing_keys = ['peer_port', 'pex_enabled']
fields = []
for key in outgoing_keys:
if key in self._fields and self._fields[key].dirty:
fields.append(key)
return fields
def _push(self):
"""Push changed fields to the server"""
dirty = self._dirty_fields()
args = {}
for key in dirty:
args[key] = self._fields[key].value
self._fields[key] = self._fields[key]._replace(dirty=False)
if len(args) > 0:
self._client.set_session(**args)
def update(self, timeout=None):
"""Update the session information."""
self._push()
session = self._client.get_session(timeout=timeout)
self._update_fields(session)
session = self._client.session_stats(timeout=timeout)
self._update_fields(session)
def from_request(self, data):
"""Update the session information."""
self._update_fields(data)
def _get_peer_port(self):
"""
Get the peer port.
"""
return self._fields['peer_port'].value
def _set_peer_port(self, port):
"""
Set the peer port.
"""
if isinstance(port, integer_types):
self._fields['peer_port'] = Field(port, True)
self._push()
else:
raise ValueError("Not a valid limit")
peer_port = property(_get_peer_port, _set_peer_port, None, "Peer port. This is a mutator.")
def _get_pex_enabled(self):
"""Is peer exchange enabled?"""
return self._fields['pex_enabled'].value
def _set_pex_enabled(self, enabled):
"""Enable/disable peer exchange."""
if isinstance(enabled, bool):
self._fields['pex_enabled'] = Field(enabled, True)
self._push()
else:
raise TypeError("Not a valid type")
pex_enabled = property(_get_pex_enabled, _set_pex_enabled, None, "Enable peer exchange. This is a mutator.")

701
resources/lib/transmissionrpc/torrent.py Normal file → Executable file
View file

@ -1,222 +1,479 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import sys, datetime import sys, datetime
from transmissionrpc.constants import PRIORITY from transmissionrpc.constants import PRIORITY, RATIO_LIMIT, IDLE_LIMIT
from transmissionrpc.utils import format_timedelta from transmissionrpc.utils import Field, format_timedelta
class Torrent(object): from six import integer_types, string_types, text_type, iteritems
"""
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. def get_status_old(code):
This class has a few convenience properties using the torrent data. """Get the torrent status using old status codes"""
""" mapping = {
(1<<0): 'check pending',
def __init__(self, client, fields): (1<<1): 'checking',
if 'id' not in fields: (1<<2): 'downloading',
raise ValueError('Torrent requires an id') (1<<3): 'seeding',
self.fields = {} (1<<4): 'stopped',
self.update(fields) }
self.client = client return mapping[code]
def _getNameString(self, codec=None): def get_status_new(code):
if codec is None: """Get the torrent status using new status codes"""
codec = sys.getdefaultencoding() mapping = {
name = None 0: 'stopped',
# try to find name 1: 'check pending',
if 'name' in self.fields: 2: 'checking',
name = self.fields['name'] 3: 'download pending',
# if name is unicode, try to decode 4: 'downloading',
if isinstance(name, unicode): 5: 'seed pending',
try: 6: 'seeding',
name = name.encode(codec) }
except UnicodeError: return mapping[code]
name = None
return name class Torrent(object):
"""
def __repr__(self): Torrent is a class holding the data received from Transmission regarding a bittorrent transfer.
tid = self.fields['id']
name = self._getNameString() All fetched torrent fields are accessible through this class using attributes.
if isinstance(name, str): This class has a few convenience properties using the torrent data.
return '<Torrent %d \"%s\">' % (tid, name) """
else:
return '<Torrent %d>' % (tid) def __init__(self, client, fields):
if 'id' not in fields:
def __str__(self): raise ValueError('Torrent requires an id')
name = self._getNameString() self._fields = {}
if isinstance(name, str): self._update_fields(fields)
return 'Torrent \"%s\"' % (name) self._incoming_pending = False
else: self._outgoing_pending = False
return 'Torrent' self._client = client
def __copy__(self): def _get_name_string(self, codec=None):
return Torrent(self.client, self.fields) """Get the name"""
if codec is None:
def _rpc_version(self): codec = sys.getdefaultencoding()
if self.client: name = None
return self.client.rpc_version # try to find name
return 2 if 'name' in self._fields:
name = self._fields['name'].value
def _status_old(self, code): # if name is unicode, try to decode
mapping = { if isinstance(name, text_type):
(1<<0): 'check pending', try:
(1<<1): 'checking', name = name.encode(codec)
(1<<2): 'downloading', except UnicodeError:
(1<<3): 'seeding', name = None
(1<<4): 'stopped', return name
}
return mapping[code] def __repr__(self):
tid = self._fields['id'].value
def _status_new(self, code): name = self._get_name_string()
mapping = { if isinstance(name, str):
0: 'stopped', return '<Torrent %d \"%s\">' % (tid, name)
1: 'check pending', else:
2: 'checking', return '<Torrent %d>' % (tid)
3: 'download pending',
4: 'downloading', def __str__(self):
5: 'seed pending', name = self._get_name_string()
6: 'seeding', if isinstance(name, str):
} return 'Torrent \"%s\"' % (name)
return mapping[code] else:
return 'Torrent'
def _status(self):
code = self.fields['status'] def __copy__(self):
if self._rpc_version() >= 14: return Torrent(self._client, self._fields)
return self._status_new(code)
else: def __getattr__(self, name):
return self._status_old(code) try:
return self._fields[name].value
def update(self, other): except KeyError:
""" raise AttributeError('No attribute %s' % name)
Update the torrent data from a Transmission JSON-RPC arguments dictionary
""" def _rpc_version(self):
fields = None """Get the Transmission RPC API version."""
if isinstance(other, dict): if self._client:
fields = other return self._client.rpc_version
elif isinstance(other, Torrent): return 2
fields = other.fields
else: def _dirty_fields(self):
raise ValueError('Cannot update with supplied data') """Enumerate changed fields"""
for key, value in fields.iteritems(): outgoing_keys = ['bandwidthPriority', 'downloadLimit', 'downloadLimited', 'peer_limit', 'queuePosition'
self.fields[key.replace('-', '_')] = value , 'seedIdleLimit', 'seedIdleMode', 'seedRatioLimit', 'seedRatioMode', 'uploadLimit', 'uploadLimited']
fields = []
def files(self): for key in outgoing_keys:
""" if key in self._fields and self._fields[key].dirty:
Get list of files for this torrent. fields.append(key)
return fields
This function returns a dictionary with file information for each file.
The file information is has following fields: def _push(self):
:: """Push changed fields to the server"""
dirty = self._dirty_fields()
{ args = {}
<file id>: { for key in dirty:
'name': <file name>, args[key] = self._fields[key].value
'size': <file size in bytes>, self._fields[key] = self._fields[key]._replace(dirty=False)
'completed': <bytes completed>, if len(args) > 0:
'priority': <priority ('high'|'normal'|'low')>, self._client.change_torrent(self.id, **args)
'selected': <selected for download>
} def _update_fields(self, other):
... """
} Update the torrent data from a Transmission JSON-RPC arguments dictionary
""" """
result = {} fields = None
if 'files' in self.fields: if isinstance(other, dict):
indices = xrange(len(self.fields['files'])) for key, value in iteritems(other):
files = self.fields['files'] self._fields[key.replace('-', '_')] = Field(value, False)
priorities = self.fields['priorities'] elif isinstance(other, Torrent):
wanted = self.fields['wanted'] for key in list(other._fields.keys()):
for item in zip(indices, files, priorities, wanted): self._fields[key] = Field(other._fields[key].value, False)
selected = False else:
if item[3]: raise ValueError('Cannot update with supplied data')
selected = True self._incoming_pending = False
priority = PRIORITY[item[2]]
result[item[0]] = { def _status(self):
'selected': selected, """Get the torrent status"""
'priority': priority, code = self._fields['status'].value
'size': item[1]['length'], if self._rpc_version() >= 14:
'name': item[1]['name'], return get_status_new(code)
'completed': item[1]['bytesCompleted']} else:
return result return get_status_old(code)
def __getattr__(self, name): def files(self):
try: """
return self.fields[name] Get list of files for this torrent.
except KeyError:
raise AttributeError('No attribute %s' % name) This function returns a dictionary with file information for each file.
The file information is has following fields:
@property ::
def status(self):
""" {
Returns the torrent status. Is either one of 'check pending', 'checking', <file id>: {
'downloading', 'seeding' or 'stopped'. The first two is related to 'name': <file name>,
verification. 'size': <file size in bytes>,
""" 'completed': <bytes completed>,
return self._status() 'priority': <priority ('high'|'normal'|'low')>,
'selected': <selected for download>
@property }
def progress(self): ...
"""Get the download progress in percent.""" }
try: """
return 100.0 * (self.fields['sizeWhenDone'] - self.fields['leftUntilDone']) / float(self.fields['sizeWhenDone']) result = {}
except ZeroDivisionError: if 'files' in self._fields:
return 0.0 files = self._fields['files'].value
indices = range(len(files))
@property priorities = self._fields['priorities'].value
def ratio(self): wanted = self._fields['wanted'].value
"""Get the upload/download ratio.""" for item in zip(indices, files, priorities, wanted):
return float(self.fields['uploadRatio']) selected = True if item[3] else False
priority = PRIORITY[item[2]]
@property result[item[0]] = {
def eta(self): 'selected': selected,
"""Get the "eta" as datetime.timedelta.""" 'priority': priority,
eta = self.fields['eta'] 'size': item[1]['length'],
if eta >= 0: 'name': item[1]['name'],
return datetime.timedelta(seconds=eta) 'completed': item[1]['bytesCompleted']}
else: return result
ValueError('eta not valid')
@property
@property def status(self):
def date_active(self): """
"""Get the attribute "activityDate" as datetime.datetime.""" Returns the torrent status. Is either one of 'check pending', 'checking',
return datetime.datetime.fromtimestamp(self.fields['activityDate']) 'downloading', 'seeding' or 'stopped'. The first two is related to
verification.
@property """
def date_added(self): return self._status()
"""Get the attribute "addedDate" as datetime.datetime."""
return datetime.datetime.fromtimestamp(self.fields['addedDate']) @property
def progress(self):
@property """Get the download progress in percent."""
def date_started(self): try:
"""Get the attribute "startDate" as datetime.datetime.""" size = self._fields['sizeWhenDone'].value
return datetime.datetime.fromtimestamp(self.fields['startDate']) left = self._fields['leftUntilDone'].value
return 100.0 * (size - left) / float(size)
@property except ZeroDivisionError:
def date_done(self): return 0.0
"""Get the attribute "doneDate" as datetime.datetime."""
return datetime.datetime.fromtimestamp(self.fields['doneDate']) @property
def ratio(self):
def format_eta(self): """Get the upload/download ratio."""
""" return float(self._fields['uploadRatio'].value)
Returns the attribute *eta* formatted as a string.
@property
* If eta is -1 the result is 'not available' def eta(self):
* If eta is -2 the result is 'unknown' """Get the "eta" as datetime.timedelta."""
* Otherwise eta is formatted as <days> <hours>:<minutes>:<seconds>. eta = self._fields['eta'].value
""" if eta >= 0:
eta = self.fields['eta'] return datetime.timedelta(seconds=eta)
if eta == -1: else:
return 'not available' raise ValueError('eta not valid')
elif eta == -2:
return 'unknown' @property
else: def date_active(self):
return format_timedelta(self.eta) """Get the attribute "activityDate" as datetime.datetime."""
return datetime.datetime.fromtimestamp(self._fields['activityDate'].value)
@property
def priority(self): @property
""" def date_added(self):
Get the priority as string. """Get the attribute "addedDate" as datetime.datetime."""
Can be one of 'low', 'normal', 'high'. return datetime.datetime.fromtimestamp(self._fields['addedDate'].value)
"""
return PRIORITY[self.fields['bandwidthPriority']] @property
def date_started(self):
"""Get the attribute "startDate" as datetime.datetime."""
return datetime.datetime.fromtimestamp(self._fields['startDate'].value)
@property
def date_done(self):
"""Get the attribute "doneDate" as datetime.datetime."""
return datetime.datetime.fromtimestamp(self._fields['doneDate'].value)
def format_eta(self):
"""
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>.
"""
eta = self._fields['eta'].value
if eta == -1:
return 'not available'
elif eta == -2:
return 'unknown'
else:
return format_timedelta(self.eta)
def _get_download_limit(self):
"""
Get the download limit.
Can be a number or None.
"""
if self._fields['downloadLimited'].value:
return self._fields['downloadLimit'].value
else:
return None
def _set_download_limit(self, limit):
"""
Get the download limit.
Can be a number, 'session' or None.
"""
if isinstance(limit, integer_types):
self._fields['downloadLimited'] = Field(True, True)
self._fields['downloadLimit'] = Field(limit, True)
self._push()
elif limit == None:
self._fields['downloadLimited'] = Field(False, True)
self._push()
else:
raise ValueError("Not a valid limit")
download_limit = property(_get_download_limit, _set_download_limit, None, "Download limit in Kbps or None. This is a mutator.")
def _get_peer_limit(self):
"""
Get the peer limit.
"""
return self._fields['peer_limit'].value
def _set_peer_limit(self, limit):
"""
Set the peer limit.
"""
if isinstance(limit, integer_types):
self._fields['peer_limit'] = Field(limit, True)
self._push()
else:
raise ValueError("Not a valid limit")
peer_limit = property(_get_peer_limit, _set_peer_limit, None, "Peer limit. This is a mutator.")
def _get_priority(self):
"""
Get the priority as string.
Can be one of 'low', 'normal', 'high'.
"""
return PRIORITY[self._fields['bandwidthPriority'].value]
def _set_priority(self, priority):
"""
Set the priority as string.
Can be one of 'low', 'normal', 'high'.
"""
if isinstance(priority, string_types):
self._fields['bandwidthPriority'] = Field(PRIORITY[priority], True)
self._push()
priority = property(_get_priority, _set_priority, None
, "Bandwidth priority as string. Can be one of 'low', 'normal', 'high'. This is a mutator.")
def _get_seed_idle_limit(self):
"""
Get the seed idle limit in minutes.
"""
return self._fields['seedIdleLimit'].value
def _set_seed_idle_limit(self, limit):
"""
Set the seed idle limit in minutes.
"""
if isinstance(limit, integer_types):
self._fields['seedIdleLimit'] = Field(limit, True)
self._push()
else:
raise ValueError("Not a valid limit")
seed_idle_limit = property(_get_seed_idle_limit, _set_seed_idle_limit, None
, "Torrent seed idle limit in minutes. Also see seed_idle_mode. This is a mutator.")
def _get_seed_idle_mode(self):
"""
Get the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
"""
return IDLE_LIMIT[self._fields['seedIdleMode'].value]
def _set_seed_idle_mode(self, mode):
"""
Set the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
"""
if isinstance(mode, str):
self._fields['seedIdleMode'] = Field(IDLE_LIMIT[mode], True)
self._push()
else:
raise ValueError("Not a valid limit")
seed_idle_mode = property(_get_seed_idle_mode, _set_seed_idle_mode, None,
"""
Seed idle mode as string. Can be one of 'global', 'single' or 'unlimited'.
* global, use session seed idle limit.
* single, use torrent seed idle limit. See seed_idle_limit.
* unlimited, no seed idle limit.
This is a mutator.
"""
)
def _get_seed_ratio_limit(self):
"""
Get the seed ratio limit as float.
"""
return float(self._fields['seedRatioLimit'].value)
def _set_seed_ratio_limit(self, limit):
"""
Set the seed ratio limit as float.
"""
if isinstance(limit, (integer_types, float)) and limit >= 0.0:
self._fields['seedRatioLimit'] = Field(float(limit), True)
self._push()
else:
raise ValueError("Not a valid limit")
seed_ratio_limit = property(_get_seed_ratio_limit, _set_seed_ratio_limit, None
, "Torrent seed ratio limit as float. Also see seed_ratio_mode. This is a mutator.")
def _get_seed_ratio_mode(self):
"""
Get the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
"""
return RATIO_LIMIT[self._fields['seedRatioMode'].value]
def _set_seed_ratio_mode(self, mode):
"""
Set the seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
"""
if isinstance(mode, str):
self._fields['seedRatioMode'] = Field(RATIO_LIMIT[mode], True)
self._push()
else:
raise ValueError("Not a valid limit")
seed_ratio_mode = property(_get_seed_ratio_mode, _set_seed_ratio_mode, None,
"""
Seed ratio mode as string. Can be one of 'global', 'single' or 'unlimited'.
* global, use session seed ratio limit.
* single, use torrent seed ratio limit. See seed_ratio_limit.
* unlimited, no seed ratio limit.
This is a mutator.
"""
)
def _get_upload_limit(self):
"""
Get the upload limit.
Can be a number or None.
"""
if self._fields['uploadLimited'].value:
return self._fields['uploadLimit'].value
else:
return None
def _set_upload_limit(self, limit):
"""
Set the upload limit.
Can be a number, 'session' or None.
"""
if isinstance(limit, integer_types):
self._fields['uploadLimited'] = Field(True, True)
self._fields['uploadLimit'] = Field(limit, True)
self._push()
elif limit == None:
self._fields['uploadLimited'] = Field(False, True)
self._push()
else:
raise ValueError("Not a valid limit")
upload_limit = property(_get_upload_limit, _set_upload_limit, None, "Upload limit in Kbps or None. This is a mutator.")
def _get_queue_position(self):
"""Get the queue position for this torrent."""
if self._rpc_version() >= 14:
return self._fields['queuePosition'].value
else:
return 0
def _set_queue_position(self, position):
"""Set the queue position for this torrent."""
if self._rpc_version() >= 14:
if isinstance(position, integer_types):
self._fields['queuePosition'] = Field(position, True)
self._push()
else:
raise ValueError("Not a valid position")
else:
pass
queue_position = property(_get_queue_position, _set_queue_position, None, "Queue position")
def update(self, timeout=None):
"""Update the torrent information."""
self._push()
torrent = self._client.get_torrent(self.id, timeout=timeout)
self._update_fields(torrent)
def start(self, bypass_queue=False, timeout=None):
"""
Start the torrent.
"""
self._incoming_pending = True
self._client.start_torrent(self.id, bypass_queue=bypass_queue, timeout=timeout)
def stop(self, timeout=None):
"""Stop the torrent."""
self._incoming_pending = True
self._client.stop_torrent(self.id, timeout=timeout)
def move_data(self, location, timeout=None):
"""Move torrent data to location."""
self._incoming_pending = True
self._client.move_torrent_data(self.id, location, timeout=timeout)
def locate_data(self, location, timeout=None):
"""Locate torrent data at location."""
self._incoming_pending = True
self._client.locate_torrent_data(self.id, location, timeout=timeout)

View file

@ -1,191 +1,207 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2008-2011 Erik Svensson <erik.public@gmail.com> # Copyright (c) 2008-2013 Erik Svensson <erik.public@gmail.com>
# Licensed under the MIT license. # Licensed under the MIT license.
import socket, datetime, logging import socket, datetime, logging
import transmissionrpc.constants as constants from collections import namedtuple
from transmissionrpc.constants import LOGGER import transmissionrpc.constants as constants
from transmissionrpc.constants import LOGGER
UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
from six import string_types, iteritems
def format_size(size):
""" UNITS = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
Format byte size into IEC prefixes, B, KiB, MiB ...
""" def format_size(size):
size = float(size) """
i = 0 Format byte size into IEC prefixes, B, KiB, MiB ...
while size >= 1024.0 and i < len(UNITS): """
i += 1 size = float(size)
size /= 1024.0 i = 0
return (size, UNITS[i]) while size >= 1024.0 and i < len(UNITS):
i += 1
def format_speed(size): size /= 1024.0
""" return (size, UNITS[i])
Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ...
""" def format_speed(size):
(size, unit) = format_size(size) """
return (size, unit + '/s') Format bytes per second speed into IEC prefixes, B/s, KiB/s, MiB/s ...
"""
def format_timedelta(delta): (size, unit) = format_size(size)
""" return (size, unit + '/s')
Format datetime.timedelta into <days> <hours>:<minutes>:<seconds>.
""" def format_timedelta(delta):
minutes, seconds = divmod(delta.seconds, 60) """
hours, minutes = divmod(minutes, 60) Format datetime.timedelta into <days> <hours>:<minutes>:<seconds>.
return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds) """
minutes, seconds = divmod(delta.seconds, 60)
def format_timestamp(timestamp, utc=False): hours, minutes = divmod(minutes, 60)
""" return '%d %02d:%02d:%02d' % (delta.days, hours, minutes, seconds)
Format unix timestamp into ISO date format.
""" def format_timestamp(timestamp, utc=False):
if timestamp > 0: """
if utc: Format unix timestamp into ISO date format.
dt_timestamp = datetime.datetime.utcfromtimestamp(timestamp) """
else: if timestamp > 0:
dt_timestamp = datetime.datetime.fromtimestamp(timestamp) if utc:
return dt_timestamp.isoformat(' ') dt_timestamp = datetime.datetime.utcfromtimestamp(timestamp)
else: else:
return '-' dt_timestamp = datetime.datetime.fromtimestamp(timestamp)
return dt_timestamp.isoformat(' ')
class INetAddressError(Exception): else:
""" return '-'
Error parsing / generating a internet address.
""" class INetAddressError(Exception):
pass """
Error parsing / generating a internet address.
def inet_address(address, default_port, default_address='localhost'): """
""" pass
Parse internet address.
""" def inet_address(address, default_port, default_address='localhost'):
addr = address.split(':') """
if len(addr) == 1: Parse internet address.
try: """
port = int(addr[0]) addr = address.split(':')
addr = default_address if len(addr) == 1:
except ValueError: try:
addr = addr[0] port = int(addr[0])
port = default_port addr = default_address
elif len(addr) == 2: except ValueError:
try: addr = addr[0]
port = int(addr[1]) port = default_port
except ValueError: elif len(addr) == 2:
raise INetAddressError('Invalid address "%s".' % address) try:
if len(addr[0]) == 0: port = int(addr[1])
addr = default_address except ValueError:
else: raise INetAddressError('Invalid address "%s".' % address)
addr = addr[0] if len(addr[0]) == 0:
else: addr = default_address
raise INetAddressError('Invalid address "%s".' % address) else:
try: addr = addr[0]
socket.getaddrinfo(addr, port, socket.AF_INET, socket.SOCK_STREAM) else:
except socket.gaierror: raise INetAddressError('Invalid address "%s".' % address)
raise INetAddressError('Cannot look up address "%s".' % address) try:
return (addr, port) socket.getaddrinfo(addr, port, socket.AF_INET, socket.SOCK_STREAM)
except socket.gaierror:
def rpc_bool(arg): raise INetAddressError('Cannot look up address "%s".' % address)
""" return (addr, port)
Convert between Python boolean and Transmission RPC boolean.
""" def rpc_bool(arg):
if isinstance(arg, (str, unicode)): """
try: Convert between Python boolean and Transmission RPC boolean.
arg = bool(int(arg)) """
except ValueError: if isinstance(arg, string_types):
arg = arg.lower() in [u'true', u'yes'] try:
if bool(arg): arg = bool(int(arg))
return 1 except ValueError:
else: arg = arg.lower() in ['true', 'yes']
return 0 return 1 if bool(arg) else 0
TR_TYPE_MAP = { TR_TYPE_MAP = {
'number' : int, 'number' : int,
'string' : str, 'string' : str,
'double': float, 'double': float,
'boolean' : rpc_bool, 'boolean' : rpc_bool,
'array': list, 'array': list,
'object': dict 'object': dict
} }
def make_python_name(name): def make_python_name(name):
""" """
Convert Transmission RPC name to python compatible name. Convert Transmission RPC name to python compatible name.
""" """
return name.replace('-', '_') return name.replace('-', '_')
def make_rpc_name(name): def make_rpc_name(name):
""" """
Convert python compatible name to Transmission RPC name. Convert python compatible name to Transmission RPC name.
""" """
return name.replace('_', '-') return name.replace('_', '-')
def argument_value_convert(method, argument, value, rpc_version): def argument_value_convert(method, argument, value, rpc_version):
""" """
Check and fix Transmission RPC issues with regards to methods, arguments and values. Check and fix Transmission RPC issues with regards to methods, arguments and values.
""" """
if method in ('torrent-add', 'torrent-get', 'torrent-set'): if method in ('torrent-add', 'torrent-get', 'torrent-set'):
args = constants.TORRENT_ARGS[method[-3:]] args = constants.TORRENT_ARGS[method[-3:]]
elif method in ('session-get', 'session-set'): elif method in ('session-get', 'session-set'):
args = constants.SESSION_ARGS[method[-3:]] args = constants.SESSION_ARGS[method[-3:]]
else: else:
return ValueError('Method "%s" not supported' % (method)) return ValueError('Method "%s" not supported' % (method))
if argument in args: if argument in args:
info = args[argument] info = args[argument]
invalid_version = True invalid_version = True
while invalid_version: while invalid_version:
invalid_version = False invalid_version = False
replacement = None replacement = None
if rpc_version < info[1]: if rpc_version < info[1]:
invalid_version = True invalid_version = True
replacement = info[3] replacement = info[3]
if info[2] and info[2] <= rpc_version: if info[2] and info[2] <= rpc_version:
invalid_version = True invalid_version = True
replacement = info[4] replacement = info[4]
if invalid_version: if invalid_version:
if replacement: if replacement:
LOGGER.warning( LOGGER.warning(
'Replacing requested argument "%s" with "%s".' 'Replacing requested argument "%s" with "%s".'
% (argument, replacement)) % (argument, replacement))
argument = replacement argument = replacement
info = args[argument] info = args[argument]
else: else:
raise ValueError( raise ValueError(
'Method "%s" Argument "%s" does not exist in version %d.' 'Method "%s" Argument "%s" does not exist in version %d.'
% (method, argument, rpc_version)) % (method, argument, rpc_version))
return (argument, TR_TYPE_MAP[info[0]](value)) return (argument, TR_TYPE_MAP[info[0]](value))
else: else:
raise ValueError('Argument "%s" does not exists for method "%s".', raise ValueError('Argument "%s" does not exists for method "%s".',
(argument, method)) (argument, method))
def get_arguments(method, rpc_version): def get_arguments(method, rpc_version):
""" """
Get arguments for method in specified Transmission RPC version. Get arguments for method in specified Transmission RPC version.
""" """
if method in ('torrent-add', 'torrent-get', 'torrent-set'): if method in ('torrent-add', 'torrent-get', 'torrent-set'):
args = constants.TORRENT_ARGS[method[-3:]] args = constants.TORRENT_ARGS[method[-3:]]
elif method in ('session-get', 'session-set'): elif method in ('session-get', 'session-set'):
args = constants.SESSION_ARGS[method[-3:]] args = constants.SESSION_ARGS[method[-3:]]
else: else:
return ValueError('Method "%s" not supported' % (method)) return ValueError('Method "%s" not supported' % (method))
accessible = [] accessible = []
for argument, info in args.iteritems(): for argument, info in iteritems(args):
valid_version = True valid_version = True
if rpc_version < info[1]: if rpc_version < info[1]:
valid_version = False valid_version = False
if info[2] and info[2] <= rpc_version: if info[2] and info[2] <= rpc_version:
valid_version = False valid_version = False
if valid_version: if valid_version:
accessible.append(argument) accessible.append(argument)
return accessible return accessible
def add_stdout_logger(level='debug'): def add_stdout_logger(level='debug'):
""" """
Add a stdout target for the transmissionrpc logging. Add a stdout target for the transmissionrpc logging.
""" """
levels = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR} levels = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARNING, 'error': logging.ERROR}
trpc_logger = logging.getLogger('transmissionrpc') trpc_logger = logging.getLogger('transmissionrpc')
loghandler = logging.StreamHandler() loghandler = logging.StreamHandler()
if level in levels.keys(): if level in list(levels.keys()):
loglevel = levels[level] loglevel = levels[level]
trpc_logger.setLevel(loglevel) trpc_logger.setLevel(loglevel)
loghandler.setLevel(loglevel) loghandler.setLevel(loglevel)
trpc_logger.addHandler(loghandler) trpc_logger.addHandler(loghandler)
def add_file_logger(filepath, 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.FileHandler(filepath, encoding='utf-8')
if level in list(levels.keys()):
loglevel = levels[level]
trpc_logger.setLevel(loglevel)
loghandler.setLevel(loglevel)
trpc_logger.addHandler(loghandler)
Field = namedtuple('Field', ['value', 'dirty'])