From f24d0491ba1e92b717fca3207e2d18c4c9fb2802 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 18 Jul 2012 19:25:39 +0530 Subject: [PATCH 01/16] Sort upcoming releases by the soonest first --- headphones/webserve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/webserve.py b/headphones/webserve.py index bd2c11e1..a9914f10 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -214,7 +214,7 @@ class WebInterface(object): def upcoming(self): myDB = db.DBConnection() - upcoming = myDB.select("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate DESC") + upcoming = myDB.select("SELECT * from albums WHERE ReleaseDate > date('now') order by ReleaseDate ASC") wanted = myDB.select("SELECT * from albums WHERE Status='Wanted'") return serve_template(templatename="upcoming.html", title="Upcoming", upcoming=upcoming, wanted=wanted) upcoming.exposed = True From 55c4081d5ff31c8013694af74dd7f1392f35cba5 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 19 Jul 2012 13:09:18 +0530 Subject: [PATCH 02/16] Changed favicon.ico per elmarkou --- data/images/favicon.ico | Bin 99678 -> 1150 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/images/favicon.ico b/data/images/favicon.ico index 6a4728a6bfc2bf19f7dd0004b627993e437f9b15..bc5b5d35da5396313344f710ffaeaeff8ee185c0 100644 GIT binary patch literal 1150 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 Date: Fri, 20 Jul 2012 16:04:54 +0530 Subject: [PATCH 03/16] Mark alhad albums as downloaded instead of skipped (or wanted when it's a new artist/album) --- headphones/__init__.py | 15 ++++++++++++--- headphones/importer.py | 27 ++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/headphones/__init__.py b/headphones/__init__.py index f3bfc597..a6ab5255 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -136,6 +136,8 @@ LOSSY_MEDIA_FORMATS = ["mp3", "aac", "ogg", "ape", "m4a"] LOSSLESS_MEDIA_FORMATS = ["flac"] MEDIA_FORMATS = LOSSY_MEDIA_FORMATS + LOSSLESS_MEDIA_FORMATS +ALBUM_COMPLETION_PCT = None # This is used in importer.py to determine how complete an album needs to be - to be considered "downloaded". Percentage from 0-100 + TORRENTBLACKHOLE_DIR = None NUMBEROFSEEDERS = 10 ISOHUNT = None @@ -242,7 +244,8 @@ def initialize(): NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \ ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, \ ENCODERLOSSLESS, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, MIRRORLIST, MIRROR, CUSTOMHOST, CUSTOMPORT, \ - CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, SYNOINDEX_ENABLED + CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, SYNOINDEX_ENABLED, \ + ALBUM_COMPLETION_PCT if __INITIALIZED__: return False @@ -259,6 +262,7 @@ def initialize(): CheckSection('XBMC') CheckSection('NMA') CheckSection('Synoindex') + CheckSection('Advanced') # Set global variables based on config file or use defaults CONFIG_VERSION = check_setting_str(CFG, 'General', 'config_version', '0') @@ -383,8 +387,10 @@ def initialize(): CUSTOMHOST = check_setting_str(CFG, 'General', 'customhost', 'localhost') CUSTOMPORT = check_setting_int(CFG, 'General', 'customport', 5000) CUSTOMSLEEP = check_setting_int(CFG, 'General', 'customsleep', 1) - HPUSER = check_setting_str(CFG, 'General', 'hpuser', 'username') - HPPASS = check_setting_str(CFG, 'General', 'hppass', 'password') + HPUSER = check_setting_str(CFG, 'General', 'hpuser', '') + HPPASS = check_setting_str(CFG, 'General', 'hppass', '') + + ALBUM_COMPLETION_PCT = check_setting_int(CFG, 'Advanced', 'album_completion_pct', 80) # update folder formats in the config & bump up config version if CONFIG_VERSION == '0': @@ -662,6 +668,9 @@ def config_write(): new_config['General']['hpuser'] = HPUSER new_config['General']['hppass'] = HPPASS + new_config['Advanced'] = {} + new_config['Advanced']['album_completion_pct'] = ALBUM_COMPLETION_PCT + new_config.write() diff --git a/headphones/importer.py b/headphones/importer.py index 5a5efa75..143ebe78 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -156,7 +156,7 @@ def addArtisttoDB(artistid, extrasonly=False): rgid = rg['id'] # check if the album already exists - rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rg['id']]) + rg_exists = myDB.action("SELECT * from albums WHERE AlbumID=?", [rg['id']]).fetchone() try: release_dict = mb.getReleaseGroup(rgid) @@ -180,7 +180,7 @@ def addArtisttoDB(artistid, extrasonly=False): } # Only change the status & add DateAdded if the album is not already in the database - if not len(rg_exists): + if not rg_exists: newValueDict['DateAdded']= helpers.today() @@ -193,6 +193,10 @@ def addArtisttoDB(artistid, extrasonly=False): myDB.upsert("albums", newValueDict, controlValueDict) + # This is used to see how many tracks you have from an album - to mark it as downloaded. Default is 80%, can be set in config as ALBUM_COMPLETION_PCT + total_track_count = len(release_dict['tracks']) + + for track in release_dict['tracks']: cleanname = helpers.cleanName(artist['artist_name'] + ' ' + rg['title'] + ' ' + track['title']) @@ -217,16 +221,29 @@ def addArtisttoDB(artistid, extrasonly=False): if not match: match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() if match: + have_track_count += 1 newValueDict['Location'] = match['Location'] newValueDict['BitRate'] = match['BitRate'] newValueDict['Format'] = match['Format'] myDB.action('DELETE from have WHERE Location=?', [match['Location']]) - + myDB.upsert("tracks", newValueDict, controlValueDict) - + + # Mark albums as downloaded if they have at least 80% (by default, configurable) of the album + have_track_count = len(myDB.select('SELECT * from tracks WHERE AlbumID=? AND Location IS NOT NULL', [rg['id']])) + + if rg_exists: + if rg_exists['Status'] == 'Skipped' and ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)): + logger.info('album exists, marking as downloaded 1') + myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', rg['id']]) + else: + if ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)): + logger.info('album exists, marking as downloaded 2') + myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', rg['id']]) + logger.debug(u"Updating album cache for " + rg['title']) cache.getThumb(AlbumID=rg['id']) - + latestalbum = myDB.action('SELECT AlbumTitle, ReleaseDate, AlbumID from albums WHERE ArtistID=? order by ReleaseDate DESC', [artistid]).fetchone() totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['artist_name']])) From 5469d2106644d343bd6750dc2b11fd4f819ef1aa Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 20 Jul 2012 16:45:40 +0530 Subject: [PATCH 04/16] Removed test logging, leftover have track counter --- headphones/importer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/headphones/importer.py b/headphones/importer.py index 143ebe78..10d77e52 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -196,7 +196,6 @@ def addArtisttoDB(artistid, extrasonly=False): # This is used to see how many tracks you have from an album - to mark it as downloaded. Default is 80%, can be set in config as ALBUM_COMPLETION_PCT total_track_count = len(release_dict['tracks']) - for track in release_dict['tracks']: cleanname = helpers.cleanName(artist['artist_name'] + ' ' + rg['title'] + ' ' + track['title']) @@ -221,7 +220,6 @@ def addArtisttoDB(artistid, extrasonly=False): if not match: match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() if match: - have_track_count += 1 newValueDict['Location'] = match['Location'] newValueDict['BitRate'] = match['BitRate'] newValueDict['Format'] = match['Format'] @@ -234,11 +232,9 @@ def addArtisttoDB(artistid, extrasonly=False): if rg_exists: if rg_exists['Status'] == 'Skipped' and ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)): - logger.info('album exists, marking as downloaded 1') myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', rg['id']]) else: if ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)): - logger.info('album exists, marking as downloaded 2') myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', rg['id']]) logger.debug(u"Updating album cache for " + rg['title']) From a2ddfb546dfa4ff5378f37a15e60dc053a390605 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 20 Jul 2012 18:45:31 +0530 Subject: [PATCH 05/16] Added links to ArtistName in default/upcoming.html --- data/interfaces/default/upcoming.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/upcoming.html b/data/interfaces/default/upcoming.html index 69f75c4c..5720f12a 100644 --- a/data/interfaces/default/upcoming.html +++ b/data/interfaces/default/upcoming.html @@ -41,7 +41,7 @@ - ${album['ArtistName']} + ${album['ArtistName']} ${album['AlbumTitle']} ${album['ReleaseDate']} ${album['Type']} @@ -71,7 +71,7 @@ %for album in upcoming: - ${album['ArtistName']} + ${album['ArtistName']} ${album['AlbumTitle']} ${album['ReleaseDate']} ${album['Type']} From 22685fdd95df11309741cffa1ed03918856c862a Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 20 Jul 2012 18:48:38 +0530 Subject: [PATCH 06/16] Run refreshArtist in a separate thread --- headphones/webserve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/webserve.py b/headphones/webserve.py index a9914f10..fc0332e7 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -143,7 +143,7 @@ class WebInterface(object): deleteArtist.exposed = True def refreshArtist(self, ArtistID): - importer.addArtisttoDB(ArtistID) + threading.Thread(target=importer.addArtisttoDB, args=[ArtistID]).start() raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID) refreshArtist.exposed=True From 9dfce0e39d16f21a9e4ecdf1f19f3f5a9536ad9b Mon Sep 17 00:00:00 2001 From: rembo10 Date: Sat, 21 Jul 2012 15:31:46 +0530 Subject: [PATCH 07/16] Put username & password together for sabnzbd on default config page, changes newzbin password field from text to password type --- data/interfaces/default/config.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 599759e9..ef0f534c 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -109,14 +109,14 @@ m<%inherit file="base.html"/>
-
+ +
+ + +
-
-
- -
@@ -238,7 +238,7 @@ m<%inherit file="base.html"/>
- +
From 425ece17ace5bed5775176413d0a7927ced55f06 Mon Sep 17 00:00:00 2001 From: Aaron Cohen Date: Sun, 22 Jul 2012 22:18:44 -0700 Subject: [PATCH 08/16] Synoindex tweaks, now will always log when sending notification --- headphones/notifiers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/headphones/notifiers.py b/headphones/notifiers.py index ae04fe99..3c16b647 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -197,6 +197,8 @@ class Synoindex: return os.path.exists(self.util_loc) def notify(self, path): + path = os.path.abspath(path) + if not self.util_exists(): logger.warn("Error sending notification: synoindex utility not found at %s" % self.util_loc) return @@ -209,12 +211,12 @@ class Synoindex: logger.warn("Error sending notification: Path passed to synoindex was not a file or folder.") return - cmd = [self.util_loc, cmd_arg, '\"%s\"' % os.path.abspath(path)] - logger.debug("Calling synoindex command: %s" % str(cmd)) + cmd = [self.util_loc, cmd_arg, path] + logger.info("Calling synoindex command: %s" % str(cmd)) try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=headphones.PROG_DIR) out, error = p.communicate() - logger.debug("Synoindex result: %s" % str(out)) + #synoindex never returns any codes other than '0', highly irritating except OSError, e: logger.warn("Error sending notification: %s" % str(e)) From 2b51447a9e7885c5d3835154566e0d6e919efd14 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 25 Jul 2012 17:54:41 +0530 Subject: [PATCH 09/16] Initial changes to allow multiple newznab providers --- data/interfaces/default/config.html | 31 ++++++++++++++++------------- headphones/__init__.py | 5 ++++- headphones/webserve.py | 4 +++- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index ef0f534c..b5d62ecc 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -184,7 +184,7 @@ m<%inherit file="base.html"/>
- +
@@ -200,17 +200,20 @@ m<%inherit file="base.html"/>
-
- - - e.g. http://nzb.su -
-
- - -
-
- +
+ + + e.g. http://nzb.su +
+
+ + +
+
+ +
+
+ @@ -233,11 +236,11 @@ m<%inherit file="base.html"/>
- +
- +
diff --git a/headphones/__init__.py b/headphones/__init__.py index a6ab5255..91b396f7 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -121,6 +121,7 @@ NZBMATRIX_APIKEY = None NEWZNAB = False NEWZNAB_HOST = None NEWZNAB_APIKEY = None +NEWZNAB_ENABLED = False NZBSORG = False NZBSORG_UID = None @@ -240,7 +241,7 @@ def initialize(): ADD_ALBUM_ART, EMBED_ALBUM_ART, EMBED_LYRICS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \ TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, DOWNLOAD_TORRENT_DIR, \ LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \ - NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, \ + NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, \ NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \ ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, \ ENCODERLOSSLESS, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, MIRRORLIST, MIRROR, CUSTOMHOST, CUSTOMPORT, \ @@ -340,6 +341,7 @@ def initialize(): NEWZNAB = bool(check_setting_int(CFG, 'Newznab', 'newznab', 0)) NEWZNAB_HOST = check_setting_str(CFG, 'Newznab', 'newznab_host', '') NEWZNAB_APIKEY = check_setting_str(CFG, 'Newznab', 'newznab_apikey', '') + NEWZNAB_ENABLED = bool(check_setting_int(CFG, 'Newznab', 'newznab_enabled', 1)) NZBSORG = bool(check_setting_int(CFG, 'NZBsorg', 'nzbsorg', 0)) NZBSORG_UID = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_uid', '') @@ -613,6 +615,7 @@ def config_write(): new_config['Newznab']['newznab'] = int(NEWZNAB) new_config['Newznab']['newznab_host'] = NEWZNAB_HOST new_config['Newznab']['newznab_apikey'] = NEWZNAB_APIKEY + new_config['Newznab']['newznab_enabled'] = int(NEWZNAB_ENABLED) new_config['NZBsorg'] = {} new_config['NZBsorg']['nzbsorg'] = int(NZBSORG) diff --git a/headphones/webserve.py b/headphones/webserve.py index fc0332e7..93865a6a 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -384,6 +384,7 @@ class WebInterface(object): "use_newznab" : checked(headphones.NEWZNAB), "newznab_host" : headphones.NEWZNAB_HOST, "newznab_api" : headphones.NEWZNAB_APIKEY, + "newznab_enabled" : checked(headphones.NEWZNAB_ENABLED), "use_nzbsorg" : checked(headphones.NZBSORG), "nzbsorg_uid" : headphones.NZBSORG_UID, "nzbsorg_hash" : headphones.NZBSORG_HASH, @@ -458,7 +459,7 @@ class WebInterface(object): def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None, download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, sab_host=None, sab_username=None, sab_apikey=None, sab_password=None, sab_category=None, download_dir=None, blackhole=0, blackhole_dir=None, - usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None, + usenet_retention=None, nzbmatrix=0, nzbmatrix_username=None, nzbmatrix_apikey=None, newznab=0, newznab_host=None, newznab_apikey=None, newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, newzbin=0, newzbin_uid=None, newzbin_password=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, torrentblackhole_dir=None, download_torrent_dir=None, numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None, rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, autowant_upcoming=False, autowant_all=False, interface=None, log_dir=None, @@ -491,6 +492,7 @@ class WebInterface(object): headphones.NEWZNAB = newznab headphones.NEWZNAB_HOST = newznab_host headphones.NEWZNAB_APIKEY = newznab_apikey + headphones.NEWZNAB_ENABLED = newznab_enabled headphones.NZBSORG = nzbsorg headphones.NZBSORG_UID = nzbsorg_uid headphones.NZBSORG_HASH = nzbsorg_hash From bf43f65995a5093efc393da81bd975b720d641ee Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 25 Jul 2012 18:12:30 +0530 Subject: [PATCH 10/16] Added a timeout to last.fm album art calls from the post processor --- headphones/albumart.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/headphones/albumart.py b/headphones/albumart.py index 3b80e5f5..5fd6f8a8 100644 --- a/headphones/albumart.py +++ b/headphones/albumart.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . +import urllib2 from headphones import db def getAlbumArt(albumid): @@ -36,7 +37,7 @@ def getCachedArt(albumid): return None if artwork_path.startswith('http://'): - artwork = urllib.urlopen(artwork_path).read() + artwork = urllib2.urlopen(artwork_path, timeout=20).read() return artwork else: artwork = open(artwork_path, "r").read() From 4f8e5877a7e4642984f556338c5cbfd3b11ed5cb Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 25 Jul 2012 20:18:43 +0530 Subject: [PATCH 11/16] Added jquery to allow adding/removing newznab providers --- data/interfaces/default/config.html | 41 ++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index b5d62ecc..3a0ac8a8 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -199,19 +199,22 @@ m<%inherit file="base.html"/>
-
-
- - - e.g. http://nzb.su -
-
- - -
-
- +
+
+
+ + + e.g. http://nzb.su +
+
+ + +
+
+ +
+
@@ -792,7 +795,19 @@ m<%inherit file="base.html"/> initConfigCheckbox("#useapi"); } $(document).ready(function() { - initThisPage(); + initThisPage(); + $("#add_newznab").click(function() { + var intIdPrev = $("#newznab_providers > div").size() + var intId = intIdPrev + 1; + var formfields = $("
"); + var removeButton = $("
"); + removeButton.click(function() { + $(this).parent().remove(); + }); + formfields.append(removeButton); + formfields.append("
"); + $("#newznab" + intIdPrev).append(formfields); + }); }); From 0cf59d94ffea31f277e1cdde45fec671a14e0875 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 25 Jul 2012 20:33:11 +0530 Subject: [PATCH 12/16] Allow for extra newznabs to be inserted into the form on load --- data/interfaces/default/config.html | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 3a0ac8a8..9bceb8db 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -214,6 +214,28 @@ m<%inherit file="base.html"/>
+ <% + newznab_number = 2 + %> + %for newznab in config['extra_newznabs']: +
+
+ + + e.g. http://nzb.su +
+
+ + +
+
+ +
+
+ <% + newznab_number += 1 + %> + %endfor @@ -799,7 +821,7 @@ m<%inherit file="base.html"/> $("#add_newznab").click(function() { var intIdPrev = $("#newznab_providers > div").size() var intId = intIdPrev + 1; - var formfields = $("
"); + var formfields = $("
"); var removeButton = $("
"); removeButton.click(function() { $(this).parent().remove(); From cc2adb40ebda1381cb0421e720e9a316dc826ac6 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 26 Jul 2012 01:29:16 +0530 Subject: [PATCH 13/16] Extra newznabs can be saved to and pulled from the config --- data/interfaces/default/config.html | 27 ++++++++++++++++++++------- headphones/__init__.py | 5 ++++- headphones/webserve.py | 19 ++++++++++++++++++- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 9bceb8db..5f29847b 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1,6 +1,7 @@ m<%inherit file="base.html"/> <%! import headphones + from operator import itemgetter %> <%def name="headerIncludes()"> @@ -217,19 +218,27 @@ m<%inherit file="base.html"/> <% newznab_number = 2 %> - %for newznab in config['extra_newznabs']: + %for newznab in sorted(config['extra_newznabs'], key=itemgetter(0)): + <% + if newznab[2]: + newznab_enabled = "checked" + else: + newznab_enabled = "" + %>
- e.g. http://nzb.su
- +
- + +
+
+
<% @@ -804,6 +813,10 @@ m<%inherit file="base.html"/> $("#mirror").change(handleNewSelection); handleNewSelection.apply($("#mirror")); + + $(".remove").click(function() { + $(this).parent().parent().remove(); + }); $(function() { $( "#tabs" ).tabs(); }); @@ -819,16 +832,16 @@ m<%inherit file="base.html"/> $(document).ready(function() { initThisPage(); $("#add_newznab").click(function() { - var intIdPrev = $("#newznab_providers > div").size() + var intIdPrev = $("#newznab_providers > div").size(); var intId = intIdPrev + 1; - var formfields = $("
"); + var formfields = $("
"); var removeButton = $("
"); removeButton.click(function() { $(this).parent().remove(); }); formfields.append(removeButton); formfields.append("
"); - $("#newznab" + intIdPrev).append(formfields); + $("#newznab" + intIdPrev).after(formfields); }); }); diff --git a/headphones/__init__.py b/headphones/__init__.py index 91b396f7..f8eacc1a 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -122,6 +122,7 @@ NEWZNAB = False NEWZNAB_HOST = None NEWZNAB_APIKEY = None NEWZNAB_ENABLED = False +EXTRA_NEWZNABS = [] NZBSORG = False NZBSORG_UID = None @@ -241,7 +242,7 @@ def initialize(): ADD_ALBUM_ART, EMBED_ALBUM_ART, EMBED_LYRICS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \ TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, DOWNLOAD_TORRENT_DIR, \ LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \ - NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, \ + NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS,\ NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, \ ENCODERFOLDER, ENCODER, BITRATE, SAMPLINGFREQUENCY, MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, \ ENCODERLOSSLESS, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, MIRRORLIST, MIRROR, CUSTOMHOST, CUSTOMPORT, \ @@ -342,6 +343,7 @@ def initialize(): NEWZNAB_HOST = check_setting_str(CFG, 'Newznab', 'newznab_host', '') NEWZNAB_APIKEY = check_setting_str(CFG, 'Newznab', 'newznab_apikey', '') NEWZNAB_ENABLED = bool(check_setting_int(CFG, 'Newznab', 'newznab_enabled', 1)) + EXTRA_NEWZNABS = check_setting_str(CFG, 'Newznab', 'extra_newznabs', [], log=False) NZBSORG = bool(check_setting_int(CFG, 'NZBsorg', 'nzbsorg', 0)) NZBSORG_UID = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_uid', '') @@ -616,6 +618,7 @@ def config_write(): new_config['Newznab']['newznab_host'] = NEWZNAB_HOST new_config['Newznab']['newznab_apikey'] = NEWZNAB_APIKEY new_config['Newznab']['newznab_enabled'] = int(NEWZNAB_ENABLED) + new_config['Newznab']['extra_newznabs'] = EXTRA_NEWZNABS new_config['NZBsorg'] = {} new_config['NZBsorg']['nzbsorg'] = int(NZBSORG) diff --git a/headphones/webserve.py b/headphones/webserve.py index 93865a6a..2d190bf8 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -385,6 +385,7 @@ class WebInterface(object): "newznab_host" : headphones.NEWZNAB_HOST, "newznab_api" : headphones.NEWZNAB_APIKEY, "newznab_enabled" : checked(headphones.NEWZNAB_ENABLED), + "extra_newznabs" : headphones.EXTRA_NEWZNABS, "use_nzbsorg" : checked(headphones.NZBSORG), "nzbsorg_uid" : headphones.NZBSORG_UID, "nzbsorg_hash" : headphones.NZBSORG_HASH, @@ -465,7 +466,7 @@ class WebInterface(object): rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, folder_format=None, file_format=None, include_extras=0, autowant_upcoming=False, autowant_all=False, interface=None, log_dir=None, music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, prowl_enabled=0, prowl_onsnatch=0, prowl_keys=None, prowl_priority=0, xbmc_enabled=0, xbmc_host=None, xbmc_username=None, xbmc_password=None, xbmc_update=0, xbmc_notify=0, - nma_enabled=False, nma_apikey=None, nma_priority=0, synoindex_enabled=False, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None): + nma_enabled=False, nma_apikey=None, nma_priority=0, synoindex_enabled=False, mirror=None, customhost=None, customport=None, customsleep=None, hpuser=None, hppass=None, **kwargs): headphones.HTTP_HOST = http_host headphones.HTTP_PORT = http_port @@ -556,6 +557,22 @@ class WebInterface(object): headphones.CUSTOMSLEEP = customsleep headphones.HPUSER = hpuser headphones.HPPASS = hppass + + # Handle the variable config options. Note - keys with False values aren't getting passed + + headphones.EXTRA_NEWZNABS = [] + + for kwarg in kwargs: + if kwarg.startswith('newznab_host'): + newznab_number = kwarg[12:] + newznab_host = kwargs['newznab_host' + newznab_number] + newznab_api = kwargs['newznab_api' + newznab_number] + try: + newznab_enabled = int(kwargs['newznab_enabled' + newznab_number]) + except KeyError: + newznab_enabled = 0 + + headphones.EXTRA_NEWZNABS.append([newznab_host, newznab_api, newznab_enabled]) headphones.config_write() From 362338926c9aad9e961a73dfa83b0cc35934a632 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 26 Jul 2012 01:41:52 +0530 Subject: [PATCH 14/16] Make sure we can still add new newznab providers after a config save without refreshing the page. Moved the add function from the document ready function to the main functions --- data/interfaces/default/config.html | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 5f29847b..120ec49a 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -817,6 +817,20 @@ m<%inherit file="base.html"/> $(".remove").click(function() { $(this).parent().parent().remove(); }); + + $("#add_newznab").click(function() { + var intIdPrev = $("#newznab_providers > div").size(); + var intId = intIdPrev + 1; + var formfields = $("
"); + var removeButton = $("
"); + removeButton.click(function() { + $(this).parent().remove(); + }); + formfields.append(removeButton); + formfields.append("
"); + $("#newznab" + intIdPrev).after(formfields); + }); + $(function() { $( "#tabs" ).tabs(); }); @@ -831,18 +845,6 @@ m<%inherit file="base.html"/> } $(document).ready(function() { initThisPage(); - $("#add_newznab").click(function() { - var intIdPrev = $("#newznab_providers > div").size(); - var intId = intIdPrev + 1; - var formfields = $("
"); - var removeButton = $("
"); - removeButton.click(function() { - $(this).parent().remove(); - }); - formfields.append(removeButton); - formfields.append("
"); - $("#newznab" + intIdPrev).after(formfields); - }); }); From 351c5de23ebc5c9c2ce1c94cce77207f7cffa5c3 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 26 Jul 2012 02:05:54 +0530 Subject: [PATCH 15/16] Modified searcher.py to use the new multiple newznab format --- headphones/searcher.py | 81 +++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/headphones/searcher.py b/headphones/searcher.py index 95e14a5e..a7808220 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -216,6 +216,16 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): logger.info(u"No results found from NZBMatrix for %s" % term) if headphones.NEWZNAB: + + newznab_hosts = [[headphones.NEWZNAB_HOST, headphones.NEWZNAB_APIKEY, headphones.NEWZNAB_ENABLED]] + + # This is just to make sure we don't have any empty string for EXTRA_NEWZNABS + if not headphones.EXTRA_NEWZNABS: + headphones.EXTRA_NEWZNABS = [] + + for newznab_host in headphones.EXTRA_NEWZNABS: + newznab_hosts.append(newznab_host) + provider = "newznab" if headphones.PREFERRED_QUALITY == 3 or losslessOnly: categories = "3040" @@ -227,44 +237,49 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): if albums['Type'] == 'Other': categories = "3030" logger.info("Album type is audiobook/spokenword. Using audiobook category") + + for newznab_host in newznab_hosts: + + if newznab_host[2] == 0 or newznab_host[2] == '0': + continue - params = { "t": "search", - "apikey": headphones.NEWZNAB_APIKEY, - "cat": categories, - "maxage": headphones.USENET_RETENTION, - "q": term - } - - searchURL = headphones.NEWZNAB_HOST + '/api?' + urllib.urlencode(params) - - logger.info(u'Parsing results from %s' % (searchURL, headphones.NEWZNAB_HOST)) + params = { "t": "search", + "apikey": newznab_host[1], + "cat": categories, + "maxage": headphones.USENET_RETENTION, + "q": term + } - try: - data = urllib2.urlopen(searchURL, timeout=20).read() - except urllib2.URLError, e: - logger.warn('Error fetching data from %s: %s' % (headphones.NEWZNAB_HOST, e)) - data = False + searchURL = newznab_host[0] + '/api?' + urllib.urlencode(params) + + logger.info(u'Parsing results from %s' % (searchURL, newznab_host[0])) - if data: - - d = feedparser.parse(data) + try: + data = urllib2.urlopen(searchURL, timeout=20).read() + except urllib2.URLError, e: + logger.warn('Error fetching data from %s: %s' % (newznab_host[0], e)) + data = False + + if data: - if not len(d.entries): - logger.info(u"No results found from %s for %s" % (headphones.NEWZNAB_HOST, term)) - pass - - else: - for item in d.entries: - try: - url = item.link - title = item.title - size = int(item.links[1]['length']) + d = feedparser.parse(data) + + if not len(d.entries): + logger.info(u"No results found from %s for %s" % (newznab_host[0], term)) + pass + + else: + for item in d.entries: + try: + url = item.link + title = item.title + size = int(item.links[1]['length']) + + resultlist.append((title, size, url, provider)) + logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - resultlist.append((title, size, url, provider)) - logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) - - except Exception, e: - logger.error(u"An unknown error occurred trying to parse the feed: %s" % e) + except Exception, e: + logger.error(u"An unknown error occurred trying to parse the feed: %s" % e) if headphones.NZBSORG: provider = "nzbsorg" From f8ef52b8eefa4fd6828c138fdd08131f7ef6f0f0 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 26 Jul 2012 17:22:54 +0530 Subject: [PATCH 16/16] Some fixes to get mult_newznabs working: unpack & repack settings when saving to/pulling from config, modified searcher.py to work with tuples, fixed config.html to create new intIds no matter what, place new newznabs before add button, instead of after last div --- data/interfaces/default/config.html | 15 +++++++++------ headphones/__init__.py | 15 ++++++++++++--- headphones/searcher.py | 12 +++--------- headphones/webserve.py | 2 +- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 120ec49a..216e4eb6 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -1,7 +1,6 @@ m<%inherit file="base.html"/> <%! import headphones - from operator import itemgetter %> <%def name="headerIncludes()"> @@ -218,9 +217,9 @@ m<%inherit file="base.html"/> <% newznab_number = 2 %> - %for newznab in sorted(config['extra_newznabs'], key=itemgetter(0)): + %for newznab in config['extra_newznabs']: <% - if newznab[2]: + if newznab[2] == '1' or newznab[2] == 1: newznab_enabled = "checked" else: newznab_enabled = "" @@ -814,21 +813,25 @@ m<%inherit file="base.html"/> $("#mirror").change(handleNewSelection); handleNewSelection.apply($("#mirror")); + var deletedNewznabs = 0; + $(".remove").click(function() { $(this).parent().parent().remove(); + deletedNewznabs = deletedNewznabs + 1; }); $("#add_newznab").click(function() { - var intIdPrev = $("#newznab_providers > div").size(); - var intId = intIdPrev + 1; + var intId = $("#newznab_providers > div").size() + deletedNewznabs + 1; var formfields = $("
"); var removeButton = $("
"); removeButton.click(function() { $(this).parent().remove(); + deletedNewznabs = deletedNewznabs + 1; + }); formfields.append(removeButton); formfields.append("
"); - $("#newznab" + intIdPrev).after(formfields); + $("#add_newznab").before(formfields); }); $(function() { diff --git a/headphones/__init__.py b/headphones/__init__.py index f8eacc1a..265a016e 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -20,6 +20,7 @@ import os, sys, subprocess import threading import webbrowser import sqlite3 +import itertools from lib.apscheduler.scheduler import Scheduler from lib.configobj import ConfigObj @@ -229,7 +230,6 @@ def check_setting_str(config, cfg_name, item_name, def_val, log=True): else: logger.debug(item_name + " -> ******") return my_val - def initialize(): @@ -343,7 +343,10 @@ def initialize(): NEWZNAB_HOST = check_setting_str(CFG, 'Newznab', 'newznab_host', '') NEWZNAB_APIKEY = check_setting_str(CFG, 'Newznab', 'newznab_apikey', '') NEWZNAB_ENABLED = bool(check_setting_int(CFG, 'Newznab', 'newznab_enabled', 1)) - EXTRA_NEWZNABS = check_setting_str(CFG, 'Newznab', 'extra_newznabs', [], log=False) + + # Need to pack the extra newznabs back into a list of tuples + flattened_newznabs = check_setting_str(CFG, 'Newznab', 'extra_newznabs', [], log=False) + EXTRA_NEWZNABS = list(itertools.izip(*[itertools.islice(flattened_newznabs, i, None, 3) for i in range(3)])) NZBSORG = bool(check_setting_int(CFG, 'NZBsorg', 'nzbsorg', 0)) NZBSORG_UID = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_uid', '') @@ -618,7 +621,13 @@ def config_write(): new_config['Newznab']['newznab_host'] = NEWZNAB_HOST new_config['Newznab']['newznab_apikey'] = NEWZNAB_APIKEY new_config['Newznab']['newznab_enabled'] = int(NEWZNAB_ENABLED) - new_config['Newznab']['extra_newznabs'] = EXTRA_NEWZNABS + # Need to unpack the extra newznabs for saving in config.ini + flattened_newznabs = [] + for newznab in EXTRA_NEWZNABS: + for item in newznab: + flattened_newznabs.append(item) + + new_config['Newznab']['extra_newznabs'] = flattened_newznabs new_config['NZBsorg'] = {} new_config['NZBsorg']['nzbsorg'] = int(NZBSORG) diff --git a/headphones/searcher.py b/headphones/searcher.py index a7808220..85b699e5 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -217,14 +217,11 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): if headphones.NEWZNAB: - newznab_hosts = [[headphones.NEWZNAB_HOST, headphones.NEWZNAB_APIKEY, headphones.NEWZNAB_ENABLED]] - - # This is just to make sure we don't have any empty string for EXTRA_NEWZNABS - if not headphones.EXTRA_NEWZNABS: - headphones.EXTRA_NEWZNABS = [] + newznab_hosts = [(headphones.NEWZNAB_HOST, headphones.NEWZNAB_APIKEY, headphones.NEWZNAB_ENABLED)] for newznab_host in headphones.EXTRA_NEWZNABS: - newznab_hosts.append(newznab_host) + if newznab_host[2] == '1' or newznab_host[2] == 1: + newznab_hosts.append(newznab_host) provider = "newznab" if headphones.PREFERRED_QUALITY == 3 or losslessOnly: @@ -239,9 +236,6 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): logger.info("Album type is audiobook/spokenword. Using audiobook category") for newznab_host in newznab_hosts: - - if newznab_host[2] == 0 or newznab_host[2] == '0': - continue params = { "t": "search", "apikey": newznab_host[1], diff --git a/headphones/webserve.py b/headphones/webserve.py index 2d190bf8..2b2e89fd 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -572,7 +572,7 @@ class WebInterface(object): except KeyError: newznab_enabled = 0 - headphones.EXTRA_NEWZNABS.append([newznab_host, newznab_api, newznab_enabled]) + headphones.EXTRA_NEWZNABS.append((newznab_host, newznab_api, newznab_enabled)) headphones.config_write()