Transmission API working

This commit is contained in:
rembo10
2013-07-27 14:19:26 +05:30
parent 0f6dbccaa3
commit a0b538861c
4 changed files with 185 additions and 12 deletions

View File

@@ -198,7 +198,7 @@
</div>
<div class="row">
<label>Transmission Password:</label>
<input type="text" name="transmission_password" value="${config['transmission_pass']}" size="30">
<input type="password" name="transmission_password" value="${config['transmission_pass']}" size="30">
</div>
</fieldset>
<fieldset id="utorrent_options">
@@ -337,7 +337,7 @@
<fieldset>
<legend>Torrents</legend>
<div class="row checkbox">
<input type="checkbox" name="use_piratebay" value="1" ${config['use_piratebay']} /><label>Pirate Bay</label>
<input type="checkbox" name="use_piratebay" value="1" ${config['use_piratebay']} /><label>The Pirate Bay</label>
</div>
<div class="row checkbox">
<input type="checkbox" name="use_isohunt" value="1" ${config['use_isohunt']} /><label>Isohunt</label>

View File

@@ -33,6 +33,7 @@ import string
import headphones, exceptions
from headphones import logger, db, helpers, classes, sab, nzbget
from headphones import transmission
import lib.bencode as bencode
@@ -473,7 +474,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
for result in resultlist:
if high_size_limit and (result[1] > high_size_limit):
logger.info(result[0] + " is too large for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Maxsize: " + helpers.bytes_to_mb(high_size_limit))
logger.info(result[0] + " is too large for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Maxsize: " + helpers.bytes_to_mb(high_size_limit) + ")")
# Add lossless nzbs to the "flac list" which we can use if there are no good lossy matches
if 'flac' in result[0].lower():
@@ -482,7 +483,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
continue
if low_size_limit and (result[1] < low_size_limit):
logger.info(result[0] + " is too small for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Minsize: " + helpers.bytes_to_mb(low_size_limit))
logger.info(result[0] + " is too small for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Minsize: " + helpers.bytes_to_mb(low_size_limit) + ")")
continue
delta = abs(targetsize - result[1])
@@ -1351,14 +1352,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
if data and bestqual:
logger.info(u'Found best result from %s: <a href="%s">%s</a> - %s' % (bestqual[3], bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1])))
torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year)
if headphones.TORRENTBLACKHOLE_DIR == "sendtracker":
torrent = classes.TorrentDataSearchResult()
torrent.extraInfo.append(data)
torrent.name = torrent_folder_name
sab.sendTorrent(torrent)
elif headphones.TORRENTBLACKHOLE_DIR != "":
# Blackhole
if headphones.TORRENT_DOWNLOADER == 0:
# Get torrent name from .torrent, this is usually used by the torrent client as the folder name
@@ -1388,6 +1384,11 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
logger.error('Couldn\'t get name from Torrent file: %s' % e)
break
elif headphones.TORRENT_DOWNLOADER == 1:
logger.info("Sending torrent to Transmission")
transmission.sendTorrent(bestqual[2])
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", torrent_folder_name, "torrent"])
@@ -1399,9 +1400,11 @@ def preprocesstorrent(resultlist, pre_sorted_list=False):
elif int(selresult[1]) < int(result[1]): # if size is lower than new result replace previous selected result (bigger size = better quality?)
selresult = result
# get outta here if rutracker or piratebay
# get outta here if rutracker or piratebay, or if we're using Transmission or uTorrent
if selresult[3] == 'rutracker.org' or selresult[3] == 'The Pirate Bay':
return True, selresult
if headphones.TORRENT_DOWNLOADER != 0:
return True, selresult
if pre_sorted_list:
selresult = resultlist[0]

View File

