From a92d13652dc7c7f0f150a622e1ba6c6bd5c57b35 Mon Sep 17 00:00:00 2001 From: Ade Date: Fri, 6 Feb 2015 20:28:57 +1300 Subject: [PATCH 01/21] Email notifications #1045 --- data/interfaces/default/config.html | 53 +++++++++++++++++++++++++++++ headphones/config.py | 9 +++++ headphones/notifiers.py | 33 ++++++++++++++++++ headphones/postprocessor.py | 6 ++++ headphones/searcher.py | 7 +++- headphones/webserve.py | 13 +++++-- 6 files changed, 118 insertions(+), 3 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 3b72169d..28c5dd4c 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1104,6 +1104,39 @@ +
+

Email

+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
@@ -1866,6 +1899,26 @@ } }); + if ($("#email").is(":checked")) + { + $("#email_options").show(); + } + else + { + $("#email_options").hide(); + } + + $("#email").click(function(){ + if ($("#email").is(":checked")) + { + $("#email_options").slideDown(); + } + else + { + $("#email_options").slideUp(); + } + }); + if ($("#songkick").is(":checked")) { $("#songkickoptions").show(); diff --git a/headphones/config.py b/headphones/config.py index 4071edd7..741ba9cd 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -54,6 +54,15 @@ _CONFIG_DEFINITIONS = { 'DOWNLOAD_SCAN_INTERVAL': (int, 'General', 5), 'DOWNLOAD_TORRENT_DIR': (str, 'General', ''), 'DO_NOT_OVERRIDE_GIT_BRANCH': (int, 'General', 0), + 'EMAIL_ENABLED': (int, 'Email', 0), + 'EMAIL_FROM': (str, 'Email', ''), + 'EMAIL_TO': (str, 'Email', ''), + 'EMAIL_SMTP_SERVER': (str, 'Email', ''), + 'EMAIL_SMTP_USER': (str, 'Email', ''), + 'EMAIL_SMTP_PASSWORD': (str, 'Email', ''), + 'EMAIL_SMTP_PORT': (int, 'Email', 25), + 'EMAIL_TLS': (int, 'Email', 0), + 'EMAIL_ONSNATCH': (int, 'Email', 0), 'EMBED_ALBUM_ART': (int, 'General', 0), 'EMBED_LYRICS': (int, 'General', 0), 'ENABLE_HTTPS': (int, 'General', 0), diff --git a/headphones/notifiers.py b/headphones/notifiers.py index e8503b53..9631660d 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -34,6 +34,11 @@ import json import oauth2 as oauth import pythontwitter as twitter +from email.mime.text import MIMEText +import smtplib +import email.utils + + class GROWL(object): """ Growl notifications, for OS X. @@ -827,3 +832,31 @@ class SubSonicNotifier(object): # Invoke request request.request_response(self.host + "musicFolderSettings.view?scanNow", auth=(self.username, self.password)) + +class Email(object): + + def notify(self, subject, message): + + message = MIMEText(message, 'plain', "utf-8") + message['Subject'] = subject + message['From'] = email.utils.formataddr(('Headphones', headphones.CONFIG.EMAIL_FROM)) + message['To'] = headphones.CONFIG.EMAIL_TO + + try: + mailserver = smtplib.SMTP(headphones.CONFIG.EMAIL_SMTP_SERVER, headphones.CONFIG.EMAIL_SMTP_PORT) + + if (headphones.CONFIG.EMAIL_TLS): + mailserver.starttls() + + mailserver.ehlo() + + if headphones.CONFIG.EMAIL_SMTP_USER: + mailserver.login(headphones.CONFIG.EMAIL_SMTP_USER, headphones.CONFIG.EMAIL_SMTP_PASSWORD) + + mailserver.sendmail(headphones.CONFIG.EMAIL_FROM, headphones.CONFIG.EMAIL_TO, message.as_string()) + mailserver.quit() + return True + + except Exception, e: + logger.warn('Error sending Email: %s' % e) + return False \ No newline at end of file diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 5bf06d99..b9f230ff 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -513,6 +513,12 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, mpc = notifiers.MPC() mpc.notify() + if headphones.CONFIG.EMAIL_ENABLED: + logger.info(u"Sending Email notification") + email = notifiers.Email() + subject = release['ArtistName'] + ' - ' + release['AlbumTitle'] + email.notify(subject, "Download and Postprocessing completed") + def embedAlbumArt(artwork, downloaded_track_list): logger.info('Embedding album art') diff --git a/headphones/searcher.py b/headphones/searcher.py index ea78d744..b92756e9 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -946,7 +946,12 @@ def send_to_downloader(data, bestqual, album): b2msg = 'From ' + provider + '

' + name boxcar = notifiers.BOXCAR() boxcar.notify('Headphones snatched: ' + title, b2msg, rgid) - + if headphones.CONFIG.EMAIL_ENABLED and headphones.CONFIG.EMAIL_ONSNATCH: + logger.info(u"Sending Email notification") + email = notifiers.Email() + subject = artist + ' - ' + albumname + message = 'Snatched from ' + provider + '. ' + title + email.notify(subject, message) def verifyresult(title, artistterm, term, lossless): diff --git a/headphones/webserve.py b/headphones/webserve.py index b3b67cac..04740593 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1151,7 +1151,16 @@ class WebInterface(object): "cache_sizemb": headphones.CONFIG.CACHE_SIZEMB, "file_permissions": headphones.CONFIG.FILE_PERMISSIONS, "folder_permissions": headphones.CONFIG.FOLDER_PERMISSIONS, - "mpc_enabled": checked(headphones.CONFIG.MPC_ENABLED) + "mpc_enabled": checked(headphones.CONFIG.MPC_ENABLED), + "email_enabled": checked(headphones.CONFIG.EMAIL_ENABLED), + "email_from": headphones.CONFIG.EMAIL_FROM, + "email_to": headphones.CONFIG.EMAIL_TO, + "email_smtp_server": headphones.CONFIG.EMAIL_SMTP_SERVER, + "email_smtp_user": headphones.CONFIG.EMAIL_SMTP_USER, + "email_smtp_password": headphones.CONFIG.EMAIL_SMTP_PASSWORD, + "email_smtp_port": int(headphones.CONFIG.EMAIL_SMTP_PORT), + "email_tls": checked(headphones.CONFIG.EMAIL_TLS), + "email_onsnatch": checked(headphones.CONFIG.EMAIL_ONSNATCH) } # Need to convert EXTRAS to a dictionary we can pass to the config: @@ -1196,7 +1205,7 @@ class WebInterface(object): "nma_enabled", "nma_onsnatch", "pushalot_enabled", "pushalot_onsnatch", "synoindex_enabled", "pushover_enabled", "pushover_onsnatch", "pushbullet_enabled", "pushbullet_onsnatch", "subsonic_enabled", "twitter_enabled", "twitter_onsnatch", "osx_notify_enabled", "osx_notify_onsnatch", "boxcar_enabled", "boxcar_onsnatch", "songkick_enabled", "songkick_filter_enabled", - "mpc_enabled" + "mpc_enabled", "email_enabled", "email_tls", "email_onsnatch" ] for checked_config in checked_configs: if checked_config not in kwargs: From 4f20003f9a0b581427fd2d64611501688632b59c Mon Sep 17 00:00:00 2001 From: Ade Date: Fri, 6 Feb 2015 21:37:41 +1300 Subject: [PATCH 02/21] Email part 2 small fix up --- headphones/searcher.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/headphones/searcher.py b/headphones/searcher.py index b92756e9..7f03d7c3 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -949,9 +949,8 @@ def send_to_downloader(data, bestqual, album): if headphones.CONFIG.EMAIL_ENABLED and headphones.CONFIG.EMAIL_ONSNATCH: logger.info(u"Sending Email notification") email = notifiers.Email() - subject = artist + ' - ' + albumname - message = 'Snatched from ' + provider + '. ' + title - email.notify(subject, message) + message = 'Snatched from ' + provider + '. ' + name + email.notify(title, message) def verifyresult(title, artistterm, term, lossless): From 03ea67b20bb7392bae91b8a60d04267dbe71d311 Mon Sep 17 00:00:00 2001 From: Bas Stottelaar Date: Sun, 15 Feb 2015 14:02:49 +0100 Subject: [PATCH 03/21] Fix for #2119 --- headphones/searcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/searcher.py b/headphones/searcher.py index 7f03d7c3..08e8d8bd 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -1096,7 +1096,7 @@ def searchTorrent(album, new=False, losslessOnly=False, albumlength=None): if headphones.CONFIG.KAT_PROXY_URL: providerurl = fix_url(set_proxy(headphones.CONFIG.KAT_PROXY_URL)) else: - providerurl = fix_url("https://kickass.so") + providerurl = fix_url("https://kickass.to") # Build URL providerurl = providerurl + "/usearch/" + ka_term From 24a52a294cf948db8e1e9a78b8b7d10558906979 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 19 Feb 2015 00:54:10 +0200 Subject: [PATCH 04/21] Allow authentication on custom MusicBrainz servers using Basic HTTP Authentication. --- data/interfaces/default/config.html | 31 +++++++++++++++++++++++++++++ headphones/config.py | 3 +++ headphones/mb.py | 12 ++++++++--- headphones/webserve.py | 5 ++++- lib/musicbrainzngs/musicbrainz.py | 12 +++++++++-- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 28c5dd4c..9cea2d58 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1449,6 +1449,17 @@
+
+ +
+
+
+ +
+
+
+
+
@@ -1559,6 +1570,25 @@ $('#api_key').val(data); }); }); + if ($("#customauth").is(":checked")) + { + $("#customauth_options").show(); + } + else + { + $("#customauth_options").hide(); + } + + $("#customauth").click(function(){ + if ($("#customauth").is(":checked")) + { + $("#customauth_options").slideDown(); + } + else + { + $("#customauth_options").slideUp(); + } + }); if ($("#enable_https").is(":checked")) { $("#https_options").show(); @@ -2127,6 +2157,7 @@ initConfigCheckbox("#use_whatcd"); initConfigCheckbox("#api_enabled"); initConfigCheckbox("#enable_https"); + initConfigCheckbox("#customauth"); $('#twitterStep1').click(function () { diff --git a/headphones/config.py b/headphones/config.py index 741ba9cd..5306aa4e 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -44,9 +44,12 @@ _CONFIG_DEFINITIONS = { 'CUE_SPLIT': (int, 'General', 1), 'CUE_SPLIT_FLAC_PATH': (str, 'General', ''), 'CUE_SPLIT_SHNTOOL_PATH': (str, 'General', ''), + 'CUSTOMAUTH': (int, 'General', 0), 'CUSTOMHOST': (str, 'General', 'localhost'), + 'CUSTOMPASS': (str, 'General', ''), 'CUSTOMPORT': (int, 'General', 5000), 'CUSTOMSLEEP': (int, 'General', 1), + 'CUSTOMUSER': (str, 'General', ''), 'DELETE_LOSSLESS_FILES': (int, 'General', 1), 'DESTINATION_DIR': (str, 'General', ''), 'DETECT_BITRATE': (int, 'General', 0), diff --git a/headphones/mb.py b/headphones/mb.py index 6caf0090..66a9f303 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -47,6 +47,8 @@ def startmb(): elif headphones.CONFIG.MIRROR == "custom": mbhost = headphones.CONFIG.CUSTOMHOST mbport = int(headphones.CONFIG.CUSTOMPORT) + mbuser = headphones.CONFIG.CUSTOMUSER + mbpass = headphones.CONFIG.CUSTOMPASS sleepytime = int(headphones.CONFIG.CUSTOMSLEEP) elif headphones.CONFIG.MIRROR == "headphones": mbhost = "144.76.94.239" @@ -69,12 +71,16 @@ def startmb(): mb_lock.minimum_delta = sleepytime # Add headphones credentials - if headphones.CONFIG.MIRROR == "headphones": - if not mbuser and mbpass: - logger.warn("No username or password set for VIP server") + if headphones.CONFIG.MIRROR == "headphones" or headphones.CONFIG.CUSTOMAUTH: + if not mbuser or not mbpass: + logger.warn("No username or password set for MusicBrainz server") else: musicbrainzngs.hpauth(mbuser, mbpass) + # Let us know if we disable custom authentication + if not headphones.CONFIG.CUSTOMAUTH: + musicbrainzngs.disable_hpauth(); + logger.debug('Using the following server values: MBHost: %s, MBPort: %i, Sleep Interval: %i', mbhost, mbport, sleepytime) return True diff --git a/headphones/webserve.py b/headphones/webserve.py index 04740593..ed1556ee 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1142,6 +1142,9 @@ class WebInterface(object): "customhost": headphones.CONFIG.CUSTOMHOST, "customport": headphones.CONFIG.CUSTOMPORT, "customsleep": headphones.CONFIG.CUSTOMSLEEP, + "customauth": checked(headphones.CONFIG.CUSTOMAUTH), + "customuser": headphones.CONFIG.CUSTOMUSER, + "custompass": headphones.CONFIG.CUSTOMPASS, "hpuser": headphones.CONFIG.HPUSER, "hppass": headphones.CONFIG.HPPASS, "songkick_enabled": checked(headphones.CONFIG.SONGKICK_ENABLED), @@ -1205,7 +1208,7 @@ class WebInterface(object): "nma_enabled", "nma_onsnatch", "pushalot_enabled", "pushalot_onsnatch", "synoindex_enabled", "pushover_enabled", "pushover_onsnatch", "pushbullet_enabled", "pushbullet_onsnatch", "subsonic_enabled", "twitter_enabled", "twitter_onsnatch", "osx_notify_enabled", "osx_notify_onsnatch", "boxcar_enabled", "boxcar_onsnatch", "songkick_enabled", "songkick_filter_enabled", - "mpc_enabled", "email_enabled", "email_tls", "email_onsnatch" + "mpc_enabled", "email_enabled", "email_tls", "email_onsnatch", "customauth" ] for checked_config in checked_configs: if checked_config not in kwargs: diff --git a/lib/musicbrainzngs/musicbrainz.py b/lib/musicbrainzngs/musicbrainz.py index 875eff6f..d7e5e74f 100644 --- a/lib/musicbrainzngs/musicbrainz.py +++ b/lib/musicbrainzngs/musicbrainz.py @@ -271,6 +271,7 @@ user = password = "" hostname = "musicbrainz.org" _client = "" _useragent = "" +mb_auth = False def auth(u, p): """Set the username and password to be used in subsequent queries to @@ -284,9 +285,16 @@ def hpauth(u, p): """Set the username and password to be used in subsequent queries to the MusicBrainz XML API that require authentication. """ - global hpuser, hppassword + global hpuser, hppassword, mb_auth hpuser = u hppassword = p + mb_auth = True + +def disable_hpauth(): + """Disable the authentication for MusicBrainz XML API + """ + global mb_auth + mb_auth = False def set_useragent(app, version, contact=None): """Set the User-Agent to be used for requests to the MusicBrainz webservice. @@ -635,7 +643,7 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False, req.add_header('User-Agent', _useragent) # Add headphones credentials - if hostname == '144.76.94.239:8181': + if mb_auth: base64string = base64.encodestring('%s:%s' % (hpuser, hppassword)).replace('\n', '') req.add_header("Authorization", "Basic %s" % base64string) From a68bcb6ee15750831730c98d4959839260a5d1b8 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 19 Feb 2015 11:48:10 +0200 Subject: [PATCH 05/21] Fix condition that checks if we should disable custom MusicBrainz server authentication. --- headphones/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/mb.py b/headphones/mb.py index 66a9f303..3a150977 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -78,8 +78,8 @@ def startmb(): musicbrainzngs.hpauth(mbuser, mbpass) # Let us know if we disable custom authentication - if not headphones.CONFIG.CUSTOMAUTH: - musicbrainzngs.disable_hpauth(); + if not headphones.CONFIG.CUSTOMAUTH and headphones.CONFIG.MIRROR == "custom": + musicbrainzngs.disable_hpauth() logger.debug('Using the following server values: MBHost: %s, MBPort: %i, Sleep Interval: %i', mbhost, mbport, sleepytime) From aa60ba1fb1d2f4237d552d4f1b6f864dd872bbd3 Mon Sep 17 00:00:00 2001 From: Jonny Date: Mon, 16 Feb 2015 22:27:11 +0000 Subject: [PATCH 06/21] Update config.html Added option to select using ID3v2.3 --- data/interfaces/default/config.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 9cea2d58..c6925195 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -786,6 +786,10 @@ Embed lyrics +