From 7e915c59024e1ec662dc6a4347450a0d7c397481 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 14 Jan 2017 00:49:55 -0500 Subject: [PATCH 1/3] Add qbittorrent downloader support --- data/interfaces/default/config.html | 47 ++++- headphones/config.py | 4 + headphones/postprocessor.py | 8 +- headphones/qbittorrent.py | 277 ++++++++++++++++++++++++++++ headphones/searcher.py | 26 ++- headphones/webserve.py | 5 + 6 files changed, 353 insertions(+), 14 deletions(-) create mode 100644 headphones/qbittorrent.py diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 5dc6ab58..cd0bb379 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -312,6 +312,7 @@ Transmission uTorrent (Beta) Deluge (Beta) + QBitTorrent
@@ -386,6 +387,26 @@
+
+ Note: Works with WebAPI Rev 6 and later (QBitTorrent 3.4.0 and later) +
+ + + usually http://localhost:8081 +
+
+ + +
+
+ + +
+
+ + +
+
@@ -2275,26 +2296,30 @@ if ($("#torrent_downloader_blackhole").is(":checked")) { - $("#transmission_options,#utorrent_options,#deluge_options").hide(); + $("#transmission_options,#utorrent_options,#deluge_options,#qbittorrent_options").hide(); $("#torrent_blackhole_options").show(); } if ($("#torrent_downloader_transmission").is(":checked")) { - $("#torrent_blackhole_options,#utorrent_options,#deluge_options").hide(); + $("#torrent_blackhole_options,#utorrent_options,#deluge_options,#qbittorrent_options").hide(); $("#transmission_options").show(); } if ($("#torrent_downloader_utorrent").is(":checked")) { - $("#torrent_blackhole_options,#transmission_options,#deluge_options").hide(); + $("#torrent_blackhole_options,#transmission_options,#deluge_options,#qbittorrent_options").hide(); $("#utorrent_options").show(); } + if ($("#torrent_downloader_qbittorrent").is(":checked")) + { + $("#torrent_blackhole_options,#transmission_options,#utorrent_options,#deluge_options").hide(); + $("#qbittorrent_options").show(); + } if ($("#torrent_downloader_deluge").is(":checked")) { - $("#torrent_blackhole_options,#transmission_options,#utorrent_options").hide(); + $("#torrent_blackhole_options,#transmission_options,#utorrent_options,#qbittorent_options").hide(); $("#deluge_options").show(); } - $('input[type=radio]').change(function(){ if ($("#preferred_bitrate").is(":checked")) { @@ -2330,19 +2355,23 @@ } if ($("#torrent_downloader_blackhole").is(":checked")) { - $("#transmission_options,#utorrent_options,#deluge_options").fadeOut("fast", function() { $("#torrent_blackhole_options").fadeIn() }); + $("#transmission_options,#utorrent_options,#deluge_options,#qbittorrent_options").fadeOut("fast", function() { $("#torrent_blackhole_options").fadeIn() }); } if ($("#torrent_downloader_transmission").is(":checked")) { - $("#torrent_blackhole_options,#utorrent_options,#deluge_options").fadeOut("fast", function() { $("#transmission_options").fadeIn() }); + $("#torrent_blackhole_options,#utorrent_options,#deluge_options,#qbittorrent_options").fadeOut("fast", function() { $("#transmission_options").fadeIn() }); } if ($("#torrent_downloader_utorrent").is(":checked")) { - $("#torrent_blackhole_options,#transmission_options,#deluge_options").fadeOut("fast", function() { $("#utorrent_options").fadeIn() }); + $("#torrent_blackhole_options,#transmission_options,#deluge_options,#qbittorrent_options").fadeOut("fast", function() { $("#utorrent_options").fadeIn() }); } + if ($("#torrent_downloader_qbittorrent").is(":checked")) + { + $("#torrent_blackhole_options,#transmission_options,#utorrent_options,#deluge_options").fadeOut("fast", function() { $("#qbittorrent_options").fadeIn() }); + } if ($("#torrent_downloader_deluge").is(":checked")) { - $("#torrent_blackhole_options,#utorrent_options,#transmission_options").fadeOut("fast", function() { $("#deluge_options").fadeIn() }); + $("#torrent_blackhole_options,#utorrent_options,#transmission_options,#qbittorrent_options").fadeOut("fast", function() { $("#deluge_options").fadeIn() }); } }); diff --git a/headphones/config.py b/headphones/config.py index 41f50ca9..4b37c2a1 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -232,6 +232,10 @@ _CONFIG_DEFINITIONS = { 'PUSHOVER_KEYS': (str, 'Pushover', ''), 'PUSHOVER_ONSNATCH': (int, 'Pushover', 0), 'PUSHOVER_PRIORITY': (int, 'Pushover', 0), + 'QBITTORRENT_HOST': (str, 'QBitTorrent', ''), + 'QBITTORRENT_LABEL': (str, 'QBitTorrent', ''), + 'QBITTORRENT_PASSWORD': (str, 'QBitTorrent', ''), + 'QBITTORRENT_USERNAME': (str, 'QBitTorrent', ''), 'RENAME_FILES': (int, 'General', 0), 'RENAME_UNPROCESSED': (bool_int, 'General', 1), 'RENAME_FROZEN': (bool_int, 'General', 1), diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 2ddd247c..84116abc 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -26,7 +26,7 @@ from beets import autotag from beets import config as beetsconfig from beets.mediafile import MediaFile, FileTypeError, UnreadableFileError from beetsplug import lyrics as beetslyrics -from headphones import notifiers, utorrent, transmission, deluge +from headphones import notifiers, utorrent, transmission, deluge, qbittorrent from headphones import db, albumart, librarysync from headphones import logger, helpers, request, mb, music_encoder from headphones import metadata @@ -452,7 +452,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, [albumid]) # Check if torrent has finished seeding - if headphones.CONFIG.TORRENT_DOWNLOADER == 1 or headphones.CONFIG.TORRENT_DOWNLOADER == 2: + if headphones.CONFIG.TORRENT_DOWNLOADER != 0: seed_snatched = myDB.action( 'SELECT * from snatched WHERE Status="Seed_Snatched" and AlbumID=?', [albumid]).fetchone() @@ -465,8 +465,10 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, torrent_removed = transmission.removeTorrent(hash, True) elif headphones.CONFIG.TORRENT_DOWNLOADER == 3: # Deluge torrent_removed = deluge.removeTorrent(hash, True) - else: + elif headphones.CONFIG.TORRENT_DOWNLOADER == 2: torrent_removed = utorrent.removeTorrent(hash, True) + else: + torrent_removed = qbittorrent.removeTorrent(hash, True) # Torrent removed, delete the snatched record, else update Status for scheduled job to check if torrent_removed: diff --git a/headphones/qbittorrent.py b/headphones/qbittorrent.py new file mode 100644 index 00000000..8ffebdbf --- /dev/null +++ b/headphones/qbittorrent.py @@ -0,0 +1,277 @@ +# This file is part of Headphones. +# +# Headphones is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Headphones is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Headphones. If not, see . + +import urllib +import urllib2 +import cookielib +import json +import os +import time +import mimetypes +import random +import string + +import headphones + +from headphones import logger +from collections import namedtuple + + +class qbittorrentclient(object): + + TOKEN_REGEX = "" + UTSetting = namedtuple("UTSetting", ["name", "int", "str", "access"]) + + def __init__(self, base_url=None, username=None, password=None,): + + host = headphones.CONFIG.QBITTORRENT_HOST + if not host.startswith('http'): + host = 'http://' + host + + if host.endswith('/'): + host = host[:-1] + + if host.endswith('/gui'): + host = host[:-4] + + self.base_url = host + self.username = headphones.CONFIG.QBITTORRENT_USERNAME + self.password = headphones.CONFIG.QBITTORRENT_PASSWORD + self.cookiejar = cookielib.CookieJar() + self.opener = self._make_opener() + self._get_sid(self.base_url, self.username, self.password) + + def _make_opener(self): + # create opener with cookie handler to carry QBitTorrent SID cookie + cookie_handler = urllib2.HTTPCookieProcessor(self.cookiejar) + handlers = [cookie_handler] + return urllib2.build_opener(*handlers) + + def _get_sid(self, base_url, username, password): + # login so we can capture SID cookie + login_data = urllib.urlencode({'username': username, 'password': password}) + try: + self.opener.open(base_url+'/login', login_data) + except urllib2.URLError as err: + logger.debug('Error getting SID. qBittorrent responded with error: ' + str(err.reason)) + return + for cookie in self.cookiejar: + logger.debug('login cookie: ' + cookie.name + ', value: ' + cookie.value) + return + + def _command(self, command, args=None, content_type=None, files=None): + logger.debug('QBittorrent WebAPI Command: %s' % command) + + url = self.base_url + '/' + command + + data = None + headers = dict() + if content_type == 'multipart/form-data': + data, headers = encode_multipart( args, files ) + else: + if args: + data = urllib.urlencode(args) + if content_type: + headers['Content-Type'] = content_type + + logger.debug('%s' % json.dumps( headers, indent = 4 )) + logger.debug('%s' % data) + + request = urllib2.Request(url, data, headers) + try: + response = self.opener.open(request) + info = response.info() + if info: + if info.getheader('content-type'): + if info.getheader('content-type') == 'application/json': + resp = '' + for line in response: + resp = resp + line + logger.debug('response code: %s' % str(response.code) ) + logger.debug('response: %s' % resp) + return response.code, json.loads(resp) + logger.debug('response code: %s' % str(response.code) ) + return response.code, None + except urllib2.URLError as err: + logger.debug('Failed URL: %s' % url) + logger.debug('QBitTorrent webUI raised the following error: %s' % str(err)) + return None, None + + def _get_list(self, **args): + return self._command('query/torrents', args) + + def _get_settings(self): + status, value = self._command('query/preferences') + logger.debug('get_settings() returned %d items' % len(value)) + return value + + def get_savepath(self, hash): + logger.debug('qb.get_savepath(%s)' % hash) + status, torrentList = self._get_list() + for torrent in torrentList: + if torrent['hash']: + if torrent['hash'].upper() == hash.upper(): + return torrent['save_path'] + return None + + def start(self, hash): + logger.debug('qb.start(%s)' % hash) + args = {'hash': hash} + return self._command('command/resume', args, 'application/x-www-form-urlencoded') + + def pause(self, hash): + logger.debug('qb.pause(%s)' % hash) + args = {'hash': hash} + return self._command('command/pause', args,'application/x-www-form-urlencoded') + + def getfiles(self, hash): + logger.debug('qb.getfiles(%s)' % hash) + return self._command('query/propertiesFiles/'+hash) + + def getprops(self, hash): + logger.debug('qb.getprops(%s)' % hash) + return self._command('query/propertiesGeneral/'+hash) + + def setprio(self, hash, priority): + logger.debug('qb.setprio(%s,%d)' % (hash, priority)) + args = {'hash': hash, 'priority': priority} + return self._command('command/setFilePrio', args,'application/x-www-form-urlencoded') + + def remove(self, hash, remove_data=False): + logger.debug('qb.remove(%s,%s)' % (hash, remove_data)) + + args = {'hashes': hash} + if remove_data: + command = 'command/deletePerm' + else: + command = 'command/delete' + return self._command(command, args, 'application/x-www-form-urlencoded') + +def removeTorrent(hash, remove_data=False): + logger.debug('removeTorrent(%s,%s)' % (hash, remove_data)) + + qbclient = qbittorrentclient() + status, torrentList = qbclient._get_list() + for torrent in torrentList: + if torrent['hash'].upper() == hash.upper(): + if torrent['state'] == 'uploading' or torrent['state'] == 'stalledUP': + logger.info('%s has finished seeding, removing torrent and data' % torrent['name']) + qbclient.remove(hash, remove_data) + return True + else: + logger.info('%s has not finished seeding yet, torrent will not be removed, will try again on next run' % torrent['name']) + return False + return False + +def addTorrent(link): + logger.debug('addTorrent(%s)' % link) + + qbclient = qbittorrentclient() + args = {'urls': link, 'savepath': headphones.CONFIG.DOWNLOAD_TORRENT_DIR} + if headphones.CONFIG.QBITTORRENT_LABEL: + args['category'] = headphones.CONFIG.QBITTORRENT_LABEL + return qbclient._command('command/download', args, 'multipart/form-data' ) + +def addFile(data): + logger.debug('addFile(data)') + + qbclient = qbittorrentclient() + files = {'torrents': {'filename': '', 'content': data}} + return qbclient._command('command/upload', filelist=files) + +def getFolder(hash): + logger.debug('getFolder(%s)' % hash) + + qbclient = qbittorrentclient() + + # Get Active Directory from settings + settings = qbclient._get_settings() + active_dir = settings['temp_path'] + + if not active_dir: + logger.error('Could not get "Keep incomplete torrents in:" directory from QBitTorrent settings, please ensure it is set') + return None + + # Get Torrent Folder Name + torrent_folder = qbclient.get_savepath(hash) + + # If there's no folder yet then it's probably a magnet, try until folder is populated + if torrent_folder == active_dir or not torrent_folder: + tries = 1 + while (torrent_folder == active_dir or torrent_folder is None) and tries <= 10: + tries += 1 + time.sleep(6) + torrent_folder = qbclient.get_savepath(hash) + + if torrent_folder == active_dir or not torrent_folder: + torrent_folder = qbclient.get_savepath(hash) + return torrent_folder + else: + if headphones.SYS_PLATFORM != "win32": + torrent_folder = torrent_folder.replace('\\', '/') + return os.path.basename(os.path.normpath(torrent_folder)) + +_BOUNDARY_CHARS = string.digits + string.ascii_letters + +# Taken from http://code.activestate.com/recipes/578668-encode-multipart-form-data-for-uploading-files-via/ +# "MIT License" which is compatible with GPL +def encode_multipart(args, files, boundary=None): + logger.debug('encode_multipart()') + + def escape_quote(s): + return s.replace('"', '\\"') + + if boundary is None: + boundary = ''.join(random.choice(_BOUNDARY_CHARS) for i in range(30)) + lines = [] + + if args: + for name, value in args.items(): + lines.extend(( + '--{0}'.format(boundary), + 'Content-Disposition: form-data; name="{0}"'.format(escape_quote(name)), + '', + str(value), + )) + logger.debug(''.join( lines )) + + if files: + for name, value in files.items(): + filename = value['filename'] + if 'mimetype' in value: + mimetype = value['mimetype'] + else: + mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + lines.extend(( + '--{0}'.format(boundary), + 'Content-Disposition: form-data; name="{0}"; filename="{1}"'.format( + escape_quote(name), escape_quote(filename)), + 'Content-Type: {0}'.format(mimetype), + '', + value['content'], + )) + + lines.extend(( + '--{0}--'.format(boundary), + '', + )) + body = '\r\n'.join(lines) + + headers = { + 'Content-Type': 'multipart/form-data; boundary={0}'.format(boundary), + 'Content-Length': str(len(body)), + } + + return (body, headers) diff --git a/headphones/searcher.py b/headphones/searcher.py index 4b52216b..32e44a85 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -35,7 +35,7 @@ from pygazelle import format as gazelleformat import headphones from headphones.common import USER_AGENT from headphones import logger, db, helpers, classes, sab, nzbget, request -from headphones import utorrent, transmission, notifiers, rutracker, deluge +from headphones import utorrent, transmission, notifiers, rutracker, deluge, qbittorrent from bencode import bencode, bdecode # Magnet to torrent services, for Black hole. Stolen from CouchPotato. @@ -981,7 +981,7 @@ def send_to_downloader(data, bestqual, album): except Exception as e: logger.error('Error sending torrent to Deluge: %s' % str(e)) - else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 2: + elif headphones.CONFIG.TORRENT_DOWNLOADER == 2: logger.info("Sending torrent to uTorrent") # Add torrent @@ -1012,6 +1012,28 @@ def send_to_downloader(data, bestqual, album): seed_ratio = get_seed_ratio(bestqual[3]) if seed_ratio is not None: utorrent.setSeedRatio(torrentid, seed_ratio) + else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 4: + logger.info("Sending torrent to QBiTorrent") + + # Add torrent + if bestqual[3] == 'rutracker.org': + qbittorrent.addFile(data) + else: + qbittorrent.addTorrent(bestqual[2]) + + # Get hash + torrentid = calculate_torrent_hash(bestqual[2], data) + if not torrentid: + logger.error('Torrent id could not be determined') + return + + # Get folder + folder_name = qbittorrent.getFolder(torrentid) + if folder_name: + logger.info('Torrent folder name: %s' % folder_name) + else: + logger.error('Torrent folder name could not be determined') + return myDB = db.DBConnection() myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [album['AlbumID']]) diff --git a/headphones/webserve.py b/headphones/webserve.py index 278e512b..d10d6a3d 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1157,6 +1157,10 @@ class WebInterface(object): "nzbget_password": headphones.CONFIG.NZBGET_PASSWORD, "nzbget_category": headphones.CONFIG.NZBGET_CATEGORY, "nzbget_priority": headphones.CONFIG.NZBGET_PRIORITY, + "qbittorrent_host": headphones.CONFIG.QBITTORRENT_HOST, + "qbittorrent_username": headphones.CONFIG.QBITTORRENT_USERNAME, + "qbittorrent_password": headphones.CONFIG.QBITTORRENT_PASSWORD, + "qbittorrent_label": headphones.CONFIG.QBITTORRENT_LABEL, "transmission_host": headphones.CONFIG.TRANSMISSION_HOST, "transmission_username": headphones.CONFIG.TRANSMISSION_USERNAME, "transmission_password": headphones.CONFIG.TRANSMISSION_PASSWORD, @@ -1177,6 +1181,7 @@ class WebInterface(object): "torrent_downloader_transmission": radio(headphones.CONFIG.TORRENT_DOWNLOADER, 1), "torrent_downloader_utorrent": radio(headphones.CONFIG.TORRENT_DOWNLOADER, 2), "torrent_downloader_deluge": radio(headphones.CONFIG.TORRENT_DOWNLOADER, 3), + "torrent_downloader_qbittorrent": radio(headphones.CONFIG.TORRENT_DOWNLOADER, 4), "download_dir": headphones.CONFIG.DOWNLOAD_DIR, "use_blackhole": checked(headphones.CONFIG.BLACKHOLE), "blackhole_dir": headphones.CONFIG.BLACKHOLE_DIR, From fa1cb27e04fb66751528d90111b3c5c5f4b6c7e8 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 14 Jan 2017 01:13:04 -0500 Subject: [PATCH 2/3] fix pep8 errors --- headphones/qbittorrent.py | 31 ++++++++++++++++++++----------- headphones/searcher.py | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/headphones/qbittorrent.py b/headphones/qbittorrent.py index 8ffebdbf..fe82fa30 100644 --- a/headphones/qbittorrent.py +++ b/headphones/qbittorrent.py @@ -63,7 +63,7 @@ class qbittorrentclient(object): # login so we can capture SID cookie login_data = urllib.urlencode({'username': username, 'password': password}) try: - self.opener.open(base_url+'/login', login_data) + self.opener.open(base_url + '/login', login_data) except urllib2.URLError as err: logger.debug('Error getting SID. qBittorrent responded with error: ' + str(err.reason)) return @@ -79,15 +79,15 @@ class qbittorrentclient(object): data = None headers = dict() if content_type == 'multipart/form-data': - data, headers = encode_multipart( args, files ) + data, headers = encode_multipart(args, files) else: if args: data = urllib.urlencode(args) if content_type: headers['Content-Type'] = content_type - logger.debug('%s' % json.dumps( headers, indent = 4 )) - logger.debug('%s' % data) + logger.debug('%s' % json.dumps(headers, indent=4)) + logger.debug('%s' % data) request = urllib2.Request(url, data, headers) try: @@ -99,10 +99,10 @@ class qbittorrentclient(object): resp = '' for line in response: resp = resp + line - logger.debug('response code: %s' % str(response.code) ) - logger.debug('response: %s' % resp) + logger.debug('response code: %s' % str(response.code)) + logger.debug('response: %s' % resp) return response.code, json.loads(resp) - logger.debug('response code: %s' % str(response.code) ) + logger.debug('response code: %s' % str(response.code)) return response.code, None except urllib2.URLError as err: logger.debug('Failed URL: %s' % url) @@ -134,7 +134,7 @@ class qbittorrentclient(object): def pause(self, hash): logger.debug('qb.pause(%s)' % hash) args = {'hash': hash} - return self._command('command/pause', args,'application/x-www-form-urlencoded') + return self._command('command/pause', args, 'application/x-www-form-urlencoded') def getfiles(self, hash): logger.debug('qb.getfiles(%s)' % hash) @@ -147,7 +147,7 @@ class qbittorrentclient(object): def setprio(self, hash, priority): logger.debug('qb.setprio(%s,%d)' % (hash, priority)) args = {'hash': hash, 'priority': priority} - return self._command('command/setFilePrio', args,'application/x-www-form-urlencoded') + return self._command('command/setFilePrio', args, 'application/x-www-form-urlencoded') def remove(self, hash, remove_data=False): logger.debug('qb.remove(%s,%s)' % (hash, remove_data)) @@ -159,7 +159,9 @@ class qbittorrentclient(object): command = 'command/delete' return self._command(command, args, 'application/x-www-form-urlencoded') + def removeTorrent(hash, remove_data=False): + logger.debug('removeTorrent(%s,%s)' % (hash, remove_data)) qbclient = qbittorrentclient() @@ -175,6 +177,7 @@ def removeTorrent(hash, remove_data=False): return False return False + def addTorrent(link): logger.debug('addTorrent(%s)' % link) @@ -182,15 +185,19 @@ def addTorrent(link): args = {'urls': link, 'savepath': headphones.CONFIG.DOWNLOAD_TORRENT_DIR} if headphones.CONFIG.QBITTORRENT_LABEL: args['category'] = headphones.CONFIG.QBITTORRENT_LABEL - return qbclient._command('command/download', args, 'multipart/form-data' ) + + return qbclient._command('command/download', args, 'multipart/form-data') + def addFile(data): logger.debug('addFile(data)') qbclient = qbittorrentclient() files = {'torrents': {'filename': '', 'content': data}} + return qbclient._command('command/upload', filelist=files) + def getFolder(hash): logger.debug('getFolder(%s)' % hash) @@ -223,8 +230,10 @@ def getFolder(hash): torrent_folder = torrent_folder.replace('\\', '/') return os.path.basename(os.path.normpath(torrent_folder)) + _BOUNDARY_CHARS = string.digits + string.ascii_letters + # Taken from http://code.activestate.com/recipes/578668-encode-multipart-form-data-for-uploading-files-via/ # "MIT License" which is compatible with GPL def encode_multipart(args, files, boundary=None): @@ -245,7 +254,7 @@ def encode_multipart(args, files, boundary=None): '', str(value), )) - logger.debug(''.join( lines )) + logger.debug(''.join(lines)) if files: for name, value in files.items(): diff --git a/headphones/searcher.py b/headphones/searcher.py index 32e44a85..ba7040ae 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -1012,7 +1012,7 @@ def send_to_downloader(data, bestqual, album): seed_ratio = get_seed_ratio(bestqual[3]) if seed_ratio is not None: utorrent.setSeedRatio(torrentid, seed_ratio) - else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 4: + else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 4: logger.info("Sending torrent to QBiTorrent") # Add torrent From 784ec2927e2318a2432307863b83a44264aa27af Mon Sep 17 00:00:00 2001 From: dsm1212 Date: Sat, 14 Jan 2017 01:20:42 -0500 Subject: [PATCH 3/3] more pep8 fixes and a typo --- data/interfaces/default/config.html | 2 +- headphones/qbittorrent.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index cd0bb379..39a962ed 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -388,7 +388,7 @@
- Note: Works with WebAPI Rev 6 and later (QBitTorrent 3.4.0 and later) + Note: Works with WebAPI Rev 11 and later (QBitTorrent 3.4.0 and later)
diff --git a/headphones/qbittorrent.py b/headphones/qbittorrent.py index fe82fa30..6fee8d71 100644 --- a/headphones/qbittorrent.py +++ b/headphones/qbittorrent.py @@ -138,11 +138,11 @@ class qbittorrentclient(object): def getfiles(self, hash): logger.debug('qb.getfiles(%s)' % hash) - return self._command('query/propertiesFiles/'+hash) + return self._command('query/propertiesFiles/' + hash) def getprops(self, hash): logger.debug('qb.getprops(%s)' % hash) - return self._command('query/propertiesGeneral/'+hash) + return self._command('query/propertiesGeneral/' + hash) def setprio(self, hash, priority): logger.debug('qb.setprio(%s,%d)' % (hash, priority))