From a98849bf30a583e927fa226b6b00f99d52fc6b23 Mon Sep 17 00:00:00 2001 From: Jens Rogier Date: Thu, 18 Jun 2015 17:01:01 +0200 Subject: [PATCH 01/31] Added Musicbrainz link to "alternate release" window --- data/interfaces/default/album.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/interfaces/default/album.html b/data/interfaces/default/album.html index 0cc868b85..06ac7e102 100644 --- a/data/interfaces/default/album.html +++ b/data/interfaces/default/album.html @@ -35,6 +35,7 @@

- ${alternate_album_name}
+ ${alternate_album_name}MB
%endfor %endif From 275dde0630d983df4a76e60b0f741a53ff844c92 Mon Sep 17 00:00:00 2001 From: William Friesen Date: Sun, 28 Jun 2015 10:56:48 +1000 Subject: [PATCH 02/31] Fixed typo in postprocessor message --- headphones/postprocessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index a3c8e44e8..51caf5cfd 100755 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -1156,7 +1156,7 @@ def forcePostProcess(dir=None, expand_subfolders=True, album_dir=None): verify(release['AlbumID'], folder, forced=True) continue else: - logger.info('Found a (possibly) valid Musicbrainz realse group id in album folder name.') + logger.info('Found a (possibly) valid Musicbrainz release group id in album folder name.') verify(rgid, folder, forced=True) continue From 3e81c4f3b6a3219fba7e986acf059300df8f3225 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 3 Jul 2015 21:01:46 -0700 Subject: [PATCH 03/31] Fix for #2183. Check to make sure the response has headers before trying to parse them --- headphones/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/request.py b/headphones/request.py index a8b9e84db..6c8afda1a 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -206,7 +206,7 @@ def server_message(response): message = None # First attempt is to 'read' the response as HTML - if "text/html" in response.headers.get("content-type"): + if response.headers and "text/html" in response.headers.get("content-type"): try: soup = BeautifulSoup(response.content, "html5lib") except Exception: From a14369cb7dd1ca0a6e72f654f87d80971512834a Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 4 Jul 2015 00:47:02 -0700 Subject: [PATCH 04/31] Added option to only include 'official' extras, rather than everything (bootlegs, promos, etc) --- data/interfaces/default/config.html | 3 +++ headphones/config.py | 1 + headphones/mb.py | 10 ++++++---- headphones/webserve.py | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 55f0eae98..f380c7d64 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1372,6 +1372,9 @@

Email

