diff --git a/headphones/searcher.py b/headphones/searcher.py
index 6efbc0be..07a2190f 100644
--- a/headphones/searcher.py
+++ b/headphones/searcher.py
@@ -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:
%s - %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]
diff --git a/headphones/transmission.py b/headphones/transmission.py
index e3b93d4e..21d7e584 100644
--- a/headphones/transmission.py
+++ b/headphones/transmission.py
@@ -13,4 +13,80 @@
# You should have received a copy of the GNU General Public License
# along with Headphones. If not, see
.
+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
diff --git a/headphones/utorrent.py b/headphones/utorrent.py
index e3b93d4e..e464495b 100644
--- a/headphones/utorrent.py
+++ b/headphones/utorrent.py
@@ -13,4 +13,98 @@
# You should have received a copy of the GNU General Public License
# along with Headphones. If not, see
.
+## 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("
(.*?)", 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