From 8323649c1097a563b045cf4455726706e4993356 Mon Sep 17 00:00:00 2001 From: Noah Manning Date: Wed, 3 Feb 2016 17:32:50 +0200 Subject: [PATCH 01/33] Added Deluge (Downloader) and Telegram (Notifier) Support **Features:** - - Added the ability to use Deluge as a torrent downloader (http://deluge-torrent.org/) - - Added the ability to use Telegram messenger for download notifications using Telegram's Bot API (https://core.telegram.org/bots) **Issues:** - - Post-processor doesn't look for the right directory when scanning for completed downloads, it looks for the torrent's official name and not the actually directory it was saved to --- data/interfaces/default/config.html | 89 ++++++- headphones/config.py | 10 + headphones/deluge.py | 397 ++++++++++++++++++++++++++++ headphones/notifiers.py | 33 +++ headphones/postprocessor.py | 12 +- headphones/searcher.py | 56 +++- headphones/webserve.py | 19 +- 7 files changed, 605 insertions(+), 11 deletions(-) create mode 100644 headphones/deluge.py diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index a1313d58..24f090ea 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -311,6 +311,7 @@ Black Hole Transmission uTorrent (Beta) + Deluge (Beta)
@@ -385,6 +386,35 @@
+
+
+ + + Usually http://localhost:8112 (requires WebUI plugin) +
+
+ + +
+
+ Note: With Deluge, you can specify a different download directory for downloads sent from Headphones. + Set it in the Music Download Directory below +
+
+ + + Labels shouldn't contain spaces (requires Label plugin) +
+
+ + + Directory where Deluge should move completed downloads +
+
+ + +
+
@@ -1222,6 +1252,23 @@
+
+
+ +
+
+
+ Contact @BotFather to create a bot and get its token +
+
+ Contact @myidbot to get your user ID +
+
+ +
+
+
+ @@ -2003,6 +2050,26 @@ } }); + if ($("#telegram").is(":checked")) + { + $("#telegramoptions").show(); + } + else + { + $("#telegramoptions").hide(); + } + + $("#telegram").click(function(){ + if ($("#telegram").is(":checked")) + { + $("#telegramoptions").slideDown(); + } + else + { + $("#telegramoptions").slideUp(); + } + }); + if ($("#osx_notify").is(":checked")) { $("#osx_notify_options").show(); @@ -2159,19 +2226,25 @@ if ($("#torrent_downloader_blackhole").is(":checked")) { - $("#transmission_options,#utorrent_options").hide(); + $("#transmission_options,#utorrent_options,#deluge_options").hide(); $("#torrent_blackhole_options").show(); } if ($("#torrent_downloader_transmission").is(":checked")) { - $("#torrent_blackhole_options,#utorrent_options").hide(); + $("#torrent_blackhole_options,#utorrent_options,#deluge_options").hide(); $("#transmission_options").show(); } if ($("#torrent_downloader_utorrent").is(":checked")) { - $("#torrent_blackhole_options,#transmission_options").hide(); + $("#torrent_blackhole_options,#transmission_options,#deluge_options").hide(); $("#utorrent_options").show(); } + if ($("#torrent_downloader_deluge").is(":checked")) + { + $("#torrent_blackhole_options,#transmission_options,#utorrent_options").hide(); + $("#deluge_options").show(); + } + $('input[type=radio]').change(function(){ if ($("#preferred_bitrate").is(":checked")) @@ -2208,15 +2281,19 @@ } if ($("#torrent_downloader_blackhole").is(":checked")) { - $("#transmission_options,#utorrent_options").fadeOut("fast", function() { $("#torrent_blackhole_options").fadeIn() }); + $("#transmission_options,#utorrent_options,#deluge_options").fadeOut("fast", function() { $("#torrent_blackhole_options").fadeIn() }); } if ($("#torrent_downloader_transmission").is(":checked")) { - $("#torrent_blackhole_options,#utorrent_options").fadeOut("fast", function() { $("#transmission_options").fadeIn() }); + $("#torrent_blackhole_options,#utorrent_options,#deluge_options").fadeOut("fast", function() { $("#transmission_options").fadeIn() }); } if ($("#torrent_downloader_utorrent").is(":checked")) { - $("#torrent_blackhole_options,#transmission_options").fadeOut("fast", function() { $("#utorrent_options").fadeIn() }); + $("#torrent_blackhole_options,#transmission_options,#deluge_options").fadeOut("fast", function() { $("#utorrent_options").fadeIn() }); + } + if ($("#torrent_downloader_deluge").is(":checked")) + { + $("#torrent_blackhole_options,#utorrent_options,#transmission_options").fadeOut("fast", function() { $("#deluge_options").fadeIn() }); } }); diff --git a/headphones/config.py b/headphones/config.py index 8362b934..c63ae170 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -53,6 +53,12 @@ _CONFIG_DEFINITIONS = { 'CUSTOMSLEEP': (int, 'General', 1), 'CUSTOMUSER': (str, 'General', ''), 'DELETE_LOSSLESS_FILES': (int, 'General', 1), + 'DELUGE_HOST': (str, 'Deluge', ''), + 'DELUGE_PASSWORD': (str, 'Deluge', ''), + 'DELUGE_USERNAME': (str, 'Deluge', ''), + 'DELUGE_LABEL': (str, 'Deluge', ''), + 'DELUGE_DONE_DIRECTORY': (str, 'Deluge', ''), + 'DELUGE_PAUSED': (int, 'Deluge', 0), 'DESTINATION_DIR': (str, 'General', ''), 'DETECT_BITRATE': (int, 'General', 0), 'DO_NOT_PROCESS_UNMATCHED': (int, 'General', 0), @@ -234,6 +240,10 @@ _CONFIG_DEFINITIONS = { 'SUBSONIC_PASSWORD': (str, 'Subsonic', ''), 'SUBSONIC_USERNAME': (str, 'Subsonic', ''), 'SYNOINDEX_ENABLED': (int, 'Synoindex', 0), + 'TELEGRAM_TOKEN': (str, 'Telegram', ''), + 'TELEGRAM_USERID': (str, 'Telegram', ''), + 'TELEGRAM_ENABLED': (int, 'Telegram', 0), + 'TELEGRAM_ONSNATCH': (int, 'Telegram', 0), 'TORRENTBLACKHOLE_DIR': (str, 'General', ''), 'TORRENT_DOWNLOADER': (int, 'General', 0), 'TORRENT_REMOVAL_INTERVAL': (int, 'General', 720), diff --git a/headphones/deluge.py b/headphones/deluge.py new file mode 100644 index 00000000..06e95db5 --- /dev/null +++ b/headphones/deluge.py @@ -0,0 +1,397 @@ +# 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 . + +# Parts of this file are a part of SickRage. +# Author: echel0n +# URL: http://www.github.com/sickragetv/sickrage/ +# +# Adapted for Headphones by +# +# SickRage 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. +# +# SickRage 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 SickRage. If not, see . + +from __future__ import unicode_literals + +from headphones import logger, request + +import time +import re +import os +import json +import base64 +import urlparse +import headphones +import requests + +from base64 import b64encode + +delugeweb_auth = {} +delugeweb_url = '' + +def add_torrent(link, data=None): + try: + result = {} + retid = False + if link.endswith('.torrent') or data: + # .torrent? for torcache links + # or '.torrent?' in link + if data: + metainfo = str(base64.b64encode(data)) + # before I found out HP handles the downloads + #elif link.startswith('http://') or link.startswith('https://'): + # user_agent = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.1546.111 Safari/582.36' + # headers = { 'User-Agent': user_agent } + # torrentfile = '' + # r = requests.get(link, headers=headers) + # if r.status_code == 200: + # for chunk in r.iter_content(chunk_size=1024): + # if chunk: # filter out keep-alive new chunks + # torrent_file = torrentfile + chunk + # metainfo = str(base64.b64encode(torrentfile.decode('utf-8'))) + else: + with open(link, 'rb') as f: + metainfo = str(base64.b64encode(f.read())) + # Extract torrent name from .torrent + name_length = int( re.findall( 'name([0-9]*)\:.*?\:', base64.b64encode(metainfo) )[0] ) + name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo) )[0][:size] + result = { + 'type' : 'torrent', + 'name' : name, + 'content' : metainfo, + } + retid = add_torrent_file(result) + + elif link.startswith('magnet:'): + result = { + 'type' : 'magnet', + 'url' : link, + } + retid = add_torrent_uri(result) + else: + logger.error('Deluge: Unknown file type - ' + str(link)) + + if retid: + logger.info(u"Torrent sent to Deluge successfully") + return retid + else: + logger.info('Deluge returned status %s' % retid) + return False + + except Exception, e: + logger.error( str(e) ) + +def get_torrent_folder(result): + + if not any(delugeweb_auth): + get_auth() + + post_data = json.dumps({ + "method": "web.get_torrent_status", + "params": [ + result['hash'], + [ "total_done" ] + ], + "id": 22 + }) + + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result['total_done'] = json.loads(response.text)['result']['total_done'] + + tries = 0 + while result['total_done'] == 0 and tries < 10: + tries += 1 + time.sleep(5) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result['total_done'] = json.loads(response.text)['result']['total_done'] + + post_data = json.dumps({ + "method": "web.get_torrent_status", + "params": [ + result['hash'], + [ + "name", + "save_path", + "total_size", + "num_files", + "message", + "tracker", + "comment" + ] + ], + "id": 23 + }) + + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + result['save_path'] = json.loads(response.text)['result']['save_path'] + result['name'] = json.loads(response.text)['result']['name'] + + return json.loads(response.text)['result']['name'] + +def remove_torrent(torrentid, remove_data=False): + ''' + todo + ''' + return + +def get_auth(): + + global delugeweb_auth, delugeweb_url + delugeweb_auth = {} + + delugeweb_host = headphones.CONFIG.DELUGE_HOST + delugeweb_username = headphones.CONFIG.DELUGE_USERNAME + delugeweb_password = headphones.CONFIG.DELUGE_PASSWORD + + if not delugeweb_host.startswith('http'): + delugeweb_host = 'http://' + delugeweb_host + + if delugeweb_host.endswith('/'): + delugeweb_host = delugeweb_host[:-1] + + delugeweb_url = delugeweb_host + '/json' + + post_data = json.dumps({"method": "auth.login", + "params": [delugeweb_password], + "id": 1}) + try: + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + # , verify=TORRENT_VERIFY_CERT) + except Exception: + return None + + auth = json.loads(response.text)["result"] + delugeweb_auth = response.cookies + + post_data = json.dumps({"method": "web.connected", + "params": [], + "id": 10}) + try: + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + # , verify=TORRENT_VERIFY_CERT) + except Exception: + return None + + connected = json.loads(response.text)['result'] + + if not connected: + post_data = json.dumps({"method": "web.get_delugeweb_hosts", + "params": [], + "id": 11}) + try: + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + # , verify=TORRENT_VERIFY_CERT) + except Exception: + return None + + delugeweb_hosts = json.loads(response.text)['result'] + if len(delugeweb_hosts) == 0: + logger.error('Deluge: WebUI does not contain daemons') + return None + + post_data = json.dumps({"method": "web.connect", + "params": [delugeweb_hosts[0][0]], + "id": 11}) + + try: + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + # , verify=TORRENT_VERIFY_CERT) + except Exception: + return None + + post_data = json.dumps({"method": "web.connected", + "params": [], + "id": 10}) + + try: + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + # , verify=TORRENT_VERIFY_CERT) + except Exception: + return None + + connected = json.loads(response.text)['result'] + + if not connected: + logger.error('Deluge: WebUI could not connect to daemon') + return None + + return auth + +def add_torrent_uri(result): + + if not any(delugeweb_auth): + get_auth() + + post_data = json.dumps({"method": "core.add_torrent_magnet", + "params": [result['url'], {}], + "id": 2}) + + ''' + # This method doesn't return hash + post_data = json.dumps({ + "method": "web.add_torrents", + "params": [ + [ + { + "path": result['url'], + "options": { + "add_paused": headphones.CONFIG.DELUGE_PAUSED, + #"download_location": headphones.CONFIG.DOWNLOAD_TORRENT_DIR, + #"move_completed": true, + #"move_completed_path": headphones.CONFIG.DELUGE_DONE_DIRECTORY, + } + } + ] + ], + "id": 2 + }) + ''' + + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + result['hash'] = json.loads(response.text)['result'] + + return json.loads(response.text)['result'] + +def add_torrent_file(result): + + if not any(delugeweb_auth): + get_auth() + + post_data = json.dumps({"method": "core.add_torrent_file", + "params": [result['name'] + '.torrent', b64encode(result['content']), {}], + "id": 2}) + + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + result['hash'] = json.loads(response.text)['result'] + + return json.loads(response.text)['result'] + +def set_torrent_label(result): + + label = headphones.CONFIG.DELUGE_LABEL + + if not any(delugeweb_auth): + get_auth() + + if ' ' in label: + logger.error('Deluge: Invalid label. Label must not contain a space - replacing with underscores') + label = label.replace(' ', '_') + if label: + # check if label already exists and create it if not + post_data = json.dumps({"method": 'label.get_labels', + "params": [], + "id": 3}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + labels = json.loads(response.text)['result'] + + if labels is not None: + if label not in labels: + logger.debug('Deluge: ' + label + " label does not exist in Deluge we must add it") + post_data = json.dumps({"method": 'label.add', + "params": [label], + "id": 4}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + logger.debug('Deluge: ' + label + " label added to Deluge") + + # add label to torrent + post_data = json.dumps({"method": 'label.set_torrent', + "params": [result['hash'], label], + "id": 5}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + logger.debug('Deluge: ' + label + " label added to torrent") + else: + logger.debug('Deluge: ' + "label plugin not detected") + return False + + return not json.loads(response.text)['error'] + +def set_torrent_ratio(result): + + if not any(delugeweb_auth): + get_auth() + + ratio = None + if result['ratio']: + ratio = result['ratio'] + + if ratio: + post_data = json.dumps({"method": "core.set_torrent_stop_at_ratio", + "params": [result['hash'], True], + "id": 5}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + post_data = json.dumps({"method": "core.set_torrent_stop_ratio", + "params": [result['hash'], float(ratio)], + "id": 6}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + return not json.loads(response.text)['error'] + + return True + +def set_torrent_path(result): + + if not any(delugeweb_auth): + get_auth() + + if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR: + post_data = json.dumps({"method": "core.set_torrent_move_completed", + "params": [result['hash'], True], + "id": 7}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + if headphones.CONFIG.DELUGE_DONE_DIRECTORY: + move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY + else: + move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR + + if not os.path.exists(move_to): + logger.debug("Deluge: " + move_to + " directory doesn't exist, let's create it") + os.makedirs(move_to) + post_data = json.dumps({"method": "core.set_torrent_move_completed_path", + "params": [result['hash'], move_to], + "id": 8}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + return not json.loads(response.text)['error'] + + return True + +def set_torrent_pause(result): + + if not any(delugeweb_auth): + get_auth() + + if headphones.CONFIG.DELUGE_PAUSED: + post_data = json.dumps({"method": "core.pause_torrent", + "params": [[ result['hash'] ]], + "id": 9}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + + return not json.loads(response.text)['error'] + + return True + diff --git a/headphones/notifiers.py b/headphones/notifiers.py index 540107a7..2a9a0362 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -856,3 +856,36 @@ class Email(object): except Exception, e: logger.warn('Error sending Email: %s' % e) return False + + +class TELEGRAM(object): + + def notify(self, message, status): + if not headphones.CONFIG.TELEGRAM_ENABLED: + return + + import requests + + TELEGRAM_API = "https://api.telegram.org/bot%s/%s" + + # Get configuration data + token = headphones.CONFIG.TELEGRAM_TOKEN + userid = headphones.CONFIG.TELEGRAM_USERID + + # Construct message + payload = {'chat_id': userid, 'text': status + ': ' + message } + + # Send message to user using Telegram's Bot API + try: + response = requests.post(TELEGRAM_API % (token, "sendMessage"), data=payload) + except Exception, e: + logger.info(u'Telegram notify failed: ' + str(e) ) + + # Error logging + sent_successfuly = True + if not response.status_code == 200: + logger.info(u'Could not send notification to TelegramBot (token=%s). Response: [%s]', (token, response.text)) + sent_successfuly = False + + logger.info(u"Telegram notifications sent.") + return sent_successfuly diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 432ab796..c386e6e8 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -27,7 +27,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 +from headphones import notifiers, utorrent, transmission, deluge from headphones import db, albumart, librarysync from headphones import logger, helpers, request, mb, music_encoder @@ -46,7 +46,10 @@ def checkFolder(): if album['Kind'] == 'nzb': download_dir = headphones.CONFIG.DOWNLOAD_DIR else: - download_dir = headphones.CONFIG.DOWNLOAD_TORRENT_DIR + if headphones.CONFIG.DELUGE_DONE_DIRECTORY: + download_dir = headphones.CONFIG.DELUGE_DONE_DIRECTORY + else: + download_dir = headphones.CONFIG.DOWNLOAD_TORRENT_DIR album_path = os.path.join(download_dir, album['FolderName']).encode( headphones.SYS_ENCODING, 'replace') @@ -532,6 +535,11 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, pushbullet = notifiers.PUSHBULLET() pushbullet.notify(pushmessage, statusmessage) + if headphones.CONFIG.TELEGRAM_ENABLED: + logger.info(u"Telegram request") + telegram = notifiers.TELEGRAM() + telegram.notify(pushmessage, statusmessage) + if headphones.CONFIG.TWITTER_ENABLED: logger.info(u"Sending Twitter notification") twitter = notifiers.TwitterNotifier() diff --git a/headphones/searcher.py b/headphones/searcher.py index 91489a23..2d3e1e8b 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -33,7 +33,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 +from headphones import utorrent, transmission, notifiers, rutracker, deluge from bencode import bencode, bdecode @@ -851,7 +851,7 @@ def send_to_downloader(data, bestqual, album): else: logger.error("Cannot save magnet link in blackhole. " \ "Please switch your torrent downloader to " \ - "Transmission or uTorrent, or allow Headphones " \ + "Transmission, uTorrent or Deluge, or allow Headphones " \ "to open or convert magnet links") return else: @@ -889,6 +889,54 @@ def send_to_downloader(data, bestqual, album): if seed_ratio is not None: transmission.setSeedRatio(torrentid, seed_ratio) + elif headphones.CONFIG.TORRENT_DOWNLOADER == 3: # Deluge + logger.info("Sending torrent to Deluge") + + try: + # Add torrent + if bestqual[3] == 'rutracker.org': + torrentid = deluge.add_torrent('', data) + else: + torrentid = deluge.add_torrent(bestqual[2]) + + if not torrentid: + logger.error("Error sending torrent to Deluge. Are you sure it's running? Maybe the torrent already exists?") + return + + # This isn't really necessary for an "Add Paused" mode, + # but it's a bit different than the built-in "Add Paused" + # because it pauses the torrent a moment after it has already been added. + # May be useful in the future + if headphones.CONFIG.DELUGE_PAUSED: + deluge.set_torrent_pause({'hash': torrentid}) + + # Set Label + if headphones.CONFIG.DELUGE_LABEL: + deluge.set_torrent_label({'hash': torrentid}) + + # Set Seed Ratio + seed_ratio = get_seed_ratio(bestqual[3]) + if seed_ratio is not None: + deluge.set_torrent_ratio({'hash': torrentid, 'ratio': seed_ratio}) + + # Set move-to directory + if headphones.CONFIG.DELUGE_DONE_DIRECTORY: + deluge.set_torrent_path({'hash': torrentid}) + + # I only just realized this function is useless... + folder_name = deluge.get_torrent_folder({'hash': torrentid}) + if folder_name: + logger.info('Torrent folder name: %s' % folder_name) + else: + logger.error('Torrent folder name could not be determined') + return + + except Exception as e: + #exc_type, exc_obj, exc_tb = sys.exc_info() + #fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + #print(exc_type, fname, exc_tb.tb_lineno) + logger.error( str(e) ) + else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 2: logger.info("Sending torrent to uTorrent") @@ -960,6 +1008,10 @@ def send_to_downloader(data, bestqual, album): logger.info(u"Sending PushBullet notification") pushbullet = notifiers.PUSHBULLET() pushbullet.notify(name, "Download started") + if headphones.CONFIG.TELEGRAM_ENABLED and headphones.CONFIG.TELEGRAM_ONSNATCH: + logger.info(u"Sending Telegram notification") + telegram = notifiers.TELEGRAM() + telegram.notify(name, "Download started") if headphones.CONFIG.TWITTER_ENABLED and headphones.CONFIG.TWITTER_ONSNATCH: logger.info(u"Sending Twitter notification") twitter = notifiers.TwitterNotifier() diff --git a/headphones/webserve.py b/headphones/webserve.py index 561dea0e..a25e007f 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1156,6 +1156,12 @@ class WebInterface(object): "transmission_host": headphones.CONFIG.TRANSMISSION_HOST, "transmission_username": headphones.CONFIG.TRANSMISSION_USERNAME, "transmission_password": headphones.CONFIG.TRANSMISSION_PASSWORD, + "deluge_host": headphones.CONFIG.DELUGE_HOST, + "deluge_username": headphones.CONFIG.DELUGE_USERNAME, + "deluge_password": headphones.CONFIG.DELUGE_PASSWORD, + "deluge_label": headphones.CONFIG.DELUGE_LABEL, + "deluge_done_directory": headphones.CONFIG.DELUGE_DONE_DIRECTORY, + "deluge_paused": checked(headphones.CONFIG.DELUGE_PAUSED), "utorrent_host": headphones.CONFIG.UTORRENT_HOST, "utorrent_username": headphones.CONFIG.UTORRENT_USERNAME, "utorrent_password": headphones.CONFIG.UTORRENT_PASSWORD, @@ -1166,6 +1172,7 @@ class WebInterface(object): "torrent_downloader_blackhole": radio(headphones.CONFIG.TORRENT_DOWNLOADER, 0), "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), "download_dir": headphones.CONFIG.DOWNLOAD_DIR, "use_blackhole": checked(headphones.CONFIG.BLACKHOLE), "blackhole_dir": headphones.CONFIG.BLACKHOLE_DIR, @@ -1324,6 +1331,10 @@ class WebInterface(object): "pushbullet_onsnatch": checked(headphones.CONFIG.PUSHBULLET_ONSNATCH), "pushbullet_apikey": headphones.CONFIG.PUSHBULLET_APIKEY, "pushbullet_deviceid": headphones.CONFIG.PUSHBULLET_DEVICEID, + "telegram_enabled": checked(headphones.CONFIG.TELEGRAM_ENABLED), + "telegram_onsnatch": checked(headphones.CONFIG.TELEGRAM_ONSNATCH), + "telegram_token": headphones.CONFIG.TELEGRAM_TOKEN, + "telegram_userid": headphones.CONFIG.TELEGRAM_USERID, "subsonic_enabled": checked(headphones.CONFIG.SUBSONIC_ENABLED), "subsonic_host": headphones.CONFIG.SUBSONIC_HOST, "subsonic_username": headphones.CONFIG.SUBSONIC_USERNAME, @@ -1420,10 +1431,11 @@ class WebInterface(object): "synoindex_enabled", "pushover_enabled", "pushover_onsnatch", "pushbullet_enabled", "pushbullet_onsnatch", "subsonic_enabled", "twitter_enabled", "twitter_onsnatch", + "telegram_enabled", "telegram_onsnatch", "osx_notify_enabled", "osx_notify_onsnatch", "boxcar_enabled", "boxcar_onsnatch", "songkick_enabled", "songkick_filter_enabled", "mpc_enabled", "email_enabled", "email_ssl", "email_tls", "email_onsnatch", - "customauth", "idtag" + "customauth", "idtag", "deluge_paused" ] for checked_config in checked_configs: if checked_config not in kwargs: @@ -1673,6 +1685,11 @@ class WebInterface(object): pushbullet = notifiers.PUSHBULLET() pushbullet.notify("it works!", "Test message") + @cherrypy.expose + def testTelegram(self): + logger.info("Testing Telegram notifications") + telegram = notifiers.TELEGRAM() + telegram.notify("it works!", "lazers pew pew") class Artwork(object): @cherrypy.expose From 0c3ebfbbb39f58f37ec371194f784af3a9944045 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 4 Feb 2016 14:05:12 +0200 Subject: [PATCH 02/33] Exception handling for torrent name regular expression Fall back to the torrent file's name in case reular expression extraction method from the torrent file doesn't work. --- headphones/deluge.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 06e95db5..b81397f2 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -74,8 +74,15 @@ def add_torrent(link, data=None): with open(link, 'rb') as f: metainfo = str(base64.b64encode(f.read())) # Extract torrent name from .torrent - name_length = int( re.findall( 'name([0-9]*)\:.*?\:', base64.b64encode(metainfo) )[0] ) - name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo) )[0][:size] + try: + name_length = int( re.findall( 'name([0-9]*)\:.*?\:', base64.b64encode(metainfo) )[0] ) + name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo) )[0][:size] + except: + # get last part of link/path (name only) + name = link.split('\\')[-1].split('/')[-1] + # remove '.torrent' suffix + if name[-len('.torrent'):] == '.torrent': + name = name[:-len('.torrent')] result = { 'type' : 'torrent', 'name' : name, From de9d77a8ef541d1d1debfa38eeebfa2db97bd519 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 4 Feb 2016 15:48:53 +0200 Subject: [PATCH 03/33] Renames Cleanups and Torrent Removal Function **Changes:** - Modified function names to adhere to policy - Removed/commented unused elements - Added torrent removal function --- headphones/deluge.py | 162 +++++++++++++++--------------------- headphones/postprocessor.py | 4 +- headphones/searcher.py | 19 ++--- 3 files changed, 80 insertions(+), 105 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index b81397f2..445af300 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -34,14 +34,14 @@ from __future__ import unicode_literals -from headphones import logger, request +from headphones import logger +#from headphones import request import time import re import os import json import base64 -import urlparse import headphones import requests @@ -50,33 +50,20 @@ from base64 import b64encode delugeweb_auth = {} delugeweb_url = '' -def add_torrent(link, data=None): +def addTorrent(link, data=None): try: result = {} retid = False if link.endswith('.torrent') or data: - # .torrent? for torcache links - # or '.torrent?' in link if data: metainfo = str(base64.b64encode(data)) - # before I found out HP handles the downloads - #elif link.startswith('http://') or link.startswith('https://'): - # user_agent = 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.1546.111 Safari/582.36' - # headers = { 'User-Agent': user_agent } - # torrentfile = '' - # r = requests.get(link, headers=headers) - # if r.status_code == 200: - # for chunk in r.iter_content(chunk_size=1024): - # if chunk: # filter out keep-alive new chunks - # torrent_file = torrentfile + chunk - # metainfo = str(base64.b64encode(torrentfile.decode('utf-8'))) else: with open(link, 'rb') as f: metainfo = str(base64.b64encode(f.read())) # Extract torrent name from .torrent try: - name_length = int( re.findall( 'name([0-9]*)\:.*?\:', base64.b64encode(metainfo) )[0] ) - name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo) )[0][:size] + name_length = int(re.findall('name([0-9]*)\:.*?\:', base64.b64encode(metainfo))[0]) + name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo))[0][:name_length] except: # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] @@ -88,14 +75,14 @@ def add_torrent(link, data=None): 'name' : name, 'content' : metainfo, } - retid = add_torrent_file(result) + retid = _add_torrent_file(result) elif link.startswith('magnet:'): result = { 'type' : 'magnet', 'url' : link, } - retid = add_torrent_uri(result) + retid = _add_torrent_uri(result) else: logger.error('Deluge: Unknown file type - ' + str(link)) @@ -107,20 +94,20 @@ def add_torrent(link, data=None): return False except Exception, e: - logger.error( str(e) ) + logger.error(str(e)) -def get_torrent_folder(result): +def getTorrentFolder(result): if not any(delugeweb_auth): - get_auth() + _get_auth() post_data = json.dumps({ - "method": "web.get_torrent_status", - "params": [ - result['hash'], - [ "total_done" ] - ], - "id": 22 + "method": "web.get_torrent_status", + "params": [ + result['hash'], + ["total_done"] + ], + "id": 22 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) @@ -134,20 +121,20 @@ def get_torrent_folder(result): result['total_done'] = json.loads(response.text)['result']['total_done'] post_data = json.dumps({ - "method": "web.get_torrent_status", - "params": [ - result['hash'], - [ - "name", - "save_path", - "total_size", - "num_files", - "message", - "tracker", - "comment" - ] - ], - "id": 23 + "method": "web.get_torrent_status", + "params": [ + result['hash'], + [ + "name", + "save_path", + "total_size", + "num_files", + "message", + "tracker", + "comment" + ] + ], + "id": 23 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) @@ -157,19 +144,32 @@ def get_torrent_folder(result): return json.loads(response.text)['result']['name'] -def remove_torrent(torrentid, remove_data=False): - ''' - todo - ''' - return +def removeTorrent(torrentid, remove_data=False): -def get_auth(): + if not any(delugeweb_auth): + _get_auth() + + result = False + post_data = json.dumps({ + "method": "core.remove_torrent", + "params": [ + torrentid, + remove_data + ], + "id": 25 + }) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result = json.loads(response.text)['result'] + + return result + +def _get_auth(): global delugeweb_auth, delugeweb_url delugeweb_auth = {} delugeweb_host = headphones.CONFIG.DELUGE_HOST - delugeweb_username = headphones.CONFIG.DELUGE_USERNAME + # delugeweb_username = headphones.CONFIG.DELUGE_USERNAME delugeweb_password = headphones.CONFIG.DELUGE_PASSWORD if not delugeweb_host.startswith('http'): @@ -184,7 +184,7 @@ def get_auth(): "params": [delugeweb_password], "id": 1}) try: - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) # , verify=TORRENT_VERIFY_CERT) except Exception: return None @@ -196,7 +196,7 @@ def get_auth(): "params": [], "id": 10}) try: - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) # , verify=TORRENT_VERIFY_CERT) except Exception: return None @@ -204,11 +204,11 @@ def get_auth(): connected = json.loads(response.text)['result'] if not connected: - post_data = json.dumps({"method": "web.get_delugeweb_hosts", + post_data = json.dumps({"method": "web.get_hosts", "params": [], "id": 11}) try: - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) # , verify=TORRENT_VERIFY_CERT) except Exception: return None @@ -223,7 +223,7 @@ def get_auth(): "id": 11}) try: - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) # , verify=TORRENT_VERIFY_CERT) except Exception: return None @@ -233,7 +233,7 @@ def get_auth(): "id": 10}) try: - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth ) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) # , verify=TORRENT_VERIFY_CERT) except Exception: return None @@ -246,63 +246,39 @@ def get_auth(): return auth -def add_torrent_uri(result): +def _add_torrent_uri(result): if not any(delugeweb_auth): - get_auth() + _get_auth() post_data = json.dumps({"method": "core.add_torrent_magnet", "params": [result['url'], {}], "id": 2}) - - ''' - # This method doesn't return hash - post_data = json.dumps({ - "method": "web.add_torrents", - "params": [ - [ - { - "path": result['url'], - "options": { - "add_paused": headphones.CONFIG.DELUGE_PAUSED, - #"download_location": headphones.CONFIG.DOWNLOAD_TORRENT_DIR, - #"move_completed": true, - #"move_completed_path": headphones.CONFIG.DELUGE_DONE_DIRECTORY, - } - } - ] - ], - "id": 2 - }) - ''' - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - result['hash'] = json.loads(response.text)['result'] return json.loads(response.text)['result'] -def add_torrent_file(result): +def _add_torrent_file(result): if not any(delugeweb_auth): - get_auth() + _get_auth() post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'] + '.torrent', b64encode(result['content']), {}], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - result['hash'] = json.loads(response.text)['result'] return json.loads(response.text)['result'] -def set_torrent_label(result): +def setTorrentLabel(result): label = headphones.CONFIG.DELUGE_LABEL if not any(delugeweb_auth): - get_auth() + _get_auth() if ' ' in label: logger.error('Deluge: Invalid label. Label must not contain a space - replacing with underscores') @@ -336,10 +312,10 @@ def set_torrent_label(result): return not json.loads(response.text)['error'] -def set_torrent_ratio(result): +def setSeedRatio(result): if not any(delugeweb_auth): - get_auth() + _get_auth() ratio = None if result['ratio']: @@ -359,10 +335,10 @@ def set_torrent_ratio(result): return True -def set_torrent_path(result): +def setTorrentPath(result): if not any(delugeweb_auth): - get_auth() + _get_auth() if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR: post_data = json.dumps({"method": "core.set_torrent_move_completed", @@ -387,14 +363,14 @@ def set_torrent_path(result): return True -def set_torrent_pause(result): +def addTorrentPause(result): if not any(delugeweb_auth): - get_auth() + _get_auth() if headphones.CONFIG.DELUGE_PAUSED: post_data = json.dumps({"method": "core.pause_torrent", - "params": [[ result['hash'] ]], + "params": [[result['hash']]], "id": 9}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index c386e6e8..9ad4f039 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -456,6 +456,8 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, release['ArtistName'], release['AlbumTitle'])) if headphones.CONFIG.TORRENT_DOWNLOADER == 1: torrent_removed = transmission.removeTorrent(hash, True) + elif headphones.CONFIG.TORRENT_DOWNLOADER == 3: # Deluge + torrent_removed = deluge.removeTorrent(hash, True) else: torrent_removed = utorrent.removeTorrent(hash, True) @@ -539,7 +541,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, logger.info(u"Telegram request") telegram = notifiers.TELEGRAM() telegram.notify(pushmessage, statusmessage) - + if headphones.CONFIG.TWITTER_ENABLED: logger.info(u"Sending Twitter notification") twitter = notifiers.TwitterNotifier() diff --git a/headphones/searcher.py b/headphones/searcher.py index 2d3e1e8b..ef93519b 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -895,36 +895,33 @@ def send_to_downloader(data, bestqual, album): try: # Add torrent if bestqual[3] == 'rutracker.org': - torrentid = deluge.add_torrent('', data) + torrentid = deluge.addTorrent('', data) else: - torrentid = deluge.add_torrent(bestqual[2]) + torrentid = deluge.addTorrent(bestqual[2]) if not torrentid: logger.error("Error sending torrent to Deluge. Are you sure it's running? Maybe the torrent already exists?") return - # This isn't really necessary for an "Add Paused" mode, - # but it's a bit different than the built-in "Add Paused" - # because it pauses the torrent a moment after it has already been added. - # May be useful in the future + # This pauses the torrent right after it is added if headphones.CONFIG.DELUGE_PAUSED: - deluge.set_torrent_pause({'hash': torrentid}) + deluge.setTorrentPause({'hash': torrentid}) # Set Label if headphones.CONFIG.DELUGE_LABEL: - deluge.set_torrent_label({'hash': torrentid}) + deluge.setTorrentLabel({'hash': torrentid}) # Set Seed Ratio seed_ratio = get_seed_ratio(bestqual[3]) if seed_ratio is not None: - deluge.set_torrent_ratio({'hash': torrentid, 'ratio': seed_ratio}) + deluge.setSeedRatio({'hash': torrentid, 'ratio': seed_ratio}) # Set move-to directory if headphones.CONFIG.DELUGE_DONE_DIRECTORY: - deluge.set_torrent_path({'hash': torrentid}) + deluge.setTorrentPath({'hash': torrentid}) # I only just realized this function is useless... - folder_name = deluge.get_torrent_folder({'hash': torrentid}) + folder_name = deluge.getTorrentFolder({'hash': torrentid}) if folder_name: logger.info('Torrent folder name: %s' % folder_name) else: From ca36938a838c57b0ad7da6d78378bba59b12f8da Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 4 Feb 2016 16:10:22 +0200 Subject: [PATCH 04/33] Removed Whitespace - Travis stuff --- headphones/deluge.py | 75 ++++++++++++++++++----------------------- headphones/notifiers.py | 10 +++--- headphones/searcher.py | 6 ++-- headphones/webserve.py | 2 +- 4 files changed, 42 insertions(+), 51 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 445af300..fec96781 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -54,7 +54,7 @@ def addTorrent(link, data=None): try: result = {} retid = False - if link.endswith('.torrent') or data: + if link.endswith('.torrent') or data: if data: metainfo = str(base64.b64encode(data)) else: @@ -70,8 +70,7 @@ def addTorrent(link, data=None): # remove '.torrent' suffix if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] - result = { - 'type' : 'torrent', + result = {'type': 'torrent', 'name' : name, 'content' : metainfo, } @@ -101,14 +100,12 @@ def getTorrentFolder(result): if not any(delugeweb_auth): _get_auth() - post_data = json.dumps({ - "method": "web.get_torrent_status", - "params": [ - result['hash'], - ["total_done"] - ], - "id": 22 - }) + post_data = json.dumps({"method": "web.get_torrent_status", + "params": [ + result['hash'], + ["total_done"] + ], + "id": 22}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['total_done'] = json.loads(response.text)['result']['total_done'] @@ -120,22 +117,20 @@ def getTorrentFolder(result): response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['total_done'] = json.loads(response.text)['result']['total_done'] - post_data = json.dumps({ - "method": "web.get_torrent_status", - "params": [ - result['hash'], - [ - "name", - "save_path", - "total_size", - "num_files", - "message", - "tracker", - "comment" - ] - ], - "id": 23 - }) + post_data = json.dumps({"method": "web.get_torrent_status", + "params": [ + result['hash'], + [ + "name", + "save_path", + "total_size", + "num_files", + "message", + "tracker", + "comment" + ] + ], + "id": 23}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) @@ -150,14 +145,12 @@ def removeTorrent(torrentid, remove_data=False): _get_auth() result = False - post_data = json.dumps({ - "method": "core.remove_torrent", - "params": [ - torrentid, - remove_data - ], - "id": 25 - }) + post_data = json.dumps({"method": "core.remove_torrent", + "params": [ + torrentid, + remove_data + ], + "id": 25}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result = json.loads(response.text)['result'] @@ -165,7 +158,7 @@ def removeTorrent(torrentid, remove_data=False): def _get_auth(): - global delugeweb_auth, delugeweb_url + global delugeweb_auth, delugeweb_url delugeweb_auth = {} delugeweb_host = headphones.CONFIG.DELUGE_HOST @@ -267,7 +260,6 @@ def _add_torrent_file(result): post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'] + '.torrent', b64encode(result['content']), {}], "id": 2}) - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] @@ -345,10 +337,10 @@ def setTorrentPath(result): "params": [result['hash'], True], "id": 7}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - + if headphones.CONFIG.DELUGE_DONE_DIRECTORY: move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY - else: + else: move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR if not os.path.exists(move_to): @@ -364,7 +356,7 @@ def setTorrentPath(result): return True def addTorrentPause(result): - + if not any(delugeweb_auth): _get_auth() @@ -376,5 +368,4 @@ def addTorrentPause(result): return not json.loads(response.text)['error'] - return True - + return True \ No newline at end of file diff --git a/headphones/notifiers.py b/headphones/notifiers.py index 2a9a0362..d22303cc 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -865,21 +865,21 @@ class TELEGRAM(object): return import requests - + TELEGRAM_API = "https://api.telegram.org/bot%s/%s" - + # Get configuration data token = headphones.CONFIG.TELEGRAM_TOKEN userid = headphones.CONFIG.TELEGRAM_USERID - + # Construct message - payload = {'chat_id': userid, 'text': status + ': ' + message } + payload = {'chat_id': userid, 'text': status + ': ' + message} # Send message to user using Telegram's Bot API try: response = requests.post(TELEGRAM_API % (token, "sendMessage"), data=payload) except Exception, e: - logger.info(u'Telegram notify failed: ' + str(e) ) + logger.info(u'Telegram notify failed: ' + str(e)) # Error logging sent_successfuly = True diff --git a/headphones/searcher.py b/headphones/searcher.py index ef93519b..b06bcffc 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -919,7 +919,7 @@ def send_to_downloader(data, bestqual, album): # Set move-to directory if headphones.CONFIG.DELUGE_DONE_DIRECTORY: deluge.setTorrentPath({'hash': torrentid}) - + # I only just realized this function is useless... folder_name = deluge.getTorrentFolder({'hash': torrentid}) if folder_name: @@ -927,12 +927,12 @@ def send_to_downloader(data, bestqual, album): else: logger.error('Torrent folder name could not be determined') return - + except Exception as e: #exc_type, exc_obj, exc_tb = sys.exc_info() #fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] #print(exc_type, fname, exc_tb.tb_lineno) - logger.error( str(e) ) + logger.error(str(e)) else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 2: logger.info("Sending torrent to uTorrent") diff --git a/headphones/webserve.py b/headphones/webserve.py index a25e007f..1cfb929a 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1431,7 +1431,7 @@ class WebInterface(object): "synoindex_enabled", "pushover_enabled", "pushover_onsnatch", "pushbullet_enabled", "pushbullet_onsnatch", "subsonic_enabled", "twitter_enabled", "twitter_onsnatch", - "telegram_enabled", "telegram_onsnatch", + "telegram_enabled", "telegram_onsnatch", "osx_notify_enabled", "osx_notify_onsnatch", "boxcar_enabled", "boxcar_onsnatch", "songkick_enabled", "songkick_filter_enabled", "mpc_enabled", "email_enabled", "email_ssl", "email_tls", "email_onsnatch", From b5a9d525920afb75ee1abaa98487b3d0985d3328 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 4 Feb 2016 16:17:27 +0200 Subject: [PATCH 05/33] Fixed Whitespace Again - Oh Travis... --- headphones/deluge.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index fec96781..d960d73f 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -71,16 +71,13 @@ def addTorrent(link, data=None): if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] result = {'type': 'torrent', - 'name' : name, - 'content' : metainfo, - } + 'name': name, + 'content': metainfo} retid = _add_torrent_file(result) elif link.startswith('magnet:'): - result = { - 'type' : 'magnet', - 'url' : link, - } + result = {'type': 'magnet', + 'url': link} retid = _add_torrent_uri(result) else: logger.error('Deluge: Unknown file type - ' + str(link)) @@ -368,4 +365,4 @@ def addTorrentPause(result): return not json.loads(response.text)['error'] - return True \ No newline at end of file + return True From fd55e84274f91f21a8d712c9741df7dbed0064a3 Mon Sep 17 00:00:00 2001 From: Noam Date: Fri, 12 Feb 2016 13:16:22 +0200 Subject: [PATCH 06/33] Try to Download and Parse Torrents if Receives URL Deluge module will attempt to download any http(s) links it receives --- headphones/deluge.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/headphones/deluge.py b/headphones/deluge.py index d960d73f..4be0fa91 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -75,6 +75,37 @@ def addTorrent(link, data=None): 'content': metainfo} retid = _add_torrent_file(result) + elif link.startswith('http://') or link.startswith('https://'): + user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2243.2 Safari/537.36' + headers = { 'User-Agent': user_agent } + torrentfile = '' + r = requests.get(link, headers=headers) + if r.status_code == 200: + for chunk in r.iter_content(chunk_size=1024): + if chunk: # filter out keep-alive new chunks + torrent_file = torrentfile + chunk + else: + logger.debug('Deluge: Trying to GET ' + link + ' returned status ' + r.status_code) + return False + metainfo = str(base64.b64encode(torrentfile.decode('utf-8'))) + if 'announce' not in metainfo[:40]: + logger.debug('Deluge: Contents of ' + link + ' doesn\'t look like a torrent file') + return False + # Extract torrent name from .torrent + try: + name_length = int(re.findall('name([0-9]*)\:.*?\:', base64.b64encode(metainfo))[0]) + name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo))[0][:name_length] + except: + # get last part of link/path (name only) + name = link.split('\\')[-1].split('/')[-1] + # remove '.torrent' suffix + if name[-len('.torrent'):] == '.torrent': + name = name[:-len('.torrent')] + result = {'type': 'torrent', + 'name': name, + 'content': metainfo} + retid = _add_torrent_file(result) + elif link.startswith('magnet:'): result = {'type': 'magnet', 'url': link} From c492a4122c03ce758a44fd7bc5128cfde29d11c0 Mon Sep 17 00:00:00 2001 From: Noam Date: Fri, 12 Feb 2016 15:59:47 +0200 Subject: [PATCH 07/33] Travis fixes - Whitespace - Old variable name --- headphones/deluge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 4be0fa91..910f5a53 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -77,13 +77,13 @@ def addTorrent(link, data=None): elif link.startswith('http://') or link.startswith('https://'): user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2243.2 Safari/537.36' - headers = { 'User-Agent': user_agent } + headers = {'User-Agent': user_agent} torrentfile = '' r = requests.get(link, headers=headers) if r.status_code == 200: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks - torrent_file = torrentfile + chunk + torrentfile = torrentfile + chunk else: logger.debug('Deluge: Trying to GET ' + link + ' returned status ' + r.status_code) return False From 602dfbabc29d00b3094fceb57bc6d666212c3725 Mon Sep 17 00:00:00 2001 From: Noam Date: Sat, 13 Feb 2016 00:41:15 +0200 Subject: [PATCH 08/33] Encoding issues - UTF-8 encoding/decoding issues, removed conversion (for now...) - Noticed I was accidentally base64-encoding already-based64-encoded torrents --- headphones/deluge.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 910f5a53..9d84bd9f 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -62,8 +62,8 @@ def addTorrent(link, data=None): metainfo = str(base64.b64encode(f.read())) # Extract torrent name from .torrent try: - name_length = int(re.findall('name([0-9]*)\:.*?\:', base64.b64encode(metainfo))[0]) - name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo))[0][:name_length] + name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) + name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] except: # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] @@ -87,14 +87,14 @@ def addTorrent(link, data=None): else: logger.debug('Deluge: Trying to GET ' + link + ' returned status ' + r.status_code) return False - metainfo = str(base64.b64encode(torrentfile.decode('utf-8'))) - if 'announce' not in metainfo[:40]: + metainfo = str(base64.b64encode(torrentfile)) + if 'announce' not in torrentfile[:40]: logger.debug('Deluge: Contents of ' + link + ' doesn\'t look like a torrent file') return False # Extract torrent name from .torrent try: - name_length = int(re.findall('name([0-9]*)\:.*?\:', base64.b64encode(metainfo))[0]) - name = re.findall('name[0-9]*\:(.*?)\:', base64.b64encode(metainfo))[0][:name_length] + name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) + name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] except: # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] @@ -286,7 +286,7 @@ def _add_torrent_file(result): _get_auth() post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result['name'] + '.torrent', b64encode(result['content']), {}], + "params": [result['name'] + '.torrent', result['content'], {}], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] From 92cd62f0e94fbd2e1e917b7d396d45098b59bf34 Mon Sep 17 00:00:00 2001 From: Noam Date: Sat, 13 Feb 2016 12:07:24 +0200 Subject: [PATCH 09/33] Removed b64encode Import Already importing all of base64, silly me --- headphones/deluge.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 9d84bd9f..6874ce9e 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -45,8 +45,6 @@ import base64 import headphones import requests -from base64 import b64encode - delugeweb_auth = {} delugeweb_url = '' From 05df81be2fa3997804ee6b12ee9cbbaf964672ff Mon Sep 17 00:00:00 2001 From: Noam Date: Sat, 13 Feb 2016 12:17:00 +0200 Subject: [PATCH 10/33] Travis Not sure why Travis failed, let's try again --- headphones/deluge.py | 1 + 1 file changed, 1 insertion(+) diff --git a/headphones/deluge.py b/headphones/deluge.py index 6874ce9e..39e2a035 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -283,6 +283,7 @@ def _add_torrent_file(result): if not any(delugeweb_auth): _get_auth() + # content is already base64 encoded post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'] + '.torrent', result['content'], {}], "id": 2}) From 60f718c2cea5488bb628d4cf3caab27ddd97baee Mon Sep 17 00:00:00 2001 From: Noam Date: Sun, 14 Feb 2016 11:28:06 +0200 Subject: [PATCH 11/33] Temporary Debugging Print source of exception --- headphones/request.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/headphones/request.py b/headphones/request.py index e29ce794..4f59396b 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -123,6 +123,14 @@ def request_response(url, method="get", auto_raise=True, "Request raise HTTP error with status code %d (%s).", e.response.status_code, cause) + # REMOVE LATER - FOR DEBUGGING + super_debug = True + if super_debug: + import os + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logger.error('SUPER_DEBUG: ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) + # Debug response if headphones.VERBOSE: server_message(e.response) From c446dc263ad654eaae2557e2a5dafcc07420c467 Mon Sep 17 00:00:00 2001 From: Noam Date: Sun, 14 Feb 2016 11:31:23 +0200 Subject: [PATCH 12/33] Add Problematic URL to Debug --- headphones/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/request.py b/headphones/request.py index 4f59396b..7efd340e 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -129,7 +129,7 @@ def request_response(url, method="get", auto_raise=True, import os exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - logger.error('SUPER_DEBUG: ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) + logger.error('SUPER_DEBUG: ' + str(url) + ' -- ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) # Debug response if headphones.VERBOSE: From a2e7d4bdd1ea6184bcb1aa978934fc6084f56a28 Mon Sep 17 00:00:00 2001 From: Noam Date: Sun, 14 Feb 2016 15:08:56 +0200 Subject: [PATCH 13/33] Base64 Encode Received Torrent Binary and Updated Credits - Instead of base64-encoding the torrent binary data for each method (data/file/url/link), _add_torrent_file receives binary data and base64 encodes it - Today I found out SickRage split into two groups somewhere along the way (https://github.com/SickRage/sickrage-issues/issues/3), and since I used parts of the code from SickRage I thought the credits in the file should reflect its true author --- headphones/deluge.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 39e2a035..981ca8e3 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -14,10 +14,10 @@ # along with Headphones. If not, see . # Parts of this file are a part of SickRage. -# Author: echel0n -# URL: http://www.github.com/sickragetv/sickrage/ -# +# Author: Mr_Orange +# URL: http://code.google.com/p/sickbeard/ # Adapted for Headphones by +# URL: https://github.com/noam09 # # SickRage is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,11 +26,11 @@ # # SickRage 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 +# 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 SickRage. If not, see . +# along with SickRage. If not, see . from __future__ import unicode_literals @@ -41,9 +41,9 @@ import time import re import os import json -import base64 import headphones import requests +from base64 import b64encode delugeweb_auth = {} delugeweb_url = '' @@ -54,10 +54,10 @@ def addTorrent(link, data=None): retid = False if link.endswith('.torrent') or data: if data: - metainfo = str(base64.b64encode(data)) + torrentfile = data else: with open(link, 'rb') as f: - metainfo = str(base64.b64encode(f.read())) + torrentfile = f.read() # Extract torrent name from .torrent try: name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) @@ -70,7 +70,7 @@ def addTorrent(link, data=None): name = name[:-len('.torrent')] result = {'type': 'torrent', 'name': name, - 'content': metainfo} + 'content': torrentfile} retid = _add_torrent_file(result) elif link.startswith('http://') or link.startswith('https://'): @@ -85,7 +85,6 @@ def addTorrent(link, data=None): else: logger.debug('Deluge: Trying to GET ' + link + ' returned status ' + r.status_code) return False - metainfo = str(base64.b64encode(torrentfile)) if 'announce' not in torrentfile[:40]: logger.debug('Deluge: Contents of ' + link + ' doesn\'t look like a torrent file') return False @@ -101,7 +100,7 @@ def addTorrent(link, data=None): name = name[:-len('.torrent')] result = {'type': 'torrent', 'name': name, - 'content': metainfo} + 'content': torrentfile} retid = _add_torrent_file(result) elif link.startswith('magnet:'): @@ -283,9 +282,9 @@ def _add_torrent_file(result): if not any(delugeweb_auth): _get_auth() - # content is already base64 encoded + # content is torrent file contents that needs to be encoded to base64 post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result['name'] + '.torrent', result['content'], {}], + "params": [result['name'] + '.torrent', b64encode(result['content']), {}], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] From 86fb9bbd6c9b8043919847a7a5632579e71e9a7c Mon Sep 17 00:00:00 2001 From: Noam Date: Sun, 14 Feb 2016 18:56:10 +0200 Subject: [PATCH 14/33] Update README.md Deluge support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f82e75a5..87eb2f36 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ **Master Branch:** [![Build Status](https://travis-ci.org/rembo10/headphones.svg?branch=master)](https://travis-ci.org/rembo10/headphones) **Develop Branch:** [![Build Status](https://travis-ci.org/rembo10/headphones.svg?branch=develop)](https://travis-ci.org/rembo10/headphones) -Headphones is an automated music downloader for NZB and Torrent, written in Python. It supports SABnzbd, NZBget, Transmission, µTorrent and Blackhole. +Headphones is an automated music downloader for NZB and Torrent, written in Python. It supports SABnzbd, NZBget, Transmission, µTorrent, Deluge and Blackhole. ## Support & Discuss You are free to join the Headphones support community on IRC where you can ask questions, hang around and discuss anything related to HP. From ef9a2015eba4be75ed160a90cb08e1299c00126e Mon Sep 17 00:00:00 2001 From: Noam Date: Tue, 16 Feb 2016 21:04:42 +0200 Subject: [PATCH 15/33] Added debugging for tests and fixed setTorrentPause --- headphones/deluge.py | 2 +- headphones/searcher.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 981ca8e3..b0517b72 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -381,7 +381,7 @@ def setTorrentPath(result): return True -def addTorrentPause(result): +def setTorrentPause(result): if not any(delugeweb_auth): _get_auth() diff --git a/headphones/searcher.py b/headphones/searcher.py index b06bcffc..61ab99d8 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -929,9 +929,14 @@ def send_to_downloader(data, bestqual, album): return except Exception as e: - #exc_type, exc_obj, exc_tb = sys.exc_info() - #fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - #print(exc_type, fname, exc_tb.tb_lineno) + # REMOVE LATER - FOR DEBUGGING + super_debug = True + if super_debug: + import sys + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logger.error('SUPER_DEBUG: ' + str(e) + ' -- ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) + ###### logger.error(str(e)) else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 2: From 20baed87ff3c3d8b2197578988e40c5ca43c0e6f Mon Sep 17 00:00:00 2001 From: Noam Date: Tue, 16 Feb 2016 21:18:54 +0200 Subject: [PATCH 16/33] More debugging --- headphones/deluge.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/headphones/deluge.py b/headphones/deluge.py index b0517b72..03fb2dc2 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -44,6 +44,8 @@ import json import headphones import requests from base64 import b64encode +# remove later +import sys delugeweb_auth = {} delugeweb_url = '' @@ -118,6 +120,13 @@ def addTorrent(link, data=None): return False except Exception, e: + # REMOVE LATER - FOR DEBUGGING + super_debug = True + if super_debug: + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + logger.error('SUPER_DEBUG: ' + str(e) + ' -- ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) + ###### logger.error(str(e)) def getTorrentFolder(result): From c445a0732fd3194a9cb53812d53c2c248ec40765 Mon Sep 17 00:00:00 2001 From: Noam Date: Tue, 16 Feb 2016 22:06:49 +0200 Subject: [PATCH 17/33] Added Logging For debugging --- headphones/deluge.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 03fb2dc2..52d572b4 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -56,31 +56,40 @@ def addTorrent(link, data=None): retid = False if link.endswith('.torrent') or data: if data: + logger.debug('Deluge: Getting .torrent data') torrentfile = data else: + logger.debug('Deluge: Getting .torrent file') with open(link, 'rb') as f: torrentfile = f.read() # Extract torrent name from .torrent try: + logger.debug('Deluge: Getting torrent name length') name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) + logger.debug('Deluge: Getting torrent name') name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] except: + logger.debug('Deluge: Could not get torrent name, getting file name') # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] # remove '.torrent' suffix if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] + logger.debug('Deluge: Sending Deluge torrent with name ' + name) result = {'type': 'torrent', 'name': name, 'content': torrentfile} retid = _add_torrent_file(result) elif link.startswith('http://') or link.startswith('https://'): + logger.debug('Deluge: Got a URL: ' + link) user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2243.2 Safari/537.36' headers = {'User-Agent': user_agent} torrentfile = '' + logger.debug('Deluge: Trying to download (GET)') r = requests.get(link, headers=headers) if r.status_code == 200: + logger.debug('Deluge: 200 OK') for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks torrentfile = torrentfile + chunk @@ -92,20 +101,25 @@ def addTorrent(link, data=None): return False # Extract torrent name from .torrent try: + logger.debug('Deluge: Getting torrent name length') name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) + logger.debug('Deluge: Getting torrent name') name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] except: + logger.debug('Deluge: Could not get torrent name, getting file name') # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] # remove '.torrent' suffix if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] + logger.debug('Deluge: Sending Deluge torrent with name ' + name) result = {'type': 'torrent', 'name': name, 'content': torrentfile} retid = _add_torrent_file(result) elif link.startswith('magnet:'): + logger.debug('Deluge: Got a magnet link: ' + link) result = {'type': 'magnet', 'url': link} retid = _add_torrent_uri(result) @@ -191,7 +205,7 @@ def removeTorrent(torrentid, remove_data=False): return result def _get_auth(): - + logger.debug('Deluge: Authenticating...') global delugeweb_auth, delugeweb_url delugeweb_auth = {} @@ -274,7 +288,7 @@ def _get_auth(): return auth def _add_torrent_uri(result): - + logger.debug('Deluge: Adding URI') if not any(delugeweb_auth): _get_auth() @@ -283,11 +297,11 @@ def _add_torrent_uri(result): "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - + logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) return json.loads(response.text)['result'] def _add_torrent_file(result): - + logger.debug('Deluge: Adding file') if not any(delugeweb_auth): _get_auth() @@ -297,11 +311,11 @@ def _add_torrent_file(result): "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - + logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) return json.loads(response.text)['result'] def setTorrentLabel(result): - + logger.debug('Deluge: Setting label') label = headphones.CONFIG.DELUGE_LABEL if not any(delugeweb_auth): @@ -340,7 +354,7 @@ def setTorrentLabel(result): return not json.loads(response.text)['error'] def setSeedRatio(result): - + logger.debug('Deluge: Setting seed ratio') if not any(delugeweb_auth): _get_auth() @@ -363,7 +377,7 @@ def setSeedRatio(result): return True def setTorrentPath(result): - + logger.debug('Deluge: Setting download path') if not any(delugeweb_auth): _get_auth() @@ -391,7 +405,7 @@ def setTorrentPath(result): return True def setTorrentPause(result): - + logger.debug('Deluge: Pausing torrent') if not any(delugeweb_auth): _get_auth() From 5adf684dbeda3af8f18884087951442093dcfc4c Mon Sep 17 00:00:00 2001 From: Noam Date: Wed, 17 Feb 2016 10:58:29 +0200 Subject: [PATCH 18/33] More Logging - Adding torrent by URL via Deluge still commented out because it doesn't work with all links (probably because of user-agent) - String formatting - Removed debug bits - Log exceptions --- headphones/deluge.py | 186 ++++++++++++++++++++++------------------- headphones/request.py | 8 -- headphones/searcher.py | 10 +-- 3 files changed, 102 insertions(+), 102 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 52d572b4..a33d4fd5 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -44,8 +44,6 @@ import json import headphones import requests from base64 import b64encode -# remove later -import sys delugeweb_auth = {} delugeweb_url = '' @@ -75,7 +73,7 @@ def addTorrent(link, data=None): # remove '.torrent' suffix if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] - logger.debug('Deluge: Sending Deluge torrent with name ' + name) + logger.debug('Deluge: Sending Deluge torrent with name %s' % name) result = {'type': 'torrent', 'name': name, 'content': torrentfile} @@ -87,17 +85,20 @@ def addTorrent(link, data=None): headers = {'User-Agent': user_agent} torrentfile = '' logger.debug('Deluge: Trying to download (GET)') - r = requests.get(link, headers=headers) - if r.status_code == 200: - logger.debug('Deluge: 200 OK') - for chunk in r.iter_content(chunk_size=1024): - if chunk: # filter out keep-alive new chunks - torrentfile = torrentfile + chunk - else: - logger.debug('Deluge: Trying to GET ' + link + ' returned status ' + r.status_code) - return False + try: + r = requests.get(link, headers=headers) + if r.status_code == 200: + logger.debug('Deluge: 200 OK') + for chunk in r.iter_content(chunk_size=1024): + if chunk: # filter out keep-alive new chunks + torrentfile = torrentfile + chunk + else: + logger.debug('Deluge: Trying to GET %s returned status %d' % (link, r.status_code)) + return False + except Exception as e: + logger.debug('Deluge: Download failed: ' + str(e)) if 'announce' not in torrentfile[:40]: - logger.debug('Deluge: Contents of ' + link + ' doesn\'t look like a torrent file') + logger.debug('Deluge: Contents of %s doesn\'t look like a torrent file' % link) return False # Extract torrent name from .torrent try: @@ -112,80 +113,75 @@ def addTorrent(link, data=None): # remove '.torrent' suffix if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] - logger.debug('Deluge: Sending Deluge torrent with name ' + name) + logger.debug('Deluge: Sending Deluge torrent with name %s' % name) result = {'type': 'torrent', 'name': name, 'content': torrentfile} retid = _add_torrent_file(result) elif link.startswith('magnet:'): - logger.debug('Deluge: Got a magnet link: ' + link) + logger.debug('Deluge: Got a magnet link: %s' % link) result = {'type': 'magnet', 'url': link} - retid = _add_torrent_uri(result) + retid = _add_torrent_magnet(result) else: - logger.error('Deluge: Unknown file type - ' + str(link)) + logger.error('Deluge: Unknown file type: %s' % link) if retid: - logger.info(u"Torrent sent to Deluge successfully") + logger.info('Deluge: Torrent sent to Deluge successfully (%s)' % retid) return retid else: logger.info('Deluge returned status %s' % retid) return False - except Exception, e: - # REMOVE LATER - FOR DEBUGGING - super_debug = True - if super_debug: - exc_type, exc_obj, exc_tb = sys.exc_info() - fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - logger.error('SUPER_DEBUG: ' + str(e) + ' -- ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) - ###### + except Exception as e: logger.error(str(e)) def getTorrentFolder(result): - + logger.debug('Deluge: Get torrent folder name') if not any(delugeweb_auth): _get_auth() - post_data = json.dumps({"method": "web.get_torrent_status", - "params": [ - result['hash'], - ["total_done"] - ], - "id": 22}) - - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - result['total_done'] = json.loads(response.text)['result']['total_done'] - - tries = 0 - while result['total_done'] == 0 and tries < 10: - tries += 1 - time.sleep(5) + try: + post_data = json.dumps({"method": "web.get_torrent_status", + "params": [ + result['hash'], + ["total_done"] + ], + "id": 22}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['total_done'] = json.loads(response.text)['result']['total_done'] - post_data = json.dumps({"method": "web.get_torrent_status", - "params": [ - result['hash'], - [ - "name", - "save_path", - "total_size", - "num_files", - "message", - "tracker", - "comment" - ] - ], - "id": 23}) + tries = 0 + while result['total_done'] == 0 and tries < 10: + tries += 1 + time.sleep(5) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result['total_done'] = json.loads(response.text)['result']['total_done'] - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + post_data = json.dumps({"method": "web.get_torrent_status", + "params": [ + result['hash'], + [ + "name", + "save_path", + "total_size", + "num_files", + "message", + "tracker", + "comment" + ] + ], + "id": 23}) - result['save_path'] = json.loads(response.text)['result']['save_path'] - result['name'] = json.loads(response.text)['result']['name'] + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - return json.loads(response.text)['result']['name'] + result['save_path'] = json.loads(response.text)['result']['save_path'] + result['name'] = json.loads(response.text)['result']['name'] + + return json.loads(response.text)['result']['name'] + except Exception as e: + logger.debug('Deluge: Could not get torrent folder name: %s' % str(e)) def removeTorrent(torrentid, remove_data=False): @@ -210,11 +206,10 @@ def _get_auth(): delugeweb_auth = {} delugeweb_host = headphones.CONFIG.DELUGE_HOST - # delugeweb_username = headphones.CONFIG.DELUGE_USERNAME delugeweb_password = headphones.CONFIG.DELUGE_PASSWORD if not delugeweb_host.startswith('http'): - delugeweb_host = 'http://' + delugeweb_host + delugeweb_host = 'http://%s' % delugeweb_host if delugeweb_host.endswith('/'): delugeweb_host = delugeweb_host[:-1] @@ -287,32 +282,53 @@ def _get_auth(): return auth -def _add_torrent_uri(result): - logger.debug('Deluge: Adding URI') +def _add_torrent_magnet(result): + logger.debug('Deluge: Adding magnet') if not any(delugeweb_auth): _get_auth() + try: + post_data = json.dumps({"method": "core.add_torrent_magnet", + "params": [result['url'], {}], + "id": 2}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result['hash'] = json.loads(response.text)['result'] + logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) + return json.loads(response.text)['result'] + except Exception as e: + logger.error('Deluge: Adding torrent magnet failed: %s' % str(e)) - post_data = json.dumps({"method": "core.add_torrent_magnet", - "params": [result['url'], {}], - "id": 2}) - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) - return json.loads(response.text)['result'] +''' +def _add_torrent_url(result): + logger.debug('Deluge: Adding URL') + if not any(delugeweb_auth): + _get_auth() + try: + post_data = json.dumps({"method": "web.download_torrent_from_url", + "params": [result['url'], {}], + "id": 2}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result['hash'] = json.loads(response.text)['result'] + logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) + return json.loads(response.text)['result'] + except Exception as e: + logger.error('Deluge: Adding torrent URL failed: %s' % str(e)) +''' def _add_torrent_file(result): logger.debug('Deluge: Adding file') if not any(delugeweb_auth): _get_auth() - - # content is torrent file contents that needs to be encoded to base64 - post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result['name'] + '.torrent', b64encode(result['content']), {}], - "id": 2}) - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) - return json.loads(response.text)['result'] + try: + # content is torrent file contents that needs to be encoded to base64 + post_data = json.dumps({"method": "core.add_torrent_file", + "params": [result['name'] + '.torrent', b64encode(result['content']), {}], + "id": 2}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + result['hash'] = json.loads(response.text)['result'] + logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) + return json.loads(response.text)['result'] + except Exception as e: + logger.error('Deluge: Adding torrent file failed: %s' % str(e)) def setTorrentLabel(result): logger.debug('Deluge: Setting label') @@ -322,7 +338,7 @@ def setTorrentLabel(result): _get_auth() if ' ' in label: - logger.error('Deluge: Invalid label. Label must not contain a space - replacing with underscores') + logger.error('Deluge: Invalid label. Label can\'t contain spaces - replacing with underscores') label = label.replace(' ', '_') if label: # check if label already exists and create it if not @@ -334,21 +350,21 @@ def setTorrentLabel(result): if labels is not None: if label not in labels: - logger.debug('Deluge: ' + label + " label does not exist in Deluge we must add it") + logger.debug('Deluge: %s label doesn\'t exist in Deluge, let\'s add it' % label) post_data = json.dumps({"method": 'label.add', "params": [label], "id": 4}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - logger.debug('Deluge: ' + label + " label added to Deluge") + logger.debug('Deluge: %s label added to Deluge' % label) # add label to torrent post_data = json.dumps({"method": 'label.set_torrent', "params": [result['hash'], label], "id": 5}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - logger.debug('Deluge: ' + label + " label added to torrent") + logger.debug('Deluge: %s label added to torrent' % label) else: - logger.debug('Deluge: ' + "label plugin not detected") + logger.debug('Deluge: Label plugin not detected') return False return not json.loads(response.text)['error'] @@ -393,7 +409,7 @@ def setTorrentPath(result): move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR if not os.path.exists(move_to): - logger.debug("Deluge: " + move_to + " directory doesn't exist, let's create it") + logger.debug('Deluge: %s directory doesn\'t exist, let\'s create it' % move_to) os.makedirs(move_to) post_data = json.dumps({"method": "core.set_torrent_move_completed_path", "params": [result['hash'], move_to], diff --git a/headphones/request.py b/headphones/request.py index 7efd340e..e29ce794 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -123,14 +123,6 @@ def request_response(url, method="get", auto_raise=True, "Request raise HTTP error with status code %d (%s).", e.response.status_code, cause) - # REMOVE LATER - FOR DEBUGGING - super_debug = True - if super_debug: - import os - exc_type, exc_obj, exc_tb = sys.exc_info() - fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - logger.error('SUPER_DEBUG: ' + str(url) + ' -- ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) - # Debug response if headphones.VERBOSE: server_message(e.response) diff --git a/headphones/searcher.py b/headphones/searcher.py index 61ab99d8..5c8b4144 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -929,15 +929,7 @@ def send_to_downloader(data, bestqual, album): return except Exception as e: - # REMOVE LATER - FOR DEBUGGING - super_debug = True - if super_debug: - import sys - exc_type, exc_obj, exc_tb = sys.exc_info() - fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - logger.error('SUPER_DEBUG: ' + str(e) + ' -- ' + '; '.join([exc_type, fname, exc_tb.tb_lineno])) - ###### - logger.error(str(e)) + logger.error('Error sending torrent to Deluge: %s' % str(e)) else: # if headphones.CONFIG.TORRENT_DOWNLOADER == 2: logger.info("Sending torrent to uTorrent") From da5ac9757a6b90cdd4243370574eda5d24f95bd3 Mon Sep 17 00:00:00 2001 From: Noam Date: Wed, 17 Feb 2016 16:20:37 +0200 Subject: [PATCH 19/33] Remove requests iter_content - inter_content might have been the reason for repeated encoding issues - let's try with good old r.text --- headphones/deluge.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index a33d4fd5..3c29b019 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -89,9 +89,10 @@ def addTorrent(link, data=None): r = requests.get(link, headers=headers) if r.status_code == 200: logger.debug('Deluge: 200 OK') - for chunk in r.iter_content(chunk_size=1024): - if chunk: # filter out keep-alive new chunks - torrentfile = torrentfile + chunk + torrentfile = r.text + #for chunk in r.iter_content(chunk_size=1024): + # if chunk: # filter out keep-alive new chunks + # torrentfile = torrentfile + chunk else: logger.debug('Deluge: Trying to GET %s returned status %d' % (link, r.status_code)) return False From 6218734783a5bed43b41737926581d388a345f47 Mon Sep 17 00:00:00 2001 From: Noam Date: Wed, 17 Feb 2016 17:22:59 +0200 Subject: [PATCH 20/33] Encoding Change and More String Formatting - Try base64 encoding utf-8 if ascii fails - More string formatting --- headphones/deluge.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 3c29b019..8ad88b69 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -80,7 +80,7 @@ def addTorrent(link, data=None): retid = _add_torrent_file(result) elif link.startswith('http://') or link.startswith('https://'): - logger.debug('Deluge: Got a URL: ' + link) + logger.debug('Deluge: Got a URL: %s' % link) user_agent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2243.2 Safari/537.36' headers = {'User-Agent': user_agent} torrentfile = '' @@ -97,7 +97,7 @@ def addTorrent(link, data=None): logger.debug('Deluge: Trying to GET %s returned status %d' % (link, r.status_code)) return False except Exception as e: - logger.debug('Deluge: Download failed: ' + str(e)) + logger.debug('Deluge: Download failed: %s' % str(e)) if 'announce' not in torrentfile[:40]: logger.debug('Deluge: Contents of %s doesn\'t look like a torrent file' % link) return False @@ -293,7 +293,7 @@ def _add_torrent_magnet(result): "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) + logger.debug('Deluge: Response was %s' % json.loads(response.text)['result']) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent magnet failed: %s' % str(e)) @@ -309,7 +309,7 @@ def _add_torrent_url(result): "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) + logger.debug('Deluge: Response was %s' % json.loads(response.text)['result']) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent URL failed: %s' % str(e)) @@ -321,9 +321,15 @@ def _add_torrent_file(result): _get_auth() try: # content is torrent file contents that needs to be encoded to base64 - post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result['name'] + '.torrent', b64encode(result['content']), {}], - "id": 2}) + try: + post_data = json.dumps({"method": "core.add_torrent_file", + "params": [result['name'] + '.torrent', b64encode(result['content']), {}], + "id": 2}) + except Exception as e: + logger.error('Deluge: Encoding issue? Trying again with UTF8 (%s)' % str(e)) + post_data = json.dumps({"method": "core.add_torrent_file", + "params": [result['name'] + '.torrent', b64encode(result['content'].encode('utf-8')), {}], + "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) From 0e334a8f927f4f5572c1b0d1cb86ca5c8249e1ba Mon Sep 17 00:00:00 2001 From: Noam Date: Wed, 17 Feb 2016 17:28:19 +0200 Subject: [PATCH 21/33] Label Debugging - Labels not set properly, need log --- headphones/deluge.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 8ad88b69..5d7db316 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -357,12 +357,15 @@ def setTorrentLabel(result): if labels is not None: if label not in labels: - logger.debug('Deluge: %s label doesn\'t exist in Deluge, let\'s add it' % label) - post_data = json.dumps({"method": 'label.add', - "params": [label], - "id": 4}) - response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) - logger.debug('Deluge: %s label added to Deluge' % label) + try: + logger.debug('Deluge: %s label doesn\'t exist in Deluge, let\'s add it' % label) + post_data = json.dumps({"method": 'label.add', + "params": [label], + "id": 4}) + response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) + logger.debug('Deluge: %s label added to Deluge' % label) + except: + logger.error('Deluge: Setting label failed: %s' % str(e)) # add label to torrent post_data = json.dumps({"method": 'label.set_torrent', From 876b6402c47831408acb3e400b1fea72f43ef98b Mon Sep 17 00:00:00 2001 From: Noam Date: Wed, 17 Feb 2016 17:55:46 +0200 Subject: [PATCH 22/33] Better URL Checking and Logging - Improved if/else so that URLs won't end up in the file/data opener - Log torrent file content --- headphones/deluge.py | 55 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 5d7db316..1c20b8de 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -52,32 +52,33 @@ def addTorrent(link, data=None): try: result = {} retid = False - if link.endswith('.torrent') or data: - if data: - logger.debug('Deluge: Getting .torrent data') - torrentfile = data - else: - logger.debug('Deluge: Getting .torrent file') - with open(link, 'rb') as f: - torrentfile = f.read() - # Extract torrent name from .torrent - try: - logger.debug('Deluge: Getting torrent name length') - name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) - logger.debug('Deluge: Getting torrent name') - name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] - except: - logger.debug('Deluge: Could not get torrent name, getting file name') - # get last part of link/path (name only) - name = link.split('\\')[-1].split('/')[-1] - # remove '.torrent' suffix - if name[-len('.torrent'):] == '.torrent': - name = name[:-len('.torrent')] - logger.debug('Deluge: Sending Deluge torrent with name %s' % name) - result = {'type': 'torrent', - 'name': name, - 'content': torrentfile} - retid = _add_torrent_file(result) + if not (link.startswith('http://') or link.startswith('https://')): + if link.endswith('.torrent') or data: + if data: + logger.debug('Deluge: Getting .torrent data') + torrentfile = data + else: + logger.debug('Deluge: Getting .torrent file') + with open(link, 'rb') as f: + torrentfile = f.read() + # Extract torrent name from .torrent + try: + logger.debug('Deluge: Getting torrent name length') + name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) + logger.debug('Deluge: Getting torrent name') + name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] + except: + logger.debug('Deluge: Could not get torrent name, getting file name') + # get last part of link/path (name only) + name = link.split('\\')[-1].split('/')[-1] + # remove '.torrent' suffix + if name[-len('.torrent'):] == '.torrent': + name = name[:-len('.torrent')] + logger.debug('Deluge: Sending Deluge torrent with name %s and content [%s...]' % (name, torrentfile[:40])) + result = {'type': 'torrent', + 'name': name, + 'content': torrentfile} + retid = _add_torrent_file(result) elif link.startswith('http://') or link.startswith('https://'): logger.debug('Deluge: Got a URL: %s' % link) @@ -114,7 +115,7 @@ def addTorrent(link, data=None): # remove '.torrent' suffix if name[-len('.torrent'):] == '.torrent': name = name[:-len('.torrent')] - logger.debug('Deluge: Sending Deluge torrent with name %s' % name) + logger.debug('Deluge: Sending Deluge torrent with name %s and content [%s...]' % (name, torrentfile[:40])) result = {'type': 'torrent', 'name': name, 'content': torrentfile} From 396e75ed2d175b3d22f3bb8e5a7f2d88f1821a44 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 18 Feb 2016 17:02:44 +0200 Subject: [PATCH 23/33] Exception Debugging - Add traceback output to log --- headphones/deluge.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index 1c20b8de..e10061e0 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -44,6 +44,7 @@ import json import headphones import requests from base64 import b64encode +import traceback delugeweb_auth = {} delugeweb_url = '' @@ -67,7 +68,7 @@ def addTorrent(link, data=None): name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) logger.debug('Deluge: Getting torrent name') name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] - except: + except Exception as e: logger.debug('Deluge: Could not get torrent name, getting file name') # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] @@ -108,7 +109,7 @@ def addTorrent(link, data=None): name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) logger.debug('Deluge: Getting torrent name') name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] - except: + except Exception as e: logger.debug('Deluge: Could not get torrent name, getting file name') # get last part of link/path (name only) name = link.split('\\')[-1].split('/')[-1] @@ -138,6 +139,8 @@ def addTorrent(link, data=None): except Exception as e: logger.error(str(e)) + formatted_lines = traceback.format_exc().splitlines() + logger.error('; '.join(formatted_lines)) def getTorrentFolder(result): logger.debug('Deluge: Get torrent folder name') @@ -328,6 +331,8 @@ def _add_torrent_file(result): "id": 2}) except Exception as e: logger.error('Deluge: Encoding issue? Trying again with UTF8 (%s)' % str(e)) + formatted_lines = traceback.format_exc().splitlines() + logger.error('; '.join(formatted_lines)) post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'] + '.torrent', b64encode(result['content'].encode('utf-8')), {}], "id": 2}) @@ -337,6 +342,8 @@ def _add_torrent_file(result): return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent file failed: %s' % str(e)) + formatted_lines = traceback.format_exc().splitlines() + logger.error('; '.join(formatted_lines)) def setTorrentLabel(result): logger.debug('Deluge: Setting label') @@ -365,8 +372,10 @@ def setTorrentLabel(result): "id": 4}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) logger.debug('Deluge: %s label added to Deluge' % label) - except: + except Exception as e: logger.error('Deluge: Setting label failed: %s' % str(e)) + formatted_lines = traceback.format_exc().splitlines() + logger.error('; '.join(formatted_lines)) # add label to torrent post_data = json.dumps({"method": 'label.set_torrent', From 41aee3d6bb8a16a5aa50766410b66ddbb6b84642 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 18 Feb 2016 19:35:48 +0200 Subject: [PATCH 24/33] Encode UTF-8 Before Base64ing - I was using UTF-8 when I started out, don't know why I changed it back then... Oh well, hopefully this solves the encoding problems (note to self: you can't base64 unicode) --- headphones/deluge.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index e10061e0..d51ba678 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -297,7 +297,7 @@ def _add_torrent_magnet(result): "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was %s' % json.loads(response.text)['result']) + logger.debug('Deluge: Response was %s' % str(json.loads(response.text)['result'])) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent magnet failed: %s' % str(e)) @@ -313,7 +313,7 @@ def _add_torrent_url(result): "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was %s' % json.loads(response.text)['result']) + logger.debug('Deluge: Response was %s' % str(json.loads(response.text)['result'])) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent URL failed: %s' % str(e)) @@ -325,20 +325,12 @@ def _add_torrent_file(result): _get_auth() try: # content is torrent file contents that needs to be encoded to base64 - try: - post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result['name'] + '.torrent', b64encode(result['content']), {}], - "id": 2}) - except Exception as e: - logger.error('Deluge: Encoding issue? Trying again with UTF8 (%s)' % str(e)) - formatted_lines = traceback.format_exc().splitlines() - logger.error('; '.join(formatted_lines)) - post_data = json.dumps({"method": "core.add_torrent_file", - "params": [result['name'] + '.torrent', b64encode(result['content'].encode('utf-8')), {}], - "id": 2}) + post_data = json.dumps({"method": "core.add_torrent_file", + "params": [result['name'] + '.torrent', b64encode(result['content'].encode('utf8')), {}], + "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['hash'] = json.loads(response.text)['result'] - logger.debug('Deluge: Response was ' + json.loads(response.text)['result']) + logger.debug('Deluge: Response was %s' % str(json.loads(response.text)['result'])) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent file failed: %s' % str(e)) From bd74d21c839b526377bb234ab3df2895d8812cc4 Mon Sep 17 00:00:00 2001 From: Noam Date: Fri, 19 Feb 2016 11:38:37 +0200 Subject: [PATCH 25/33] Moved Around Torrent-Type Checking - From local/data->URL>magnet to magnet->URL->local/data. I feel stupid. --- headphones/deluge.py | 66 +++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/headphones/deluge.py b/headphones/deluge.py index d51ba678..9f999924 100644 --- a/headphones/deluge.py +++ b/headphones/deluge.py @@ -53,33 +53,12 @@ def addTorrent(link, data=None): try: result = {} retid = False - if not (link.startswith('http://') or link.startswith('https://')): - if link.endswith('.torrent') or data: - if data: - logger.debug('Deluge: Getting .torrent data') - torrentfile = data - else: - logger.debug('Deluge: Getting .torrent file') - with open(link, 'rb') as f: - torrentfile = f.read() - # Extract torrent name from .torrent - try: - logger.debug('Deluge: Getting torrent name length') - name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) - logger.debug('Deluge: Getting torrent name') - name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] - except Exception as e: - logger.debug('Deluge: Could not get torrent name, getting file name') - # get last part of link/path (name only) - name = link.split('\\')[-1].split('/')[-1] - # remove '.torrent' suffix - if name[-len('.torrent'):] == '.torrent': - name = name[:-len('.torrent')] - logger.debug('Deluge: Sending Deluge torrent with name %s and content [%s...]' % (name, torrentfile[:40])) - result = {'type': 'torrent', - 'name': name, - 'content': torrentfile} - retid = _add_torrent_file(result) + + if link.startswith('magnet:'): + logger.debug('Deluge: Got a magnet link: %s' % link) + result = {'type': 'magnet', + 'url': link} + retid = _add_torrent_magnet(result) elif link.startswith('http://') or link.startswith('https://'): logger.debug('Deluge: Got a URL: %s' % link) @@ -122,11 +101,34 @@ def addTorrent(link, data=None): 'content': torrentfile} retid = _add_torrent_file(result) - elif link.startswith('magnet:'): - logger.debug('Deluge: Got a magnet link: %s' % link) - result = {'type': 'magnet', - 'url': link} - retid = _add_torrent_magnet(result) + elif not (link.startswith('http://') or link.startswith('https://')): + #elif link.endswith('.torrent') or data: + if data: + logger.debug('Deluge: Getting .torrent data') + torrentfile = data + else: + logger.debug('Deluge: Getting .torrent file') + with open(link, 'rb') as f: + torrentfile = f.read() + # Extract torrent name from .torrent + try: + logger.debug('Deluge: Getting torrent name length') + name_length = int(re.findall('name([0-9]*)\:.*?\:', torrentfile)[0]) + logger.debug('Deluge: Getting torrent name') + name = re.findall('name[0-9]*\:(.*?)\:', torrentfile)[0][:name_length] + except Exception as e: + logger.debug('Deluge: Could not get torrent name, getting file name') + # get last part of link/path (name only) + name = link.split('\\')[-1].split('/')[-1] + # remove '.torrent' suffix + if name[-len('.torrent'):] == '.torrent': + name = name[:-len('.torrent')] + logger.debug('Deluge: Sending Deluge torrent with name %s and content [%s...]' % (name, torrentfile[:40])) + result = {'type': 'torrent', + 'name': name, + 'content': torrentfile} + retid = _add_torrent_file(result) + else: logger.error('Deluge: Unknown file type: %s' % link) From 89d5649b4fb695b7b10a1612b75e8f60d5d20ed2 Mon Sep 17 00:00:00 2001 From: Andrzej Ciarkowski Date: Mon, 22 Feb 2016 20:11:38 +0100 Subject: [PATCH 26/33] config.html: Add explanation text for optional elements and single-quote escapes This is follow up to the change introducing "pathrender.py" and extended path formatting syntax. --- data/interfaces/default/config.html | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 05109089..8c43f678 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -858,7 +858,7 @@
as .jpg
- Use $Artist/$artist, $Album/$album, $Year/$year + Use $Artist/$artist, $Album/$album, $Year/$year, put optional variables in square brackets, use single-quote marks to escape square brackets literally ('[', ']').