%endfor +
+ +
diff --git a/headphones/config.py b/headphones/config.py index bc499fce6..dec8c741c 100644 --- a/headphones/config.py +++ b/headphones/config.py @@ -154,6 +154,7 @@ def bool_int(value): 'NZBSORG_HASH': (str, 'NZBsorg', ''), 'NZBSORG_UID': (str, 'NZBsorg', ''), 'NZB_DOWNLOADER': (int, 'General', 0), + 'OFFICIAL_RELEASES_ONLY': (int, 'General', 0), 'OMGWTFNZBS': (int, 'omgwtfnzbs', 0), 'OMGWTFNZBS_APIKEY': (str, 'omgwtfnzbs', ''), 'OMGWTFNZBS_UID': (str, 'omgwtfnzbs', ''), diff --git a/headphones/mb.py b/headphones/mb.py index 5aa711432..d83b45729 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -466,6 +466,11 @@ def get_new_releases(rgid, includeExtras=False, forcefull=False): myDB = db.DBConnection() results = [] + + release_status = "official" + if includeExtras and not headphones.CONFIG.OFFICIAL_RELEASES_ONLY: + release_status = [] + try: limit = 100 newResults = None @@ -474,6 +479,7 @@ def get_new_releases(rgid, includeExtras=False, forcefull=False): newResults = musicbrainzngs.browse_releases( release_group=rgid, includes=['artist-credits', 'labels', 'recordings', 'release-groups', 'media'], + release_status = release_status, limit=limit, offset=len(results)) if 'release-list' not in newResults: @@ -513,10 +519,6 @@ def get_new_releases(rgid, includeExtras=False, forcefull=False): num_new_releases = 0 for releasedata in results: - #releasedata.get will return None if it doesn't have a status - #all official releases should have the Official status included - if not includeExtras and releasedata.get('status') != 'Official': - continue release = {} rel_id_check = releasedata['id'] diff --git a/headphones/webserve.py b/headphones/webserve.py index 164895db5..9b2a86e49 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1073,6 +1073,7 @@ def config(self): "file_format": headphones.CONFIG.FILE_FORMAT, "file_underscores": checked(headphones.CONFIG.FILE_UNDERSCORES), "include_extras": checked(headphones.CONFIG.INCLUDE_EXTRAS), + "official_releases_only": checked(headphones.CONFIG.OFFICIAL_RELEASES_ONLY), "autowant_upcoming": checked(headphones.CONFIG.AUTOWANT_UPCOMING), "autowant_all": checked(headphones.CONFIG.AUTOWANT_ALL), "autowant_manually_added": checked(headphones.CONFIG.AUTOWANT_MANUALLY_ADDED), @@ -1220,7 +1221,7 @@ def configUpdate(self, **kwargs): "use_nzbsorg", "use_omgwtfnzbs", "use_kat", "use_piratebay", "use_oldpiratebay", "use_mininova", "use_waffles", "use_rutracker", "use_whatcd", "preferred_bitrate_allow_lossless", "detect_bitrate", "ignore_clean_releases", "freeze_db", "cue_split", "move_files", "rename_files", "correct_metadata", "cleanup_files", "keep_nfo", "add_album_art", "embed_album_art", "embed_lyrics", "replace_existing_folders", "keep_original_folder", - "file_underscores", "include_extras", "autowant_upcoming", "autowant_all", "autowant_manually_added", "keep_torrent_files", "music_encoder", + "file_underscores", "include_extras", "official_releases_only", "autowant_upcoming", "autowant_all", "autowant_manually_added", "keep_torrent_files", "music_encoder", "encoderlossless", "encoder_multicore", "delete_lossless_files", "growl_enabled", "growl_onsnatch", "prowl_enabled", "prowl_onsnatch", "xbmc_enabled", "xbmc_update", "xbmc_notify", "lms_enabled", "plex_enabled", "plex_update", "plex_notify", "nma_enabled", "nma_onsnatch", "pushalot_enabled", "pushalot_onsnatch", "synoindex_enabled", "pushover_enabled", From 24d7647c086e6029fe96c9f46550c70f1c472462 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 4 Jul 2015 12:13:40 -0700 Subject: [PATCH 05/31] Updated notifymyandroid library --- lib/pynma/__init__.py | 2 +- lib/pynma/pynma.py | 77 ++++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 34 deletions(-) mode change 100644 => 100755 lib/pynma/pynma.py diff --git a/lib/pynma/__init__.py b/lib/pynma/__init__.py index cbd82cfd5..a75b42855 100644 --- a/lib/pynma/__init__.py +++ b/lib/pynma/__init__.py @@ -1,4 +1,4 @@ #!/usr/bin/python -from pynma import PyNMA +from .pynma import PyNMA diff --git a/lib/pynma/pynma.py b/lib/pynma/pynma.py old mode 100644 new mode 100755 index 037145c88..2fc55560a --- a/lib/pynma/pynma.py +++ b/lib/pynma/pynma.py @@ -1,12 +1,20 @@ #!/usr/bin/python from xml.dom.minidom import parseString -from httplib import HTTPSConnection -from urllib import urlencode -__version__ = "0.1" +try: + from http.client import HTTPSConnection +except ImportError: + from httplib import HTTPSConnection -API_SERVER = 'nma.usk.bz' +try: + from urllib.parse import urlencode +except ImportError: + from urllib import urlencode + +__version__ = "1.0" + +API_SERVER = 'www.notifymyandroid.com' ADD_PATH = '/publicapi/notify' USER_AGENT="PyNMA/v%s"%__version__ @@ -18,14 +26,14 @@ def uniq_preserve(seq): # Dave Kirby def uniq(seq): # Not order preserving - return {}.fromkeys(seq).keys() + return list({}.fromkeys(seq).keys()) class PyNMA(object): """PyNMA(apikey=[], developerkey=None) - takes 2 optional arguments: - - (opt) apykey: might me a string containing 1 key or an array of keys - - (opt) developerkey: where you can store your developer key - """ +takes 2 optional arguments: + - (opt) apykey: might me a string containing 1 key or an array of keys + - (opt) developerkey: where you can store your developer key +""" def __init__(self, apikey=[], developerkey=None): self._developerkey = None @@ -60,19 +68,20 @@ def developerkey(self, developerkey): if type(developerkey) == str and len(developerkey) == 48: self._developerkey = developerkey - def push(self, application="", event="", description="", url="", priority=0, batch_mode=False): + def push(self, application="", event="", description="", url="", contenttype=None, priority=0, batch_mode=False, html=False): """Pushes a message on the registered API keys. - takes 5 arguments: - - (req) application: application name [256] - - (req) event: event name [1000] - - (req) description: description [10000] - - (opt) url: url [512] - - (opt) priority: from -2 (lowest) to 2 (highest) (def:0) - - (opt) batch_mode: call API 5 by 5 (def:False) - - Warning: using batch_mode will return error only if all API keys are bad - cf: http://nma.usk.bz/api.php - """ +takes 5 arguments: + - (req) application: application name [256] + - (req) event: event name [1000] + - (req) description: description [10000] + - (opt) url: url [512] + - (opt) contenttype: Content Type (act: None (plain text) or text/html) + - (opt) priority: from -2 (lowest) to 2 (highest) (def:0) + - (opt) batch_mode: push to all keys at once (def:False) + - (opt) html: shortcut for contenttype=text/html +Warning: using batch_mode will return error only if all API keys are bad + cf: http://nma.usk.bz/api.php +""" datas = { 'application': application[:256].encode('utf8'), 'event': event[:1024].encode('utf8'), @@ -82,7 +91,10 @@ def push(self, application="", event="", description="", url="", priority=0, bat if url: datas['url'] = url[:512] - + + if contenttype == "text/html" or html == True: # Currently only accepted content type + datas['content-type'] = "text/html" + if self._developerkey: datas['developerkey'] = self._developerkey @@ -94,12 +106,11 @@ def push(self, application="", event="", description="", url="", priority=0, bat res = self.callapi('POST', ADD_PATH, datas) results[key] = res else: - for i in range(0, len(self._apikey), 5): - datas['apikey'] = ",".join(self._apikey[i:i+5]) - res = self.callapi('POST', ADD_PATH, datas) - results[datas['apikey']] = res + datas['apikey'] = ",".join(self._apikey) + res = self.callapi('POST', ADD_PATH, datas) + results[datas['apikey']] = res return results - + def callapi(self, method, path, args): headers = { 'User-Agent': USER_AGENT } if method == "POST": @@ -110,13 +121,13 @@ def callapi(self, method, path, args): try: res = self._parse_reponse(resp.read()) - except Exception, e: + except Exception as e: res = {'type': "pynmaerror", 'code': 600, 'message': str(e) } pass - + return res def _parse_reponse(self, response): @@ -124,14 +135,14 @@ def _parse_reponse(self, response): for elem in root.childNodes: if elem.nodeType == elem.TEXT_NODE: continue if elem.tagName == 'success': - res = dict(elem.attributes.items()) + res = dict(list(elem.attributes.items())) res['message'] = "" res['type'] = elem.tagName return res if elem.tagName == 'error': - res = dict(elem.attributes.items()) + res = dict(list(elem.attributes.items())) res['message'] = elem.firstChild.nodeValue res['type'] = elem.tagName return res - - + + From a72bd4cb7ce7e64766545207db9df4c0551e58ea Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 4 Jul 2015 17:26:47 -0700 Subject: [PATCH 06/31] Fixed up the appearance of the config page (was kind of broken on Firefox) --- data/interfaces/default/config.html | 240 +++++++++++++++----------- data/interfaces/default/css/style.css | 10 ++ 2 files changed, 151 insertions(+), 99 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index f380c7d64..0e7f55a0f 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -659,50 +659,54 @@

