diff --git a/headphones/__init__.py b/headphones/__init__.py
index 380a31bf..40e085e1 100644
--- a/headphones/__init__.py
+++ b/headphones/__init__.py
@@ -38,7 +38,7 @@ SIGNAL = None
SYS_PLATFORM = None
SYS_ENCODING = None
-VERBOSE = 1
+VERBOSE = 2
DAEMON = False
CREATEPID = False
PIDFILE= None
diff --git a/headphones/searcher.py b/headphones/searcher.py
index 20b5f16b..1cc78b84 100644
--- a/headphones/searcher.py
+++ b/headphones/searcher.py
@@ -21,6 +21,8 @@ from lib.pygazelle import encoding as gazelleencoding
from lib.pygazelle import format as gazelleformat
from lib.pygazelle import media as gazellemedia
from xml.dom import minidom
+from base64 import b16encode, b32decode
+from hashlib import sha1
import os, re, time
import string
@@ -28,12 +30,13 @@ import shutil
import requests
import subprocess
+
import headphones
from headphones.common import USER_AGENT
from headphones import logger, db, helpers, classes, sab, nzbget, request
from headphones import utorrent, transmission, notifiers
-import lib.bencode as bencode
+from lib.bencode import bencode as bencode, bdecode
import headphones.searcher_rutracker as rutrackersearch
rutracker = rutrackersearch.Rutracker()
@@ -659,7 +662,7 @@ def send_to_downloader(data, bestqual, album):
#Open the fresh torrent file again so we can extract the proper torrent name
#Used later in post-processing.
with open(download_path, 'rb') as fp:
- torrent_info = bencode.bdecode(fp.read())
+ torrent_info = bdecode(fp.read())
folder_name = torrent_info['info'].get('name', '')
logger.info('Torrent folder name: %s' % folder_name)
@@ -698,14 +701,16 @@ def send_to_downloader(data, bestqual, album):
else:
logger.info("Sending torrent to uTorrent")
-
+
# rutracker needs cookies to be set, pass the .torrent file instead of url
if bestqual[3] == 'rutracker.org':
file_or_url = rutracker.get_torrent(bestqual[2])
else:
file_or_url = bestqual[2]
- folder_name = utorrent.addTorrent(file_or_url)
+ _hash = CalculateTorrentHash(file_or_url, data)
+
+ folder_name = utorrent.addTorrent(file_or_url, _hash)
if folder_name:
logger.info('Torrent folder name: %s' % folder_name)
@@ -1401,7 +1406,7 @@ def preprocess(resultlist):
if result[4] == 'torrent':
#Get out of here if we're using Transmission or uTorrent
- if headphones.TORRENT_DOWNLOADER != 0:
+ if headphones.TORRENT_DOWNLOADER == 1: ## if not a magnet link still need the .torrent to generate hash... uTorrent support labeling
return True, result
# get outta here if rutracker
if result[3] == 'rutracker.org':
@@ -1451,3 +1456,19 @@ def preprocess(resultlist):
continue
return (None, None)
+
+
+
+def CalculateTorrentHash(link, data):
+
+ if link.startswith('magnet'):
+ tor_hash = re.findall('urn:btih:([\w]{32,40})', link)[0]
+ if len(tor_hash) == 32:
+ tor_hash = b16encode(b32decode(tor_hash)).lower()
+ else:
+ info = bdecode(data)["info"]
+ tor_hash = sha1(bencode(info)).hexdigest()
+
+ logger.info('Torrent Hash: ' + str(tor_hash))
+
+ return tor_hash
\ No newline at end of file
diff --git a/headphones/utorrent.py b/headphones/utorrent.py
index fbf1a462..e60d95c5 100644
--- a/headphones/utorrent.py
+++ b/headphones/utorrent.py
@@ -13,86 +13,145 @@
# You should have received a copy of the GNU General Public License
# along with Headphones. If not, see .
+import urllib
+import urllib2
+import urlparse
+import cookielib
+import json
import re
import os
import time
-import base64
import headphones
-import simplejson as json
+from headphones import logger, notifiers
-from headphones import logger, notifiers, request
+class utorrentclient(object):
+ TOKEN_REGEX = "
([^<>]+)
"
-# 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 __init__(self, base_url = None, username = None, password = None,):
+
+ host = headphones.UTORRENT_HOST
+ if not host.startswith('http'):
+ host = 'http://' + host
+ if host.endswith('/'):
+ host = host[:-1]
-def addTorrent(link):
+ if host.endswith('/gui'):
+ host = host[:-4]
+
+ self.base_url = host
+ self.username = headphones.UTORRENT_USERNAME
+ self.password = headphones.UTORRENT_PASSWORD
+ self.opener = self._make_opener('uTorrent', self.base_url, self.username, self.password)
+ self.token = self._get_token()
+ #TODO refresh token, when necessary
+
+ def _make_opener(self, realm, base_url, username, password):
+ """uTorrent API need HTTP Basic Auth and cookie support for token verify."""
+ auth = urllib2.HTTPBasicAuthHandler()
+ auth.add_password(realm=realm,uri=base_url,user=username,passwd=password)
+ opener = urllib2.build_opener(auth)
+ urllib2.install_opener(opener)
+
+ cookie_jar = cookielib.CookieJar()
+ cookie_handler = urllib2.HTTPCookieProcessor(cookie_jar)
+
+ handlers = [auth, cookie_handler]
+ opener = urllib2.build_opener(*handlers)
+ return opener
+
+ def _get_token(self):
+ url = urlparse.urljoin(self.base_url, 'gui/token.html')
+ try:
+ response = self.opener.open(url)
+ except urllib2.HTTPError as err:
+ logger.debug('URL: ' + str(url))
+ logger.debug('Error getting Token. uTorrent responded with error: ' + str(err))
+ match = re.search(utorrentclient.TOKEN_REGEX, response.read())
+ return match.group(1)
+
+ def list(self, **kwargs):
+ params = [('list', '1')]
+ params += kwargs.items()
+ return self._action(params)
+
+ def add_url(self, url):
+ #can recieve magnet or normal .torrent link
+ params = [('action', 'add-url'), ('s', url)]
+ return self._action(params)
+
+ def start(self, *hashes):
+ params = [('action', 'start'), ]
+ for hash in hashes:
+ params.append(('hash', hash))
+ return self._action(params)
+
+ def stop(self, *hashes):
+ params = [('action', 'stop'), ]
+ for hash in hashes:
+ params.append(('hash', hash))
+ return self._action(params)
+
+ def pause(self, *hashes):
+ params = [('action', 'pause'), ]
+ for hash in hashes:
+ params.append(('hash', hash))
+ return self._action(params)
+
+ def forcestart(self, *hashes):
+ params = [('action', 'forcestart'), ]
+ for hash in hashes:
+ params.append(('hash', hash))
+ return self._action(params)
+
+ def getfiles(self, hash):
+ params = [('action', 'getfiles'), ('hash', hash)]
+ return self._action(params)
+
+ def getprops(self, hash):
+ params = [('action', 'getprops'), ('hash', hash)]
+ return self._action(params)
+
+ def setprops(self, hash, s, v):
+ params = [('action', 'setprops'), ('hash', hash), ("s", s), ("v", v)]
+ return self._action(params)
+
+ def setprio(self, hash, priority, *files):
+ params = [('action', 'setprio'), ('hash', hash), ('p', str(priority))]
+ for file_index in files:
+ params.append(('f', str(file_index)))
+
+ return self._action(params)
+
+ def _action(self, params, body=None, content_type=None):
+ url = self.base_url + '/gui/' + '?token=' + self.token + '&' + urllib.urlencode(params)
+ request = urllib2.Request(url)
+
+ if body:
+ request.add_data(body)
+ request.add_header('Content-length', len(body))
+ if content_type:
+ request.add_header('Content-type', content_type)
+
+ try:
+ response = self.opener.open(request)
+ return response.code, json.loads(response.read())
+ except urllib2.HTTPError as err:
+ logger.debug('URL: ' + str(url))
+ logger.debug('uTorrent webUI raised the following error: ' + str(err))
+
+def addTorrent(link, hash):
- host = headphones.UTORRENT_HOST
- username = headphones.UTORRENT_USERNAME
- password = headphones.UTORRENT_PASSWORD
label = headphones.UTORRENT_LABEL
- token = ''
- if not host.startswith('http'):
- host = 'http://' + host
+ uTorrentClient = utorrentclient()
+ uTorrentClient.add_url(link)
+ time.sleep(1) #need to ensure file is loaded uTorrent...
+ uTorrentClient.setprops(hash,'label', label)
+ torrentList = uTorrentClient.list()
+ for torrent in torrentList[1].get('torrents'):
+ if (torrent[0].lower()==hash):
+ return torrent[26]
- if host.endswith('/'):
- host = host[:-1]
-
- if host.endswith('/gui'):
- host = host + '/'
- else:
- host = host + '/gui/'
-
- # Retrieve session id
- auth = (username, password) if username and password else None
- token_request = request.request_response(host + 'token.html', auth=auth)
-
- token = re.findall('(.*?)', token_request.content)[0]
- guid = token_request.cookies['GUID']
-
- cookies = dict(GUID = guid)
-
- if link.startswith("magnet") or link.startswith("http") or link.endswith(".torrent"):
- params = {'action':'add-url', 's':link, 'token':token}
- response = request.request_json(host, params=params, auth=auth, cookies=cookies)
- else:
- params = {'action':'add-file', 'token':token}
- files = {'torrent_file':{'music.torrent' : link}}
- response = request.request_json(host, method="post", params=params, files=files, auth=auth, cookies=cookies)
- if not response:
- logger.error("Error sending torrent to uTorrent")
- return
-
- if link.startswith('magnet'):
- tor_hash = re.findall('urn:btih:([\w]{32,40})', link)[0]
- if len(tor_hash) == 32:
- tor_hash = b16encode(b32decode(tor_hash)).lower()
- else:
- info = bdecode(link.content)["info"]
- tor_hash = sha1(bencode(info)).hexdigest()
-
- params = {'action':'setprops', 'hash':tor_hash,'s':'label', 'v':label, 'token':token}
- response = request.request_json(host, params=params, auth=auth, cookies=cookies)
- if not response:
- logger.error("Error setting torrent label in uTorrent")
- return
-
- # folder info can probably be cleaned up with getprops
- folder = None
-
- params = {'list':'1', 'token':token}
- response = request.request_json(host, params=params, auth=auth, cookies=cookies)
- if not response:
- logger.error("Error getting torrent information from uTorrent")
- return
-
- for torrent in response['torrents']:
- folder = os.path.basename(torrent[26])
-
- return folder
\ No newline at end of file
+ return False