Merge remote-tracking branch 'delphiactual/develop' into develop

Conflicts:
	headphones/utorrent.py
This commit is contained in:
rembo10
2014-05-06 11:05:54 -07:00
3 changed files with 156 additions and 76 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ SIGNAL = None
SYS_PLATFORM = None SYS_PLATFORM = None
SYS_ENCODING = None SYS_ENCODING = None
VERBOSE = 1 VERBOSE = 2
DAEMON = False DAEMON = False
CREATEPID = False CREATEPID = False
PIDFILE= None PIDFILE= None
+26 -5
View File
@@ -21,6 +21,8 @@ from lib.pygazelle import encoding as gazelleencoding
from lib.pygazelle import format as gazelleformat from lib.pygazelle import format as gazelleformat
from lib.pygazelle import media as gazellemedia from lib.pygazelle import media as gazellemedia
from xml.dom import minidom from xml.dom import minidom
from base64 import b16encode, b32decode
from hashlib import sha1
import os, re, time import os, re, time
import string import string
@@ -28,12 +30,13 @@ import shutil
import requests import requests
import subprocess import subprocess
import headphones import headphones
from headphones.common import USER_AGENT from headphones.common import USER_AGENT
from headphones import logger, db, helpers, classes, sab, nzbget, request from headphones import logger, db, helpers, classes, sab, nzbget, request
from headphones import utorrent, transmission, notifiers 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 import headphones.searcher_rutracker as rutrackersearch
rutracker = rutrackersearch.Rutracker() 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 #Open the fresh torrent file again so we can extract the proper torrent name
#Used later in post-processing. #Used later in post-processing.
with open(download_path, 'rb') as fp: 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', '') folder_name = torrent_info['info'].get('name', '')
logger.info('Torrent folder name: %s' % folder_name) logger.info('Torrent folder name: %s' % folder_name)
@@ -698,14 +701,16 @@ def send_to_downloader(data, bestqual, album):
else: else:
logger.info("Sending torrent to uTorrent") logger.info("Sending torrent to uTorrent")
# rutracker needs cookies to be set, pass the .torrent file instead of url # rutracker needs cookies to be set, pass the .torrent file instead of url
if bestqual[3] == 'rutracker.org': if bestqual[3] == 'rutracker.org':
file_or_url = rutracker.get_torrent(bestqual[2]) file_or_url = rutracker.get_torrent(bestqual[2])
else: else:
file_or_url = bestqual[2] 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: if folder_name:
logger.info('Torrent folder name: %s' % folder_name) logger.info('Torrent folder name: %s' % folder_name)
@@ -1401,7 +1406,7 @@ def preprocess(resultlist):
if result[4] == 'torrent': if result[4] == 'torrent':
#Get out of here if we're using Transmission or uTorrent #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 return True, result
# get outta here if rutracker # get outta here if rutracker
if result[3] == 'rutracker.org': if result[3] == 'rutracker.org':
@@ -1451,3 +1456,19 @@ def preprocess(resultlist):
continue continue
return (None, None) 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
+129 -70
View File
@@ -13,86 +13,145 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Headphones. If not, see <http://www.gnu.org/licenses/>. # along with Headphones. If not, see <http://www.gnu.org/licenses/>.
import urllib
import urllib2
import urlparse
import cookielib
import json
import re import re
import os import os
import time import time
import base64
import headphones import headphones
import simplejson as json from headphones import logger, notifiers
from headphones import logger, notifiers, request class utorrentclient(object):
TOKEN_REGEX = "<div id='token' style='display:none;'>([^<>]+)</div>"
# This is just a simple script to send torrents to transmission. The def __init__(self, base_url = None, username = None, password = None,):
# intention is to turn this into a class where we can check the state
# of the download, set the download dir, etc. host = headphones.UTORRENT_HOST
# TODO: Store the session id so we don't need to make 2 calls if not host.startswith('http'):
# Store torrent id so we can check up on it 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 label = headphones.UTORRENT_LABEL
token = ''
if not host.startswith('http'): uTorrentClient = utorrentclient()
host = 'http://' + host 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('/'): return False
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('<div.*?>(.*?)</', 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