diff --git a/TorrentToMedia.py b/TorrentToMedia.py index 3e990b56..6c759cfa 100755 --- a/TorrentToMedia.py +++ b/TorrentToMedia.py @@ -7,14 +7,18 @@ import os import shutil import logging import datetime +import time +import urllib2 from subprocess import call + # Custom imports import linktastic.linktastic as linktastic import autoProcessMovie import autoProcessTV from nzbToMediaEnv import * from nzbToMediaUtil import * +from utorrent.client import UTorrentClient nzbtomedia_configure_logging(os.path.dirname(sys.argv[0])) Logger = logging.getLogger(__name__) @@ -272,7 +276,7 @@ if len(sys.argv) == 2: #for other clients we assume we must at least get the dir inputName = '' # We dont have a name yet inputCategory = '' # We dont have a category yet -elif len(sys.argv) > 3 and sys.argv[1] == 'utorrent': #distinguish utorrent from others like deluge. +elif len(sys.argv) > 4 and sys.argv[1] == 'utorrent': #distinguish utorrent from others like deluge. # We will pass in 'utorrent' '%D', '%N', and '%L' (if it exists), from uTorrent # In short pass "/path/to/downloaded/torrent/ name" to TorrentToMedia.py, eg >>>> TorrentToMedia.py /Downloaded/MovieName.2013.BluRay.1080p.x264-10bit.DTS MovieName.2013.BluRay.1080p.x264-10bit.DTS <<<< inputDirectory = os.path.normpath(sys.argv[2]) @@ -281,6 +285,7 @@ elif len(sys.argv) > 3 and sys.argv[1] == 'utorrent': #distinguish utorrent from inputCategory = sys.argv[4] # We dont have a category yet except: inputCategory = '' # We dont have a category yet + inputHash = sys.argv[5] elif len(sys.argv) == 4: # We will assume this to be the passin from deluge. torrent id, torrent name, torrent save path. @@ -312,6 +317,9 @@ movieDestination = os.path.normpath(config.get("CouchPotato", "outputDirectory") # Torrent specific useLink = int(config.get("Torrent", "useLink")) extractionTool = os.path.normpath(config.get("Torrent", "extractionTool")) +uTorrentWEBui = config.get("Torrent", "uTorrentWEBui") +uTorrentUSR = config.get("Torrent", "uTorrentUSR") +uTorrentPWD = config.get("Torrent", "uTorrentPWD") compressedContainer = (config.get("Torrent", "compressedExtentions")).split(',') mediaContainer = (config.get("Torrent", "mediaExtentions")).split(',') metaContainer = (config.get("Torrent", "metaExtentions")).split(',') @@ -429,11 +437,26 @@ old_stdout = sys.stdout # Still crude, but we wat to capture this for now logFile = os.path.join(os.path.dirname(sys.argv[0]), "postprocess.log") log_file = open(logFile,"a+") sys.stdout = log_file -if inputCategory == movieCategory: + +# Hardlink solution with uTorrent +if inputHash and useLink: + Logger.debug("MAIN: We are using hardlinks with uTorrent, calling uTorrent to pause download") + utorrentClass = UTorrentClient(uTorrentWEBui, uTorrentUSR, uTorrentPWD) + utorrentClass.stop(inputHash) + time.sleep(10) + +if inputCategory == movieCategory: Logger.info("MAIN: Calling postprocessing script for CouchPotatoServer") autoProcessMovie.process(outputDestination, inputName, status) elif inputCategory == tvCategory: Logger.info("MAIN: Calling postprocessing script for Sick-Beard") autoProcessTV.processEpisode(outputDestination, inputName, status) + +# Hardlink solution with uTorrent +if inputHash and useLink: + Logger.debug("MAIN: We are using hardlinks with uTorrent, calling uTorrent to resume download") + utorrentClass.start(inputHash) + time.sleep(10) + sys.stdout = old_stdout log_file.close() diff --git a/autoProcessMedia.cfg.sample b/autoProcessMedia.cfg.sample index f20ff807..af847ca3 100644 --- a/autoProcessMedia.cfg.sample +++ b/autoProcessMedia.cfg.sample @@ -31,6 +31,10 @@ failed_fork=0 useLink = 0 extractionTool = C:\Program Files\7-Zip\7z.exe categories = music,music_videos,pictures,software +###### uTorrent Hardlink solution (You must edit this if your using TorrentToMedia.py with uTorrent) +uTorrentWEBui = http://localhost:8090/gui/ +uTorrentUSR = your username +uTorrentPWD = your password ###### ADVANCED USE - ONLY EDIT IF YOU KNOW WHAT YOU'RE DOING ###### compressedExtentions = .zip,.rar,.7z,.gz,.bz,.tar,.arj mediaExtentions = .mkv,.avi,.divx,.xvid,.mov,.wmv,.mp4,.mpg,.mpeg,.vob,.iso diff --git a/utorrent/__init__.py b/utorrent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/utorrent/__init__.pyc b/utorrent/__init__.pyc new file mode 100644 index 00000000..d4de8d25 Binary files /dev/null and b/utorrent/__init__.pyc differ diff --git a/utorrent/client.py b/utorrent/client.py new file mode 100644 index 00000000..baffbf6e --- /dev/null +++ b/utorrent/client.py @@ -0,0 +1,124 @@ +#coding=utf8 +import urllib +import urllib2 +import urlparse +import cookielib +import re +import StringIO +try: + import json +except ImportError: + import simplejson as json + +from upload import MultiPartForm + +class UTorrentClient(object): + + def __init__(self, base_url, username, password): + self.base_url = base_url + self.username = username + self.password = password + self.opener = self._make_opener('uTorrent', base_url, username, 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_handler = urllib2.HTTPBasicAuthHandler() + auth_handler.add_password(realm=realm, + uri=base_url, + user=username, + passwd=password) + opener = urllib2.build_opener(auth_handler) + urllib2.install_opener(opener) + + cookie_jar = cookielib.CookieJar() + cookie_handler = urllib2.HTTPCookieProcessor(cookie_jar) + + handlers = [auth_handler, cookie_handler] + opener = urllib2.build_opener(*handlers) + return opener + + def _get_token(self): + url = urlparse.urljoin(self.base_url, 'token.html') + response = self.opener.open(url) + token_re = "" + match = re.search(token_re, response.read()) + return match.group(1) + + + def list(self, **kwargs): + params = [('list', '1')] + params += kwargs.items() + 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 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 addfile(self, filename, filepath=None, bytes=None): + params = [('action', 'add-file')] + + form = MultiPartForm() + if filepath is not None: + file_handler = open(filepath) + else: + file_handler = StringIO.StringIO(bytes) + + form.add_file('torrent_file', filename.encode('utf-8'), file_handler) + + return self._action(params, str(form), form.get_content_type()) + + def _action(self, params, body=None, content_type=None): + #about token, see https://github.com/bittorrent/webui/wiki/TokenSystem + url = self.base_url + '?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,e: + raise + diff --git a/utorrent/client.pyc b/utorrent/client.pyc new file mode 100644 index 00000000..840aafc0 Binary files /dev/null and b/utorrent/client.pyc differ diff --git a/utorrent/upload.py b/utorrent/upload.py new file mode 100644 index 00000000..e7c06f40 --- /dev/null +++ b/utorrent/upload.py @@ -0,0 +1,71 @@ +#code copied from http://www.doughellmann.com/PyMOTW/urllib2/ + +import itertools +import mimetools +import mimetypes +from cStringIO import StringIO +import urllib +import urllib2 + +class MultiPartForm(object): + """Accumulate the data to be used when posting a form.""" + + def __init__(self): + self.form_fields = [] + self.files = [] + self.boundary = mimetools.choose_boundary() + return + + def get_content_type(self): + return 'multipart/form-data; boundary=%s' % self.boundary + + def add_field(self, name, value): + """Add a simple field to the form data.""" + self.form_fields.append((name, value)) + return + + def add_file(self, fieldname, filename, fileHandle, mimetype=None): + """Add a file to be uploaded.""" + body = fileHandle.read() + if mimetype is None: + mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + self.files.append((fieldname, filename, mimetype, body)) + return + + def __str__(self): + """Return a string representing the form data, including attached files.""" + # Build a list of lists, each containing "lines" of the + # request. Each part is separated by a boundary string. + # Once the list is built, return a string where each + # line is separated by '\r\n'. + parts = [] + part_boundary = '--' + self.boundary + + # Add the form fields + parts.extend( + [ part_boundary, + 'Content-Disposition: form-data; name="%s"' % name, + '', + value, + ] + for name, value in self.form_fields + ) + + # Add the files to upload + parts.extend( + [ part_boundary, + 'Content-Disposition: file; name="%s"; filename="%s"' % \ + (field_name, filename), + 'Content-Type: %s' % content_type, + '', + body, + ] + for field_name, filename, content_type, body in self.files + ) + + # Flatten the list and add closing boundary marker, + # then return CR+LF separated data + flattened = list(itertools.chain(*parts)) + flattened.append('--' + self.boundary + '--') + flattened.append('') + return '\r\n'.join(flattened) diff --git a/utorrent/upload.pyc b/utorrent/upload.pyc new file mode 100644 index 00000000..5b259013 Binary files /dev/null and b/utorrent/upload.pyc differ