Settings

+
+ Quality
- Quality -
+
+
+
+
+
-
- - Reject if target size is not in bitrate range: - to\ - kbps - -
+
+
+ + Reject if target size is not in bitrate range: + to\ + kbps + +
+
@@ -735,69 +739,79 @@

Settings

Post-Processing -
- -
-
+
-
-
- -
-
- -
-
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
-
-
- as .jpg -
- Use $Artist/$artist, $Album/$album, $Year/$year +
+
+
+ as .jpg
+ Use $Artist/$artist, $Album/$album, $Year/$year +
+
+
+
-
- %for x in ['cbr', 'vbr']: <% if config['encodervbrcbr'] == x: @@ -1277,35 +1291,38 @@

Email

%endfor
-
- - +
+
+ + +
-
- - +
+
+ + +
- <% if config["samplingfrequency"] == 44100: freq44100 = 'selected="selected"' @@ -1347,7 +1364,7 @@

Email

Miscellaneous -
+
-
- +
+
-
- +
+
-
- +
+
-
- - +
+
-
+
+
+ +
@@ -1439,7 +1461,7 @@

Email

- +
@@ -1498,7 +1520,7 @@

Email

-
+
Get an Account!
@@ -1525,6 +1547,10 @@

Email

$("#lameffmpegproperties").slideUp(); $("#xldproperties").slideUp(); }; + hideEncodingOptionDivs = function () { + $("#vbr_options").slideUp(); + $("#cbr_options").slideUp(); + }; handleNewServerSelection = function () { hideServerDivs(); @@ -1558,6 +1584,19 @@

Email

} }; + handleNewEncodingOptionSelection = function () { + hideEncodingOptionDivs(); + + switch ($(this).val()) { + case 'vbr': + $("#vbr_options").slideDown(); + break; + case 'cbr': + $("#cbr_options").slideDown(); + break; + } + }; + function openExtrasDialog() { $("#dialog").dialog({ close: function(){ var elem = ''; @@ -2120,6 +2159,9 @@

Email

$("#encoder").change(handleNewEncoderSelection); handleNewEncoderSelection.apply($("#encoder")); + $("#encodervbrcbr").change(handleNewEncodingOptionSelection); + handleNewEncodingOptionSelection.apply($("#encodervbrcbr")); + var deletedNewznabs = 0; $(".remove").click(function() { diff --git a/data/interfaces/default/css/style.css b/data/interfaces/default/css/style.css index e4922d60c..7786a9baf 100644 --- a/data/interfaces/default/css/style.css +++ b/data/interfaces/default/css/style.css @@ -408,6 +408,9 @@ form .checkbox small { form .indent input { margin-left: 15px; } +form .suboptions { + margin-left: 15px; +} ul, ol { margin-left: 2em; @@ -1420,6 +1423,13 @@ div#artistheader h2 a { line-height: 10px; height: 10px; } +.nopad { + margin-bottom: 0px !important; + margin-top: 0px !important; + padding-top: 0px !important; + padding-bottom: 0px !important; + padding: 0px 0px !important; +} #album_table th#albumname, #upcoming_table th#artistname, #wanted_table th#artistname { From 6e5e15f06fa99d0e477b2df73f323424b5eb06bd Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 4 Jul 2015 19:27:27 -0700 Subject: [PATCH 07/31] Fixed plex notifications for version 12 --- headphones/notifiers.py | 32 ++++++++++++++++++++++++++------ headphones/webserve.py | 4 ++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/headphones/notifiers.py b/headphones/notifiers.py index 6a2978d8d..a383d2e52 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -345,6 +345,19 @@ def _sendhttp(self, host, command): return response + def _sendjson(self, host, method, params={}): + data = [{'id': 0, 'jsonrpc': '2.0', 'method': method, 'params': params}] + headers = {'Content-Type': 'application/json'} + url = host + '/jsonrpc' + + if self.password: + response = request.request_json(url, method="post", data=json.dumps(data), headers=headers, auth=(self.username, self.password)) + else: + response = request.request_json(url, method="post", data=json.dumps(data), headers=headers) + + if response: + return response[0]['result'] + def update(self): # From what I read you can't update the music library on a per directory or per path basis @@ -382,17 +395,24 @@ def notify(self, artist, album, albumartpath): time = "3000" # in ms for host in hosts: - logger.info('Sending notification command to Plex Media Server @ ' + host) + logger.info('Sending notification command to Plex client @ ' + host) try: - notification = header + "," + message + "," + time + "," + albumartpath - notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'} - request = self._sendhttp(host, notifycommand) + version = self._sendjson(host, 'Application.GetProperties', {'properties': ['version']})['version']['major'] + + if version < 12: #Eden + notification = header + "," + message + "," + time + "," + albumartpath + notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'} + request = self._sendhttp(host, notifycommand) + + else: #Frodo + params = {'title': header, 'message': message, 'displaytime': int(time), 'image': albumartpath} + request = self._sendjson(host, 'GUI.ShowNotification', params) if not request: raise Exception - except: - logger.warn('Error sending notification request to Plex Media Server') + except Exception: + logger.error('Error sending notification request to Plex client @ ' + host) class NMA(object): diff --git a/headphones/webserve.py b/headphones/webserve.py index 9b2a86e49..d2d27c58b 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1436,9 +1436,9 @@ def testPushover(self): @cherrypy.expose def testPlex(self): - logger.info(u"Testing plex updates") + logger.info(u"Testing plex notifications") plex = notifiers.Plex() - plex.update() + plex.notify("hellooooo", "test album!", "") class Artwork(object): @cherrypy.expose From 8aee9ac91f2fe0e504365e4abe6a1cc50fa3e934 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 4 Jul 2015 19:36:39 -0700 Subject: [PATCH 08/31] Fixed plex client url in config --- data/interfaces/default/config.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 0e7f55a0f..cba1b2d7a 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -686,7 +686,7 @@

Settings

kbps
-
+
diff --git a/data/interfaces/default/css/style.css b/data/interfaces/default/css/style.css index 7786a9baf..5d55410fc 100644 --- a/data/interfaces/default/css/style.css +++ b/data/interfaces/default/css/style.css @@ -325,7 +325,7 @@ form fieldset small.heading { margin-top: -15px; } form .row { - font-family: Helvetica, Arial; + font-family: Helvetica, Arial, sans-serif; margin-bottom: 10px; } form .row label { From 6dc75d7a44d3dc6fd1f3e4cd7d13bddd6e754ce2 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sun, 5 Jul 2015 21:40:52 -0700 Subject: [PATCH 13/31] Cleaned up the config page a little bit. Took out {option} enabled buttons and just put them in with the option. Alphabetized the notifiers. Fixed checkbox on chrome --- data/interfaces/default/config.html | 404 +++++++++++++------------- data/interfaces/default/css/style.css | 9 + 2 files changed, 204 insertions(+), 209 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 93352fc2d..ff5c60617 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -413,10 +413,9 @@

Settings

NZBs
- Headphones Indexer -
- - +
+ +
@@ -434,9 +433,8 @@

Settings

- Custom Newznab Providers -
- +
+
@@ -450,7 +448,7 @@

Settings

- +
<% @@ -488,9 +486,8 @@

Settings

- NZBs.org -
- +
+
@@ -500,9 +497,8 @@

Settings

- omgwtfnzbs -
- +
+
@@ -521,9 +517,8 @@

Settings

Torrents
- The Pirate Bay -
- +
+
@@ -539,9 +534,8 @@

Settings

- Old Pirate Bay -
- +
+
@@ -556,9 +550,8 @@

Settings

- Kick Ass Torrents -
- +
+
@@ -574,9 +567,8 @@

Settings

- Waffles.fm -
- +
+
@@ -595,9 +587,8 @@

Settings

- rutracker.org -
- +
+
@@ -616,9 +607,8 @@

Settings

- What.cd -
- +
+
@@ -637,9 +627,8 @@

Settings

- Mininova -
- +
+
@@ -838,112 +827,125 @@

Settings

-

Growl

-
- +
+
-
-
- -
+
- +
- +
-
+
+
-

Prowl

-
- +
+
-
+
- -
-
- +
- - +
-
-
-
-

XBMC

-
- -
-
- - - e.g. http://localhost:8080. Separate hosts with commas +
- +
- +
-
- +
+
-
- +
+ +
+
+ +
+
+
+
-

Plex Media Server

-
- +
+
-
+
- - - Host running Plex Media Server (eg. http://192.168.1.100:32400) +
- - - Host running Plex Client (eg. http://192.168.1.100:3005) + +
+
+
+
+
+ +
+
+ +
+
- - Username of your Plex client API (blank for none) + + + e.g. http://localhost:8080. Separate hosts with commas
- - Password of your Plex client API (blank for none) +
- - Plex Token (for use with Plex Home) +
- +
- + +
+
+
+ +
+
+ +
+
+
+ + + e.g. http://localhost:9000. Seperate hosts with commas
+
-

NotifyMyAndroid

-
- +
+ +
+
+ +
+
+
- + Separate multiple api keys with commas
@@ -974,91 +976,103 @@

NotifyMyAndroid

+
-

Logitech Media Server

-
- +
+
-
+
- - - e.g. http://localhost:9000. Seperate hosts with commas +
-
-
- -
-

Pushalot

-
- -
-
-
- +
+ Enter the path/application name to be registered with the Notification Center, default is /Applications/Headphones
- - - Separate multiple api keys with commas + +
+
+
+ + +
-

Subsonic

-
- +
+
-
+
- + + + Host running Plex Media Server (eg. http://192.168.1.100:32400)
- + + + Host running Plex Client (eg. http://192.168.1.100:3005)
- + + Username of your Plex client API (blank for none) +
+
+ + Password of your Plex client API (blank for none) +
+
+ + Plex Token (for use with Plex Home) +
+
+ +
+
+
-
-
- - - - -
-

Synology NAS

-
-
-

Pushover

-
- +
+
-
+
- +
- +
- - + + +
+
+
+ +
+
+ +
+
+
+
- + + + Separate multiple api keys with commas
-

Pushbullet

-
- +
+
@@ -1074,101 +1088,70 @@

Pushbullet

-

Twitter

-
- +
+
-
-
- -
+
- + +
+
+
- - + +
- +
-

OS X

-
- +
+
-
-
- Enter the path/application name to be registered with the Notification Center, default is /Applications/Headphones - - -
-
- +
+
+
-
-
- -
-

Boxcar2

-
- -
-
- +
-
- +
+
-
+
-

MPC

-
- +
+
-
-

Email

-
- +
+
+
-
-
- -
-
- +
+
+
- +
- + +
- -
-
- -
-
- -
-
- -
-
- +
+ @@ -1429,11 +1412,11 @@

Email

-
+ +
+ Directories
diff --git a/data/interfaces/default/css/style.css b/data/interfaces/default/css/style.css index 5d55410fc..a67238189 100644 --- a/data/interfaces/default/css/style.css +++ b/data/interfaces/default/css/style.css @@ -411,6 +411,15 @@ form .indent input { form .suboptions { margin-left: 15px; } +.option{ + font-size: 15px; + font-weight: 600; + vertical-align: middle; +} +input.bigcheck[type="checkbox"] { + width: 16px; + height: 16px; +} ul, ol { margin-left: 2em; From 57c3c7b2e043f69d864d141512f0aa5af539045c Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sun, 5 Jul 2015 21:44:37 -0700 Subject: [PATCH 14/31] Fixed songkick description to prevent spilling over on default fonts --- data/interfaces/default/config.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index ff5c60617..3e699471c 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1444,7 +1444,7 @@

Settings

Songkick
- +
From 064a21a07ce97ff1cb2cf4542cec6fdf44727ff3 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Mon, 6 Jul 2015 00:02:09 -0700 Subject: [PATCH 15/31] Better fix for #2183 --- headphones/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/request.py b/headphones/request.py index 6c8afda1a..3916199a1 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -206,7 +206,7 @@ def server_message(response): message = None # First attempt is to 'read' the response as HTML - if response.headers and "text/html" in response.headers.get("content-type"): + if response.headers.get("content-type") == "text/html": try: soup = BeautifulSoup(response.content, "html5lib") except Exception: From 872ec6b11ced1865082907c612fd6e2b3dd2b600 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Mon, 6 Jul 2015 00:09:43 -0700 Subject: [PATCH 16/31] Fix for #2089 --- headphones/librarysync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/librarysync.py b/headphones/librarysync.py index 08e5ee902..d9185e272 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -374,7 +374,7 @@ def update_album_status(AlbumID=None): album_completion = 0 logger.info('Album %s does not have any tracks in database' % album['AlbumTitle']) - if album_completion >= headphones.CONFIG.ALBUM_COMPLETION_PCT and album['Status'] == 'Skipped': + if album_completion >= headphones.CONFIG.ALBUM_COMPLETION_PCT: new_album_status = "Downloaded" # I don't think we want to change Downloaded->Skipped..... From 9149f62760d9d5a49954f96fc2be9c4ed7401d67 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Mon, 6 Jul 2015 00:21:26 -0700 Subject: [PATCH 17/31] Fix for #2173: Uncaught exception in get_age --- headphones/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/helpers.py b/headphones/helpers.py index 238b6dea2..55a8778a1 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -149,7 +149,7 @@ def get_age(date): try: days_old = int(split_date[0]) * 365 + int(split_date[1]) * 30 + int(split_date[2]) - except IndexError: + except (IndexError,ValueError): days_old = False return days_old From 7c990edeafd0136ef301bc1fae913d4b06067a12 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Mon, 6 Jul 2015 00:34:16 -0700 Subject: [PATCH 18/31] Better fix for #2183 --- headphones/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/request.py b/headphones/request.py index 3916199a1..1c6f0308a 100644 --- a/headphones/request.py +++ b/headphones/request.py @@ -206,7 +206,7 @@ def server_message(response): message = None # First attempt is to 'read' the response as HTML - if response.headers.get("content-type") == "text/html": + if response.headers.get("content-type") and "text/html" in response.headers.get("content-type"): try: soup = BeautifulSoup(response.content, "html5lib") except Exception: From 5634e17a2e5913b636b92e95d31d18ffba137702 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Mon, 6 Jul 2015 01:28:48 -0700 Subject: [PATCH 19/31] Fix for #2155, local variable response referenced before assignment utorrent --- headphones/utorrent.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/headphones/utorrent.py b/headphones/utorrent.py index c0e4f264c..352ac72b4 100644 --- a/headphones/utorrent.py +++ b/headphones/utorrent.py @@ -73,6 +73,7 @@ def _get_token(self): except urllib2.HTTPError as err: logger.debug('URL: ' + str(url)) logger.debug('Error getting Token. uTorrent responded with error: ' + str(err)) + return match = re.search(utorrentclient.TOKEN_REGEX, response.read()) return match.group(1) @@ -147,6 +148,10 @@ def remove(self, hash, remove_data=False): return self._action(params) def _action(self, params, body=None, content_type=None): + + if not self.token: + return + url = self.base_url + '/gui/' + '?token=' + self.token + '&' + urllib.urlencode(params) request = urllib2.Request(url) From cb209f8d98e00187251c5cf434d18548aa9bfa70 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Mon, 6 Jul 2015 13:05:51 -0700 Subject: [PATCH 20/31] Fix for #2264: Metacritic parsing failing with htmlparser --- headphones/metacritic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/headphones/metacritic.py b/headphones/metacritic.py index 21478800d..016471650 100644 --- a/headphones/metacritic.py +++ b/headphones/metacritic.py @@ -37,12 +37,13 @@ def update(artistid, artist_name,release_groups): url = "http://www.metacritic.com/person/" + mc_artist_name + "?filter-options=music&sort_options=date&num_items=100" - res = request.request_soup(url, headers=headers, parser='html.parser') + res = request.request_soup(url, headers=headers) rows = None try: - rows = res.tbody.find_all('tr') + table = res.find("table", class_="credits person_credits") + rows = table.tbody.find_all('tr') except: logger.info("Unable to get metacritic scores for: %s" % artist_name) From 6db2836662fcbb31a9c47a8f3052870d9a87e5a3 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 7 Jul 2015 22:54:34 -0700 Subject: [PATCH 21/31] Moved plex notifications to requests lib for pre-v12 plex --- headphones/notifiers.py | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/headphones/notifiers.py b/headphones/notifiers.py index a383d2e52..b3afa0cae 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -320,28 +320,12 @@ def __init__(self): def _sendhttp(self, host, command): - username = self.username - password = self.password + url = host + '/xbmcCmds/xbmcHttp/?' + command - url_command = urllib.urlencode(command) - - url = host + '/xbmcCmds/xbmcHttp/?' + url_command - - req = urllib2.Request(url) - - if password: - base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '') - req.add_header("Authorization", "Basic %s" % base64string) - - logger.info('Plex url: %s' % url) - - try: - handle = urllib2.urlopen(req) - except Exception as e: - logger.warn('Error opening Plex url: %s' % e) - return - - response = handle.read().decode(headphones.SYS_ENCODING) + if self.password: + response = request.request_response(url, auth=(self.username, self.password)) + else: + response = request.request_response(url) return response From 5f06d11fe9834ee60ede3f46f6c6297605a9bce2 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 7 Jul 2015 23:47:38 -0700 Subject: [PATCH 22/31] Fixed pushbullet notifications --- headphones/notifiers.py | 50 ++++++++++++----------------------------- headphones/webserve.py | 6 +++++ 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/headphones/notifiers.py b/headphones/notifiers.py index b3afa0cae..545350f71 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -443,52 +443,30 @@ def __init__(self): self.apikey = headphones.CONFIG.PUSHBULLET_APIKEY self.deviceid = headphones.CONFIG.PUSHBULLET_DEVICEID - def conf(self, options): - return cherrypy.config['config'].get('PUSHBULLET', options) - - def notify(self, message, event): + def notify(self, message): if not headphones.CONFIG.PUSHBULLET_ENABLED: return - http_handler = HTTPSConnection("api.pushbullet.com") + url = "https://api.pushbullet.com/v2/pushes" data = {'type': "note", 'title': "Headphones", - 'body': message.encode("utf-8")} + 'body': message} - http_handler.request("POST", - "/v2/pushes", - headers={'Content-type': "application/json", - 'Authorization': 'Basic %s' % base64.b64encode(headphones.CONFIG.PUSHBULLET_APIKEY + ":")}, - body=json.dumps(data)) - response = http_handler.getresponse() - request_status = response.status - logger.debug(u"PushBullet response status: %r" % request_status) - logger.debug(u"PushBullet response headers: %r" % response.getheaders()) - logger.debug(u"PushBullet response body: %r" % response.read()) + if self.deviceid: + data['device_iden'] = self.deviceid - if request_status == 200: - logger.info(u"PushBullet notifications sent.") - return True - elif request_status >= 400 and request_status < 500: - logger.info(u"PushBullet request failed: %s" % response.reason) - return False - else: - logger.info(u"PushBullet notification failed serverside.") - return False + headers={'Content-type': "application/json", + 'Authorization': 'Bearer ' + headphones.CONFIG.PUSHBULLET_APIKEY} - def updateLibrary(self): - #For uniformity reasons not removed - return - - def test(self, apikey, deviceid): - - self.enabled = True - self.apikey = apikey - self.deviceid = deviceid - - self.notify('Main Screen Activate', 'Test Message') + response = request.request_json(url, method="post", headers=headers, data=json.dumps(data)) + if response: + logger.info(u"PushBullet notifications sent.") + return True + else: + logger.info(u"PushBullet notification failed.") + return False class PUSHALOT(object): diff --git a/headphones/webserve.py b/headphones/webserve.py index 23772a9a5..610a705af 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -1443,6 +1443,12 @@ def testPlex(self): plex = notifiers.Plex() plex.notify("hellooooo", "test album!", "") + @cherrypy.expose + def testPushbullet(self): + logger.info("Testing Pushbullet notifications") + pushbullet = notifiers.PUSHBULLET() + pushbullet.notify("it works!") + class Artwork(object): @cherrypy.expose def index(self): From e6663c96b5ebc094026828d70e7a4ea77579dd20 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 7 Jul 2015 23:56:17 -0700 Subject: [PATCH 23/31] Fixed some pushbullet config text --- data/interfaces/default/config.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 3e699471c..120a4422c 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1076,10 +1076,10 @@

Settings

- +
- + Leave blank to send to all devices
From f131d316caf146fd145ec4ca947a80c073803b7e Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 8 Jul 2015 00:45:33 -0700 Subject: [PATCH 24/31] Set localhost as default --- data/interfaces/default/config.html | 2 +- headphones/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 120a4422c..7b8a1275d 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -38,7 +38,7 @@

Settings

HTTP Host - e.g. localhost or an IP, such as 0.0.0.0 + Use 0.0.0.0 to allow outside connections