@@ -13,4 +13,80 @@
# You should have received a copy of the GNU General Public License
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
import headphones
from headphones import logger, notifiers
import urllib2
import lib.simplejson as json
import base64
# This is just a simple script to send torrents to transmission. The
# intention is to turn this into a class where we can check the state
# of the download, set the download dir, etc.
# TODO: Store the session id so we don't need to make 2 calls
# Store torrent id so we can check up on it
def sendTorrent(link):
host = headphones.TRANSMISSION_HOST
username = headphones.TRANSMISSION_USERNAME
password = headphones.TRANSMISSION_PASSWORD
sessionid = None
if not host.startswith('http'):
host = 'http://' + host
if host.endswith('/'):
host = host[:-1]
host = host + "/transmission/rpc"
request = urllib2.Request(host)
if username and password:
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
request.add_header("Authorization", "Basic %s" % base64string)
opener = urllib2.build_opener()
try:
data = opener.open(request).read()
except urllib2.HTTPError, e:
if e.code == 409:
sessionid = e.hdrs['x-transmission-session-id']
else:
logger.error('Could not connect to Transmission. Error: ' + str(e))
except Exception, e:
logger.error('Could not connect to Transmission. Error: ' + str(e))
if not sessionid:
logger.error("Error getting Session ID from Transmission")
return
request.add_header('x-transmission-session-id', sessionid)
postdata = json.dumps({ 'method': 'torrent-add',
'arguments': { 'filename': link,
'download-dir': headphones.DOWNLOAD_TORRENT_DIR } })
request.add_data(postdata)
try:
response = json.loads(opener.open(request).read())
except Exception, e:
logger.error("Error sending torrent to Transmission: " + str(e))
return
if response['result'] == 'success':
name = response['arguments']['torrent-added']['name']
logger.info(u"Torrent sent to Transmission successfully")
if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH:
logger.info(u"Sending Prowl notification")
prowl = notifiers.PROWL()
prowl.notify(name,"Download started")
if headphones.PUSHOVER_ENABLED and headphones.PUSHOVER_ONSNATCH:
logger.info(u"Sending Pushover notification")
prowl = notifiers.PUSHOVER()
prowl.notify(name,"Download started")
if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH:
logger.debug(u"Sending NMA notification")
nma = notifiers.NMA()
nma.notify(snatched_nzb=name)
return True

View File

@@ -13,4 +13,98 @@
# You should have received a copy of the GNU General Public License
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
## uTorrentAPI class taken from CouchPotatoServer
## http://github.com/RuudBurger/CouchPotatoServer
class uTorrentAPI(object):
def __init__(self, host = 'localhost', port = 8000, username = None, password = None):
super(uTorrentAPI, self).__init__()
self.url = 'http://' + str(host) + ':' + str(port) + '/gui/'
self.token = ''
self.last_time = time.time()
cookies = cookielib.CookieJar()
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), MultipartPostHandler)
self.opener.addheaders = [('User-agent', 'couchpotato-utorrent-client/1.0')]
if username and password:
password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(realm = None, uri = self.url, user = username, passwd = password)
self.opener.add_handler(urllib2.HTTPBasicAuthHandler(password_manager))
self.opener.add_handler(urllib2.HTTPDigestAuthHandler(password_manager))
elif username or password:
log.debug('User or password missing, not using authentication.')
self.token = self.get_token()
def _request(self, action, data = None):
if time.time() > self.last_time + 1800:
self.last_time = time.time()
self.token = self.get_token()
request = urllib2.Request(self.url + "?token=" + self.token + "&" + action, data)
try:
open_request = self.opener.open(request)
response = open_request.read()
if response:
return response
else:
log.debug('Unknown failure sending command to uTorrent. Return text is: %s', response)
except httplib.InvalidURL, err:
log.error('Invalid uTorrent host, check your config %s', err)
except urllib2.HTTPError, err:
if err.code == 401:
log.error('Invalid uTorrent Username or Password, check your config')
else:
log.error('uTorrent HTTPError: %s', err)
except urllib2.URLError, err:
log.error('Unable to connect to uTorrent %s', err)
return False
def get_token(self):
request = self.opener.open(self.url + "token.html")
token = re.findall("<div.*?>(.*?)</", request.read())[0]
return token
def add_torrent_uri(self, torrent):
action = "action=add-url&s=%s" % urllib.quote(torrent)
return self._request(action)
def add_torrent_file(self, filename, filedata):
action = "action=add-file"
return self._request(action, {"torrent_file": (ss(filename), filedata)})
def set_torrent(self, hash, params):
action = "action=setprops&hash=%s" % hash
for k, v in params.iteritems():
action += "&s=%s&v=%s" % (k, v)
return self._request(action)
def pause_torrent(self, hash):
action = "action=pause&hash=%s" % hash
return self._request(action)
def get_status(self):
action = "list=1"
return self._request(action)
def get_settings(self):
action = "action=getsettings"
settings_dict = {}
try:
utorrent_settings = json.loads(self._request(action))
# Create settings dict
for item in utorrent_settings['settings']:
if item[1] == 0: # int
settings_dict[item[0]] = int(item[2] if not item[2].strip() == '' else '0')
elif item[1] == 1: # bool
settings_dict[item[0]] = True if item[2] == 'true' else False
elif item[1] == 2: # string
settings_dict[item[0]] = item[2]
#log.debug('uTorrent settings: %s', settings_dict)
except Exception, err:
log.error('Failed to get settings from uTorrent: %s', err)
return settings_dict