From 7bbb137fd90aa83685db20c757a7f3dda8627223 Mon Sep 17 00:00:00 2001 From: delphiactual Date: Tue, 6 May 2014 02:12:17 -0600 Subject: [PATCH] uTorrent changes... --- headphones/__init__.py | 2 +- headphones/searcher.py | 31 +++++-- headphones/utorrent.py | 200 ++++++++++++++++++++++++++--------------- 3 files changed, 156 insertions(+), 77 deletions(-) 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 7aad16f5..3ce07906 100644 --- a/headphones/utorrent.py +++ b/headphones/utorrent.py @@ -13,86 +13,144 @@ # 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 = '' + 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 not host.startswith('http'): - host = 'http://' + host - - 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('(.*?)