From 865e4451c10e98be5017f9f8b6ea5b3af9df90ee Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 14 Aug 2012 12:20:37 +0530 Subject: [PATCH 01/35] Added Status filter to webserve --- headphones/webserve.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/headphones/webserve.py b/headphones/webserve.py index 2b2e89fd..d49f338d 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -229,9 +229,12 @@ class WebInterface(object): return serve_template(templatename="manageartists.html", title="Manage Artists", artists=artists) manageArtists.exposed = True - def manageAlbums(self): + def manageAlbums(self, Status=None): myDB = db.DBConnection() - albums = myDB.select('SELECT * from albums') + if Status: + albums = myDB.select('SELECT * from albums WHERE Status=?', [Status]) + else: + albums = myDB.select('SELECT * from albums') return serve_template(templatename="managealbums.html", title="Manage Albums", albums=albums) manageAlbums.exposed = True From 0f2f48543b745bb579b8c1ae4fc24fb324290e16 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 14 Aug 2012 12:39:24 +0530 Subject: [PATCH 02/35] Added dialog popup to default interface for manage albums filter --- data/interfaces/default/js/script.js | 1 + data/interfaces/default/manage.html | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/data/interfaces/default/js/script.js b/data/interfaces/default/js/script.js index b1ac046d..3ac9c342 100644 --- a/data/interfaces/default/js/script.js +++ b/data/interfaces/default/js/script.js @@ -144,6 +144,7 @@ function initConfigCheckbox(elem) { function initActions() { $("#subhead_menu #menu_link_refresh").button({ icons: { primary: "ui-icon-refresh" } }); $("#subhead_menu #menu_link_edit").button({ icons: { primary: "ui-icon-pencil" } }); + $("#subhead_menu .menu_link_edit").button({ icons: { primary: "ui-icon-pencil" } }); $("#subhead_menu #menu_link_delete" ).button({ icons: { primary: "ui-icon-trash" } }); $("#subhead_menu #menu_link_pauze").button({ icons: { primary: "ui-icon-pause"} }); $("#subhead_menu #menu_link_resume").button({ icons: { primary: "ui-icon-play"} }); diff --git a/data/interfaces/default/manage.html b/data/interfaces/default/manage.html index ed62e445..c4786f91 100644 --- a/data/interfaces/default/manage.html +++ b/data/interfaces/default/manage.html @@ -6,10 +6,21 @@ <%def name="headerIncludes()"> @@ -95,6 +106,10 @@ <%def name="javascriptIncludes()"> From 7ea9bbf57d0a0ae0ed76b2650758d54854336463 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 15 Aug 2012 16:32:26 +0530 Subject: [PATCH 15/35] Fixed issue retrieving release if no releasedate --- headphones/mb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/mb.py b/headphones/mb.py index c98e5b07..5f9d168d 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -313,8 +313,8 @@ def getRelease(releaseid, include_artist_info=True): release['title'] = unicode(results['title']) release['id'] = unicode(results['id']) - release['asin'] = unicode(results['asin']) if 'asin' in results else None - release['date'] = unicode(results['date']) + release['asin'] = unicode(results['asin']) if 'asin' in results else u'None' + release['date'] = unicode(results['date']) if 'date' in results else u'None' try: release['format'] = unicode(results['medium-list'][0]['format']) except: From 56e5c7ae1da2b94c2699708fa7f80677cfe33668 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Wed, 15 Aug 2012 17:56:15 +0530 Subject: [PATCH 16/35] Added a timeout to lyrics fetching --- headphones/lyrics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/lyrics.py b/headphones/lyrics.py index c8eda8a6..6899359a 100644 --- a/headphones/lyrics.py +++ b/headphones/lyrics.py @@ -14,7 +14,7 @@ # along with Headphones. If not, see . import re -import urllib +import urllib, urllib2 from xml.dom import minidom import htmlentitydefs @@ -30,7 +30,7 @@ def getLyrics(artist, song): searchURL = 'http://lyrics.wikia.com/api.php?' + urllib.urlencode(params) try: - data = urllib.urlopen(searchURL).read() + data = urllib2.urlopen(searchURL, timeout=20).read() except Exception, e: logger.warn('Error opening: %s. Error: %s' % (searchURL, e)) return From 87e8e35985557f55de080039cdd05a7caa3793e3 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 16 Aug 2012 18:19:32 +0530 Subject: [PATCH 17/35] Fixed bug where fetching album art from Last.FM would hang if no album art was found --- headphones/postprocessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 3e4125bf..6a90c706 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -240,7 +240,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list) if len(artwork) < 100: logger.info("No suitable album art found from Amazon. Checking Last.FM....") artwork = albumart.getCachedArt(albumid) - if len(artwork) < 100: + if not artwork or len(artwork) < 100: artwork = False logger.info("No suitable album art found from Last.FM. Not adding album art") From f2e0afac2550e9dd59ea81d96acce84d759f41b9 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 16 Aug 2012 21:40:15 +0530 Subject: [PATCH 18/35] Fire off a searcher thread when adding albums with Mark All Albums as Wanted option is set --- headphones/importer.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/headphones/importer.py b/headphones/importer.py index 473ef59c..cf58fa63 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -338,6 +338,11 @@ def addArtisttoDB(artistid, extrasonly=False): if headphones.AUTOWANT_ALL: newValueDict['Status'] = "Wanted" + + #start a search for the album + import searcher + searcher.searchforalbum(albumid=rg['id']) + elif album['ReleaseDate'] > helpers.today() and headphones.AUTOWANT_UPCOMING: newValueDict['Status'] = "Wanted" else: @@ -522,7 +527,7 @@ def addReleaseById(rid): #start a search for the album import searcher - searcher.searchNZB(rgid, False) + searcher.searchforalbum(rgid, False) elif not rg_exists and not release_dict: logger.error("ReleaseGroup does not exist in the database and did not get a valid response from MB. Skipping release.") return From 625b0efd13024ee373abfc68a9a8b4e7bbdf5f1e Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 16 Aug 2012 22:54:33 +0530 Subject: [PATCH 19/35] A couple of changes to library sync --- data/interfaces/default/album.html | 4 +- headphones/importer.py | 4 +- headphones/librarysync.py | 244 +++++++++++++++++++++++++---- 3 files changed, 218 insertions(+), 34 deletions(-) diff --git a/data/interfaces/default/album.html b/data/interfaces/default/album.html index 20a8f659..fce97271 100644 --- a/data/interfaces/default/album.html +++ b/data/interfaces/default/album.html @@ -176,8 +176,6 @@ $('#refresh_artist').click(function() { $('#dialog').dialog("close"); }); - getAlbumInfo(); - getAlbumArt(); initActions(); setTimeout(function(){ initFancybox(); @@ -193,6 +191,8 @@ }; $(document).ready(function() { + getAlbumInfo(); + getAlbumArt(); initThisPage(); }); diff --git a/headphones/importer.py b/headphones/importer.py index cf58fa63..c92a0d49 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -236,7 +236,7 @@ def addArtisttoDB(artistid, extrasonly=False): newValueDict['Location'] = match['Location'] newValueDict['BitRate'] = match['BitRate'] newValueDict['Format'] = match['Format'] - myDB.action('UPDATE tracks SET Matched="True" WHERE Location=?', match['Location']) + myDB.action('UPDATE have SET Matched="True" WHERE Location=?', [match['Location']]) myDB.upsert("alltracks", newValueDict, controlValueDict) @@ -287,7 +287,7 @@ def addArtisttoDB(artistid, extrasonly=False): newValueDict['Location'] = match['Location'] newValueDict['BitRate'] = match['BitRate'] newValueDict['Format'] = match['Format'] - myDB.action('UPDATE tracks SET Matched="True" WHERE Location=?', match['Location']) + myDB.action('UPDATE have SET Matched="True" WHERE Location=?', [match['Location']]) myDB.upsert("alltracks", newValueDict, controlValueDict) diff --git a/headphones/librarysync.py b/headphones/librarysync.py index 8dfd4dd5..b7484b1c 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -45,6 +45,8 @@ def libraryScan(dir=None): new_artists = [] bitrates = [] + + song_list = [] myDB.action('DELETE from have') @@ -69,44 +71,224 @@ def libraryScan(dir=None): # Grab the bitrates for the auto detect bit rate option if f.bitrate: bitrates.append(f.bitrate) - - # Try to find a match based on artist/album/tracktitle + + # Use the album artist over the artist if available if f.albumartist: f_artist = f.albumartist elif f.artist: f_artist = f.artist else: - continue - - if f_artist and f.album and f.title: - - track = myDB.action('SELECT TrackID from tracks WHERE CleanName LIKE ?', [helpers.cleanName(f_artist +' '+f.album+' '+f.title)]).fetchone() - - if not track: - track = myDB.action('SELECT TrackID from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [f_artist, f.album, f.title]).fetchone() + f_artist = None - if track: - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [unicode_song_path, f.bitrate, f.format, track['TrackID']]) - continue - - # Try to match on mbid if available and we couldn't find a match based on metadata - if f.mb_trackid: + # Add the song to our song list - + # TODO: skip adding songs without the minimum requisite information (just a matter of putting together the right if statements) - # Wondering if theres a better way to do this -> do one thing if the row exists, - # do something else if it doesn't - track = myDB.action('SELECT TrackID from tracks WHERE TrackID=?', [f.mb_trackid]).fetchone() + song_dict = { 'TrackID' : f.mb_trackid, + 'ReleaseID' : f.mb_albumid, + 'ArtistName' : f_artist, + 'AlbumTitle' : f.album, + 'TrackNumber': f.track, + 'TrackLength': f.length, + 'Genre' : f.genre, + 'Date' : f.date, + 'TrackTitle' : f.title, + 'BitRate' : f.bitrate, + 'Format' : f.format, + 'Location' : unicode_song_path } + + song_list.append(song_dict) + + # Now we start track matching + total_number_of_songs = len(song_list) + logger.info("Found " + str(total_number_of_songs) + " tracks in: '" + dir + "'. Matching tracks to the appropriate releases....") + + # Sort the song_list by most vague (e.g. no trackid or releaseid) to most specific (both trackid & releaseid) + # When we insert into the database, the tracks with the most specific information will overwrite the more general matches + + song_list = helpers.multikeysort(song_list, ['ReleaseID', 'TrackID']) + + # We'll use this to give a % completion, just because the track matching might take a while + song_count = 0 + + for song in song_list: - if track: - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [unicode_song_path, f.bitrate, f.format, track['TrackID']]) - continue - - # if we can't find a match in the database on a track level, it might be a new artist or it might be on a non-mb release - new_artists.append(f_artist) - - # The have table will become the new database for unmatched tracks (i.e. tracks with no associated links in the database - myDB.action('INSERT INTO have (ArtistName, AlbumTitle, TrackNumber, TrackTitle, TrackLength, BitRate, Genre, Date, TrackID, Location, CleanName, Format) VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [f_artist, f.album, f.track, f.title, f.length, f.bitrate, f.genre, f.date, f.mb_trackid, unicode_song_path, helpers.cleanName(f_artist+' '+f.album+' '+f.title), f.format]) + song_count += 1 + completion_percentage = float(song_count)/total_number_of_songs * 100 + + if completion_percentage%10 == 0: + logger.info("Track matching is " + str(completion_percentage) + "% complete") + + # If the track has a trackid & releaseid (beets: albumid) that the most surefire way + # of identifying a track to a specific release so we'll use that first + if song['TrackID'] and song['ReleaseID']: - logger.info('Completed scanning directory: %s' % dir) + # Check both the tracks table & alltracks table in case they haven't populated the alltracks table yet + track = myDB.action('SELECT TrackID, ReleaseID, AlbumID from alltracks WHERE TrackID=? AND ReleaseID=?', [song['TrackID'], song['ReleaseID']]).fetchone() + + # It might be the case that the alltracks table isn't populated yet, so maybe we can only find a match in the tracks table + if not track: + track = myDB.action('SELECT TrackID, ReleaseID, AlbumID from tracks WHERE TrackID=? AND ReleaseID=?', [song['TrackID'], song['ReleaseID']]).fetchone() + + if track: + # Use TrackID & ReleaseID here since there can only be one possible match with a TrackID & ReleaseID query combo + controlValueDict = { 'TrackID' : track['TrackID'], + 'ReleaseID' : track['ReleaseID'] } + + # Insert it into the Headphones hybrid release (ReleaseID == AlbumID) + hybridControlValueDict = { 'TrackID' : track['TrackID'], + 'ReleaseID' : track['AlbumID'] } + + newValueDict = { 'Location' : song['Location'], + 'BitRate' : song['BitRate'], + 'Format' : song['Format'] } + + # Update both the tracks table and the alltracks table using the controlValueDict and hybridControlValueDict + myDB.upsert("alltracks", newValueDict, controlValueDict) + myDB.upsert("tracks", newValueDict, controlValueDict) + + myDB.upsert("alltracks", newValueDict, hybridControlValueDict) + myDB.upsert("tracks", newValueDict, hybridControlValueDict) + + # Matched. Move on to the next one: + continue + + # If we can't find it with TrackID & ReleaseID, next most specific will be + # releaseid + tracktitle, although perhaps less reliable due to a higher + # likelihood of variations in the song title (e.g. feat. artists) + if song['ReleaseID'] and song['TrackTitle']: + + track = myDB.action('SELECT TrackID, ReleaseID, AlbumID from alltracks WHERE ReleaseID=? AND TrackTitle=?', [song['ReleaseID'], song['TrackTitle']]).fetchone() + + if not track: + track = myDB.action('SELECT TrackID, ReleaseID, AlbumID from tracks WHERE ReleaseID=? AND TrackTitle=?', [song['ReleaseID'], song['TrackTitle']]).fetchone() + + if track: + # There can also only be one match for this query as well (although it might be on both the tracks and alltracks table) + # So use both TrackID & ReleaseID as the control values + controlValueDict = { 'TrackID' : track['TrackID'], + 'ReleaseID' : track['ReleaseID'] } + + hybridControlValueDict = { 'TrackID' : track['TrackID'], + 'ReleaseID' : track['AlbumID'] } + + newValueDict = { 'Location' : song['Location'], + 'BitRate' : song['BitRate'], + 'Format' : song['Format'] } + + # Update both tables here as well + myDB.upsert("alltracks", newValueDict, controlValueDict) + myDB.upsert("tracks", newValueDict, controlValueDict) + + myDB.upsert("alltracks", newValueDict, hybridControlValueDict) + myDB.upsert("tracks", newValueDict, hybridControlValueDict) + + # Done + continue + + # Next most specific will be the opposite: a TrackID and an AlbumTitle + # TrackIDs span multiple releases so if something is on an official album + # and a compilation, for example, this will match it to the right one + # However - there may be multiple matches here + if song['TrackID'] and song['AlbumTitle']: + + # Even though there might be multiple matches, we just need to grab one to confirm a match + track = myDB.action('SELECT TrackID, AlbumTitle from alltracks WHERE TrackID=? AND AlbumTitle LIKE ?', [song['TrackID'], song['AlbumTitle']]).fetchone() + + if not track: + track = myDB.action('SELECT TrackID, AlbumTitle from tracks WHERE TrackID=? AND AlbumTitle LIKE ?', [song['TrackID'], song['AlbumTitle']]).fetchone() + + if track: + # Don't need the hybridControlValueDict here since ReleaseID is not unique + controlValueDict = { 'TrackID' : track['TrackID'], + 'AlbumTitle' : track['AlbumTitle'] } + + newValueDict = { 'Location' : song['Location'], + 'BitRate' : song['BitRate'], + 'Format' : song['Format'] } + + myDB.upsert("alltracks", newValueDict, controlValueDict) + myDB.upsert("tracks", newValueDict, controlValueDict) + + continue + + # Next most specific is the ArtistName + AlbumTitle + TrackTitle combo (but probably + # even more unreliable than the previous queries, and might span multiple releases) + if song['ArtistName'] and song['AlbumTitle'] and song['TrackTitle']: + + track = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle from alltracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone() + + if not track: + track = myDB.action('SELECT ArtistName, AlbumTitle, TrackTitle from tracks WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [song['ArtistName'], song['AlbumTitle'], song['TrackTitle']]).fetchone() + + if track: + controlValueDict = { 'ArtistName' : track['ArtistName'], + 'AlbumTitle' : track['AlbumTitle'], + 'TrackTitle' : track['TrackTitle'] } + + newValueDict = { 'Location' : song['Location'], + 'BitRate' : song['BitRate'], + 'Format' : song['Format'] } + + myDB.upsert("alltracks", newValueDict, controlValueDict) + myDB.upsert("tracks", newValueDict, controlValueDict) + + continue + + # Use the "CleanName" (ArtistName + AlbumTitle + TrackTitle stripped of punctuation, capitalization, etc) + # This is more reliable than the former but requires some string manipulation so we'll do it only + # if we can't find a match with the original data + if song['ArtistName'] and song['AlbumTitle'] and song['TrackTitle']: + + CleanName = helpers.cleanName(song['ArtistName'] +' '+ song['AlbumTitle'] +' '+song['TrackTitle']) + + track = myDB.action('SELECT CleanName from alltracks WHERE CleanName LIKE ?', [CleanName]).fetchone() + + if not track: + track = myDB.action('SELECT CleanName from tracks WHERE CleanName LIKE ?', [CleanName]).fetchone() + + if track: + controlValueDict = { 'CleanName' : track['CleanName'] } + + newValueDict = { 'Location' : song['Location'], + 'BitRate' : song['BitRate'], + 'Format' : song['Format'] } + + myDB.upsert("alltracks", newValueDict, controlValueDict) + myDB.upsert("tracks", newValueDict, controlValueDict) + + continue + + # Match on TrackID alone if we can't find it using any of the above methods. This method is reliable + # but spans multiple releases - but that's why we're putting at the beginning as a last resort. If a track + # with more specific information exists in the library, it'll overwrite these values + if song['TrackID']: + + track = myDB.action('SELECT TrackID from alltracks WHERE TrackID=?', [song['TrackID']]).fetchone() + + if not track: + track = myDB.action('SELECT TrackID from tracks WHERE TrackID=?', [song['TrackID']]).fetchone() + + if track: + controlValueDict = { 'TrackID' : track['TrackID'] } + + newValueDict = { 'Location' : song['Location'], + 'BitRate' : song['BitRate'], + 'Format' : song['Format'] } + + myDB.upsert("alltracks", newValueDict, controlValueDict) + myDB.upsert("tracks", newValueDict, controlValueDict) + + continue + + # if we can't find a match in the database on a track level, it might be a new artist or it might be on a non-mb release + new_artists.append(song['ArtistName']) + + # The have table will become the new database for unmatched tracks (i.e. tracks with no associated links in the database + CleanName = helpers.cleanName(song['ArtistName'] +' '+ song['AlbumTitle'] +' '+song['TrackTitle']) + + myDB.action('INSERT INTO have (ArtistName, AlbumTitle, TrackNumber, TrackTitle, TrackLength, BitRate, Genre, Date, TrackID, Location, CleanName, Format) VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [song['ArtistName'], song['AlbumTitle'], song['TrackNumber'], song['TrackTitle'], song['TrackLength'], song['BitRate'], song['Genre'], song['Date'], song['TrackID'], song['Location'], CleanName, song['Format']]) + + logger.info('Completed matching tracks from directory: %s' % dir) # Clean up the new artist list unique_artists = {}.fromkeys(new_artists).keys() @@ -115,9 +297,11 @@ def libraryScan(dir=None): artist_list = [f for f in unique_artists if f.lower() not in [x[0].lower() for x in current_artists]] # Update track counts - logger.info('Updating track counts') + logger.info('Updating current artist track counts') for artist in current_artists: + # Have tracks are selected from tracks table and not all tracks because of duplicates + # We update the track count upon an album switch to compliment this havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID like ? AND Location IS NOT NULL', [artist['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']])) myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artist['ArtistID']]) From 85c1bf2a3ea71d18dc2fc0c42411e3a12582d439 Mon Sep 17 00:00:00 2001 From: Ade Date: Wed, 8 Aug 2012 20:47:16 +1200 Subject: [PATCH 20/35] Search for Torrent if NZB found but out of retention --- headphones/searcher.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/headphones/searcher.py b/headphones/searcher.py index 85b699e5..50068271 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -496,6 +496,8 @@ def searchNZB(albumid=None, new=False, losslessOnly=False): myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]]) myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", nzb_folder_name]) return "found" + else: + return "none" else: return "none" @@ -673,7 +675,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): data = False if data: - + + logger.info(u'Parsing results from KAT' % searchURL) + d = feedparser.parse(data) if not len(d.entries): logger.info(u"No results found from %s for %s" % (provider, term)) @@ -707,7 +711,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): resultlist.append((title, size, url, provider)) logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) else: - logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeders), rightformat)) + logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeders), rightformat)) except Exception, e: logger.error(u"An unknown error occurred in the KAT parser: %s" % e) @@ -754,7 +758,9 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): data = False if data: - + + logger.info(u'Parsing results from Waffles.fm' % searchURL) + d = feedparser.parse(data) if not len(d.entries): logger.info(u"No results found from %s for %s" % (provider, term)) @@ -780,7 +786,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): if headphones.ISOHUNT: - provider = "ISOhunt" + provider = "isoHunt" providerurl = url_fix("http://isohunt.com/js/rss/" + term) if headphones.PREFERRED_QUALITY == 3 or losslessOnly: categories = "7" #music @@ -809,6 +815,8 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): if data: + logger.info(u'Parsing results from isoHunt' % searchURL) + d = feedparser.parse(data) if not len(d.entries): logger.info(u"No results found from %s for %s" % (provider, term)) @@ -846,10 +854,10 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): resultlist.append((title, size, url, provider)) logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) else: - logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) + logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) except Exception, e: - logger.error(u"An unknown error occurred in the ISOhunt parser: %s" % e) + logger.error(u"An unknown error occurred in the isoHunt parser: %s" % e) if headphones.MININOVA: provider = "Mininova" @@ -877,6 +885,8 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): if data: + logger.info(u'Parsing results from Mininova' % searchURL) + d = feedparser.parse(data) if not len(d.entries): logger.info(u"No results found from %s for %s" % (provider, term)) @@ -913,10 +923,10 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): resultlist.append((title, size, url, provider)) logger.info('Found %s. Size: %s' % (title, helpers.bytes_to_mb(size))) else: - logger.info('%s is larger than the maxsize, the wrong format or has to little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) + logger.info('%s is larger than the maxsize, the wrong format or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i, Format: %s)' % (title, size, int(seeds), rightformat)) except Exception, e: - logger.error(u"An unknown error occurred in the MiniNova Parser: %s" % e) + logger.error(u"An unknown error occurred in the Mininova Parser: %s" % e) @@ -983,7 +993,7 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False): (data, bestqual) = preprocesstorrent(torrentlist) if data and bestqual: - logger.info(u'Found best result: %s - %s' % (bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) + logger.info(u'Found best result from %s: %s - %s' % (bestqual[3], bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1]))) torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year) if headphones.TORRENTBLACKHOLE_DIR == "sendtracker": From ffb4798d04f9dc7d5fd81896ab40021ebcbbb5a8 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 16 Aug 2012 23:09:27 +0530 Subject: [PATCH 21/35] Fix for last.fm query hanging when updating artist info --- headphones/lastfm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/lastfm.py b/headphones/lastfm.py index a55b6c8f..ce77948b 100644 --- a/headphones/lastfm.py +++ b/headphones/lastfm.py @@ -42,7 +42,7 @@ def getSimilar(): time.sleep(1) continue - if len(data) < 200: + if not data or len(data) < 200: continue try: From ffeb4383372851317262132d7d69eab0d2a96390 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Thu, 16 Aug 2012 23:50:32 +0530 Subject: [PATCH 22/35] Modified library scan to allow single directories to be appended to the library; postprocessor now used that function to update track counts after post processing is complete --- headphones/librarysync.py | 87 ++++++++++++++++++++++--------------- headphones/postprocessor.py | 49 ++------------------- 2 files changed, 54 insertions(+), 82 deletions(-) diff --git a/headphones/librarysync.py b/headphones/librarysync.py index b7484b1c..3f31d4c0 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -21,12 +21,16 @@ from lib.beets.mediafile import MediaFile import headphones from headphones import db, logger, helpers, importer -def libraryScan(dir=None): +# You can scan a single directory and append it to the current library by specifying append=True, ArtistID & ArtistName +def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None): if not dir: dir = headphones.MUSIC_DIR - - dir = dir.encode(headphones.SYS_ENCODING) + + # If we're appending a dir, it's coming from the post processor which is + # already bytestring + if not append: + dir = dir.encode(headphones.SYS_ENCODING) if not os.path.isdir(dir): logger.warn('Cannot find directory: %s. Not scanning' % dir.decode(headphones.SYS_ENCODING)) @@ -34,12 +38,15 @@ def libraryScan(dir=None): myDB = db.DBConnection() - # Clean up bad filepaths - tracks = myDB.select('SELECT Location, TrackID from tracks WHERE Location IS NOT NULL') + if not append: + # Clean up bad filepaths + tracks = myDB.select('SELECT Location, TrackID from tracks WHERE Location IS NOT NULL') - for track in tracks: - if not os.path.isfile(track['Location'].encode(headphones.SYS_ENCODING)): - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [None, None, None, track['TrackID']]) + for track in tracks: + if not os.path.isfile(track['Location'].encode(headphones.SYS_ENCODING)): + myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE TrackID=?', [None, None, None, track['TrackID']]) + + myDB.action('DELETE from have') logger.info('Scanning music directory: %s' % dir) @@ -47,8 +54,6 @@ def libraryScan(dir=None): bitrates = [] song_list = [] - - myDB.action('DELETE from have') for r,d,f in os.walk(dir): for files in f: @@ -290,32 +295,42 @@ def libraryScan(dir=None): logger.info('Completed matching tracks from directory: %s' % dir) - # Clean up the new artist list - unique_artists = {}.fromkeys(new_artists).keys() - current_artists = myDB.select('SELECT ArtistName, ArtistID from artists') - artist_list = [f for f in unique_artists if f.lower() not in [x[0].lower() for x in current_artists]] - - # Update track counts - logger.info('Updating current artist track counts') - - for artist in current_artists: - # Have tracks are selected from tracks table and not all tracks because of duplicates - # We update the track count upon an album switch to compliment this - havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID like ? AND Location IS NOT NULL', [artist['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']])) - myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artist['ArtistID']]) + if not append: + # Clean up the new artist list + unique_artists = {}.fromkeys(new_artists).keys() + current_artists = myDB.select('SELECT ArtistName, ArtistID from artists') - logger.info('Found %i new artists' % len(artist_list)) - - if len(artist_list): - if headphones.ADD_ARTISTS: - logger.info('Importing %i new artists' % len(artist_list)) - importer.artistlist_to_mbids(artist_list) - else: - logger.info('To add these artists, go to Manage->Manage New Artists') - myDB.action('DELETE from newartists') - for artist in artist_list: - myDB.action('INSERT into newartists VALUES (?)', [artist]) + artist_list = [f for f in unique_artists if f.lower() not in [x[0].lower() for x in current_artists]] + + # Update track counts + logger.info('Updating current artist track counts') + + for artist in current_artists: + # Have tracks are selected from tracks table and not all tracks because of duplicates + # We update the track count upon an album switch to compliment this + havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artist['ArtistID']])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['ArtistName']])) + myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artist['ArtistID']]) + + logger.info('Found %i new artists' % len(artist_list)) + + if len(artist_list): + if headphones.ADD_ARTISTS: + logger.info('Importing %i new artists' % len(artist_list)) + importer.artistlist_to_mbids(artist_list) + else: + logger.info('To add these artists, go to Manage->Manage New Artists') + myDB.action('DELETE from newartists') + for artist in artist_list: + myDB.action('INSERT into newartists VALUES (?)', [artist]) + + if headphones.DETECT_BITRATE: + headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000 + + else: + # If we're appending a new album to the database, update the artists total track counts + logger.info('Updating artist track counts') + + 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 ?', [ArtistName])) + myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, ArtistID]) - if headphones.DETECT_BITRATE: - headphones.PREFERRED_BITRATE = sum(bitrates)/len(bitrates)/1000 diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 6a90c706..3c855494 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -26,7 +26,7 @@ from lib.beets import autotag from lib.beets.mediafile import MediaFile import headphones -from headphones import db, albumart, lyrics, logger, helpers +from headphones import db, albumart, librarysync, lyrics, logger, helpers postprocessor_lock = threading.Lock() @@ -269,21 +269,8 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list) logger.error('No DESTINATION_DIR has been set. Set "Destination Directory" to the parent directory you want to move the files to') pass - myDB = db.DBConnection() - # There's gotta be a better way to update the have tracks - sqlite - - trackcount = myDB.select('SELECT HaveTracks from artists WHERE ArtistID=?', [release['ArtistID']]) - - if not trackcount[0][0]: - cur_track_count = 0 - else: - cur_track_count = trackcount[0][0] - - new_track_count = cur_track_count + len(downloaded_track_list) - myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [new_track_count, release['ArtistID']]) - myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid]) - myDB.action('UPDATE snatched SET status = "Processed" WHERE AlbumID=?', [albumid]) - updateHave(albumpath) + # Update the have tracks + librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName']) logger.info('Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle'])) @@ -577,36 +564,6 @@ def renameFiles(albumpath, downloaded_track_list, release): except Exception, e: logger.error('Error renaming file: %s. Error: %s' % (downloaded_track, e)) continue - -def updateHave(albumpath): - - results = [] - - for r,d,f in os.walk(albumpath): - for files in f: - if any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS): - results.append(os.path.join(r, files)) - - if results: - - myDB = db.DBConnection() - - for song in results: - try: - f = MediaFile(song) - #logger.debug('Reading: %s' % song.decode('UTF-8')) - except: - logger.warn('Could not read file: %s' % song) - continue - else: - if f.albumartist: - artist = f.albumartist - elif f.artist: - artist = f.artist - else: - continue - - myDB.action('UPDATE tracks SET Location=?, BitRate=?, Format=? WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [unicode(song, headphones.SYS_ENCODING, errors="replace"), f.bitrate, f.format, artist, f.album, f.title]) def renameUnprocessedFolder(albumpath): From 0039a93736c3f01cd15c373317d723431bea5e0e Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 00:28:56 +0530 Subject: [PATCH 23/35] Accidentally removed marking albums as Downloaded after being post processed --- headphones/postprocessor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 3c855494..cd7fcadc 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -269,6 +269,10 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list) logger.error('No DESTINATION_DIR has been set. Set "Destination Directory" to the parent directory you want to move the files to') pass + myDB = db.DBConnection() + myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid]) + myDB.action('UPDATE snatched SET status = "Processed" WHERE AlbumID=?', [albumid]) + # Update the have tracks librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName']) From e2911e4f2b8ad34219a7deb95694de429672c205 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 01:00:28 +0530 Subject: [PATCH 24/35] Added config options for LOSSLESS_DESTINATION_DIR and DELETE_LOSSLESS_FILES --- headphones/__init__.py | 13 ++++++++++--- headphones/webserve.py | 10 +++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/headphones/__init__.py b/headphones/__init__.py index e61cb103..2b938e02 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -84,6 +84,7 @@ CHECK_GITHUB_INTERVAL = None MUSIC_DIR = None DESTINATION_DIR = None +LOSSLESS_DESTINATION_DIR = None FOLDER_FORMAT = None FILE_FORMAT = None PATH_TO_XML = None @@ -165,6 +166,7 @@ ENCODEROUTPUTFORMAT = None ENCODERQUALITY = None ENCODERVBRCBR = None ENCODERLOSSLESS = False +DELETE_LOSSLESS_FILES = False PROWL_ENABLED = True PROWL_PRIORITY = 1 PROWL_KEYS = None @@ -238,7 +240,8 @@ def initialize(): global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \ HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, \ - CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \ + CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \ + LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, \ ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, \ 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, \ @@ -246,7 +249,7 @@ def initialize(): 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, \ + ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, 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, \ ALBUM_COMPLETION_PCT @@ -295,6 +298,7 @@ def initialize(): MUSIC_DIR = check_setting_str(CFG, 'General', 'music_dir', '') DESTINATION_DIR = check_setting_str(CFG, 'General', 'destination_dir', '') + LOSSLESS_DESTINATION_DIR = check_setting_str(CFG, 'General', 'lossless_destination_dir', '') PREFERRED_QUALITY = check_setting_int(CFG, 'General', 'preferred_quality', 0) PREFERRED_BITRATE = check_setting_int(CFG, 'General', 'preferred_bitrate', '') DETECT_BITRATE = bool(check_setting_int(CFG, 'General', 'detect_bitrate', 0)) @@ -373,6 +377,7 @@ def initialize(): ENCODERQUALITY = check_setting_int(CFG, 'General', 'encoderquality', 2) ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr') ENCODERLOSSLESS = bool(check_setting_int(CFG, 'General', 'encoderlossless', 1)) + DELETE_LOSSLESS_FILES = bool(check_setting_int(CFG, 'General', 'delete_lossless_files', 1)) PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0)) PROWL_KEYS = check_setting_str(CFG, 'Prowl', 'prowl_keys', '') @@ -570,6 +575,7 @@ def config_write(): new_config['General']['music_dir'] = MUSIC_DIR new_config['General']['destination_dir'] = DESTINATION_DIR + new_config['General']['lossless_destination_dir'] = LOSSLESS_DESTINATION_DIR new_config['General']['preferred_quality'] = PREFERRED_QUALITY new_config['General']['preferred_bitrate'] = PREFERRED_BITRATE new_config['General']['detect_bitrate'] = int(DETECT_BITRATE) @@ -677,7 +683,8 @@ def config_write(): new_config['General']['encoderoutputformat'] = ENCODEROUTPUTFORMAT new_config['General']['encoderquality'] = ENCODERQUALITY new_config['General']['encodervbrcbr'] = ENCODERVBRCBR - new_config['General']['encoderlossless'] = ENCODERLOSSLESS + new_config['General']['encoderlossless'] = int(ENCODERLOSSLESS) + new_config['General']['delete_lossless_files'] = int(DELETE_LOSSLESS_FILES) new_config['General']['mirror'] = MIRROR new_config['General']['customhost'] = CUSTOMHOST diff --git a/headphones/webserve.py b/headphones/webserve.py index 2f88c041..d00c1f86 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -429,6 +429,7 @@ class WebInterface(object): "embed_album_art" : checked(headphones.EMBED_ALBUM_ART), "embed_lyrics" : checked(headphones.EMBED_LYRICS), "dest_dir" : headphones.DESTINATION_DIR, + "lossless_dest_dir" : headphones.LOSSLESS_DESTINATION_DIR, "folder_format" : headphones.FOLDER_FORMAT, "file_format" : headphones.FILE_FORMAT, "include_extras" : checked(headphones.INCLUDE_EXTRAS), @@ -446,6 +447,7 @@ class WebInterface(object): "encodervbrcbr": headphones.ENCODERVBRCBR, "encoderquality": headphones.ENCODERQUALITY, "encoderlossless": checked(headphones.ENCODERLOSSLESS), + "delete_lossless_files": checked(headphones.DELETE_LOSSLESS_FILES), "prowl_enabled": checked(headphones.PROWL_ENABLED), "prowl_onsnatch": checked(headphones.PROWL_ONSNATCH), "prowl_keys": headphones.PROWL_KEYS, @@ -477,8 +479,8 @@ class WebInterface(object): 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, - music_encoder=0, encoder=None, bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0, + rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, embed_album_art=0, embed_lyrics=0, destination_dir=None, lossless_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, delete_lossless_files=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, **kwargs): @@ -534,6 +536,7 @@ class WebInterface(object): headphones.EMBED_ALBUM_ART = embed_album_art headphones.EMBED_LYRICS = embed_lyrics headphones.DESTINATION_DIR = destination_dir + headphones.LOSSLESS_DESTINATION_DIR = lossless_destination_dir headphones.FOLDER_FORMAT = folder_format headphones.FILE_FORMAT = file_format headphones.INCLUDE_EXTRAS = include_extras @@ -550,7 +553,8 @@ class WebInterface(object): headphones.ENCODEROUTPUTFORMAT = encoderoutputformat headphones.ENCODERVBRCBR = encodervbrcbr headphones.ENCODERQUALITY = int(encoderquality) - headphones.ENCODERLOSSLESS = encoderlossless + headphones.ENCODERLOSSLESS = int(encoderlossless) + headphones.DELETE_LOSSLESS_FILES = int(delete_lossless_files) headphones.PROWL_ENABLED = prowl_enabled headphones.PROWL_ONSNATCH = prowl_onsnatch headphones.PROWL_KEYS = prowl_keys From 1ec58533f01338f34fc64098312cebae43d0dd26 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 01:11:11 +0530 Subject: [PATCH 25/35] Added lossless_destination_dir and delete_lossless_files to the config page (default template only) --- data/interfaces/default/config.html | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 216e4eb6..3dfa2420 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -342,10 +342,15 @@ m<%inherit file="base.html"/>
- + e.g. /Users/name/Music/iTunes or /Volumes/share/music
+
+ + + Set this if you have a separate directory for lossless music +
@@ -379,8 +384,14 @@ m<%inherit file="base.html"/>
+
-
+
+
+
+ +
+ <% if config['encoder'] == 'lame': lameselect = 'selected="selected"' From f2674b55a1651735cba006c0948c3ba683023afa Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 16:07:22 +0530 Subject: [PATCH 26/35] Backend stuff done to get separate lossless & lossy directories working --- headphones/helpers.py | 36 ++++- headphones/music_encoder.py | 3 +- headphones/postprocessor.py | 271 ++++++++++++++++++++++++------------ 3 files changed, 219 insertions(+), 91 deletions(-) diff --git a/headphones/helpers.py b/headphones/helpers.py index 1dbfbcda..7f5cbab9 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -13,10 +13,10 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import time +import os, time from operator import itemgetter import datetime -import re +import re, shutil import headphones @@ -218,3 +218,35 @@ def extract_song_data(s): else: logger.info("Couldn't parse " + s + " into a valid Newbin format") return (name, album, year) + +def smartMove(src, dest, delete=True): + + source_dir = os.path.dirname(src) + filename = os.path.basename(src) + + if os.path.isfile(os.path.join(dest, filename)): + logger.info('Destination file exists: %s' % os.path.join(dest, filename).decode(headphones.SYS_ENCODING)) + title = os.path.splitext(filename)[0] + ext = os.path.splitext(filename)[1] + i = 1 + while True: + newfile = title + '(' + str(i) + ')' + ext + if os.path.isfile(os.path.join(dest, newfile)): + i += 1 + else: + logger.info('Renaming to %s' % newfile) + try: + os.rename(src, os.path.join(source_dir, newfile)) + filename = newfile + except Exception, e: + logger.warn('Error renaming %s: %s' % (src.decode(headphones.SYS_ENCODING), e)) + break + + try: + if delete: + shutil.move(os.path.join(source_dir, filename), os.path.join(dest, filename)) + else: + shutil.copy(os.path.join(source_dir, filename), os.path.join(dest, filename)) + return True + except Exception, e: + logger.warn('Error moving file %s: %s' % (filename.decode(headphones.SYS_ENCODING), e)) diff --git a/headphones/music_encoder.py b/headphones/music_encoder.py index 51864aa4..71da9ba5 100644 --- a/headphones/music_encoder.py +++ b/headphones/music_encoder.py @@ -138,7 +138,8 @@ def command(encoder,musicSource,musicDest,albumPath): time.sleep(10) return_code = call(cmd, shell=True) if (return_code==0) and (os.path.exists(musicDest)): - os.remove(musicSource) + if headphones.DELETE_LOSSLESS_FILES: + os.remove(musicSource) shutil.move(musicDest,albumPath) logger.info('Music "%s" encoded in %s' % (musicSource,getTimeEncode(startMusicTime))) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index cd7fcadc..7959dd64 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -263,7 +263,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list) renameFiles(albumpath, downloaded_track_list, release) if headphones.MOVE_FILES and headphones.DESTINATION_DIR: - albumpath = moveFiles(albumpath, release, tracks) + albumpaths = moveFiles(albumpath, release, tracks) if headphones.MOVE_FILES and not headphones.DESTINATION_DIR: logger.error('No DESTINATION_DIR has been set. Set "Destination Directory" to the parent directory you want to move the files to') @@ -273,8 +273,9 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list) myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid]) myDB.action('UPDATE snatched SET status = "Processed" WHERE AlbumID=?', [albumid]) - # Update the have tracks - librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName']) + # Update the have tracks for all created dirs: + for albumpath in albumpaths: + librarysync.libraryScan(dir=albumpath, append=True, ArtistID=release['ArtistID'], ArtistName=release['ArtistName']) logger.info('Post-processing for %s - %s complete' % (release['ArtistName'], release['AlbumTitle'])) @@ -297,7 +298,8 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list) if headphones.SYNOINDEX_ENABLED: syno = notifiers.Synoindex() - syno.notify(albumpath) + for albumpath in albumpaths: + syno.notify(albumpath) def embedAlbumArt(artwork, downloaded_track_list): logger.info('Embedding album art') @@ -375,71 +377,138 @@ def moveFiles(albumpath, release, tracks): if folder.startswith('.'): folder = folder.replace(0, '_') + + # Grab our list of files early on so we can determine if we need to create + # the lossy_dest_dir, lossless_dest_dir, or both + files_to_move = [] + lossy_media = False + lossless_media = False - destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, folder)).encode(headphones.SYS_ENCODING) - - last_folder = headphones.FOLDER_FORMAT.split('/')[-1] - - # Only rename the folder if they use the album name, otherwise merge into existing folder - if os.path.exists(destination_path) and 'album' in last_folder.lower(): - i = 1 - while True: - newfolder = folder + '[%i]' % i - destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING) - if os.path.exists(destination_path): - i += 1 - else: - folder = newfolder - break - - logger.info('Moving files from %s to %s' % (unicode(albumpath, headphones.SYS_ENCODING, errors="replace"), unicode(destination_path, headphones.SYS_ENCODING, errors="replace"))) - - # Basically check if generic/non-album folders already exist, since we're going to merge - if not os.path.exists(destination_path): - try: - os.makedirs(destination_path) - except Exception, e: - logger.error('Could not create folder for %s. Not moving: %s' % (release['AlbumTitle'], e)) - return albumpath - - # Move files to the destination folder, renaming them if they already exist for r,d,f in os.walk(albumpath): for files in f: - if os.path.isfile(os.path.join(destination_path, files)): - logger.info('Destination file exists: %s' % os.path.join(destination_path, files)) - title = os.path.splitext(files)[0] - ext = os.path.splitext(files)[1] - i = 1 - while True: - newfile = title + '(' + str(i) + ')' + ext - if os.path.isfile(os.path.join(destination_path, newfile)): - i += 1 - else: - logger.info('Renaming to %s' % newfile) - try: - os.rename(os.path.join(r, files), os.path.join(r, newfile)) - files = newfile - except Exception, e: - logger.warn('Error renaming %s: %s' % (files, e)) - break + files_to_move.append(os.path.join(r, files)) + if any(files.lower.endswith(x) for x in headphones.LOSSY_MEDIA_FORMATS): + lossy_media = True + if any(files.lower.endswith(x) for x in headphones.LOSSLESS_MEDIA_FORMATS): + lossless_media = True + # Do some sanity checking to see what directories we need to create: + make_lossy_folder = False + make_lossless_folder = False + + lossy_destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, folder)).encode(headphones.SYS_ENCODING) + lossless_destination_path = os.path.normpath(os.path.join(headphones.LOSSLESS_DESTINATION_DIR, folder)).encode(headphones.SYS_ENCODING) + + # If they set a destination dir for lossless media, only create the lossy folder if there is lossy media + if headphones.LOSSLESS_DESTINATION_DIR: + if lossy_media: + make_lossy_folder = True + if lossless_media: + make_lossless_folder = True + # If they haven't set a lossless dest_dir, just create the "lossy" folder + else: + make_lossy_folder = True + + last_folder = headphones.FOLDER_FORMAT.split('/')[-1] + + if make_lossless_folder: + # Only rename the folder if they use the album name, otherwise merge into existing folder + if os.path.exists(lossless_destination_path) and 'album' in last_folder.lower(): + i = 1 + while True: + newfolder = folder + '[%i]' % i + lossless_destination_path = os.path.normpath(os.path.join(headphones.LOSSLESS_DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING) + if os.path.exists(destination_path): + i += 1 + else: + folder = newfolder + break + + if not os.path.exists(lossless_destination_path): try: - shutil.move(os.path.join(r, files), os.path.join(destination_path, files)) + os.makedirs(lossless_destination_path) except Exception, e: - logger.warn('Error moving file %s: %s' % (files, e)) + logger.error('Could not create lossless folder for %s. (Error: %s)' % (release['AlbumTitle'], e)) + if not make_lossy_folder: + return albumpath + if make_lossy_folder: + if os.path.exists(lossy_destination_path) and 'album' in last_folder.lower(): + i = 1 + while True: + newfolder = folder + '[%i]' % i + lossy_destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING) + if os.path.exists(lossy_destination_path): + i += 1 + else: + folder = newfolder + break + + if not os.path.exists(lossy_destination_path): + try: + os.makedirs(lossy_destination_path) + except Exception, e: + logger.error('Could not create folder for %s. Not moving: %s' % (release['AlbumTitle'], e)) + return albumpath + + logger.info('Checking which files we need to move.....') + + # Move files to the destination folder, renaming them if they already exist + # If we have two desination_dirs, move non-music files to both + if make_lossy_folder and make_lossless_folder: + + for file_to_move in files_to_move: + + if any(file_to_move.lower.endswith(x) for x in headphones.LOSSY_MEDIA_FORMATS): + helpers.smartMove(file_to_move, lossy_destination_path) + + elif any(files.lower.endswith(x) for x in headphones.LOSSLESS_MEDIA_FORMATS): + helpers.smartMove(file_to_move, lossless_destination_path) + + # If it's a non-music file, move it to both dirs + else: + + moved_to_lossy_folder = helpers.smartMove(file_to_move, lossy_destination_path, delete=False) + moved_to_lossless_folder = helpers.smartMove(file_to_move, lossless_destination_path, delete=False) + + if moved_to_lossy_folder or moved_to_lossless_folder: + try: + os.remove(file_to_move) + except Exception, e: + logger.error("Error deleting file '" + file_to_move.decode(headphones.SYS_ENCODING) + "' from source directory") + else: + logger.error("Error copying '" + file_to_move.decode(headphones.SYS_ENCODING) + "'. Not deleting from download directory") + + elif make_lossless_folder and not make_lossy_folder: + + for file_to_move in files_to_move: + helpers.smartMove(file_to_move, lossless_destination_path) + + else: + + for file_to_move in files_to_move: + helpers.smartMove(file_to_move, lossy_destination_path) + # Chmod the directories using the folder_format (script courtesy of premiso!) folder_list = folder.split('/') - temp_f = headphones.DESTINATION_DIR - - for f in folder_list: - - temp_f = os.path.join(temp_f, f) - - try: - os.chmod(os.path.normpath(temp_f).encode(headphones.SYS_ENCODING), int(headphones.FOLDER_PERMISSIONS, 8)) - except Exception, e: - logger.error("Error trying to change permissions on folder: %s" % temp_f) + temp_fs = [] + + if make_lossless_folder: + temp_fs.append(headphones.LOSSLESS_DESTINATION_DIR) + + if make_lossy_folder: + temp_fs.append(headphones.DESTINATION_DIR) + + for temp_f in temp_fs: + + for f in folder_list: + + temp_f = os.path.join(temp_f, f) + + try: + os.chmod(os.path.normpath(temp_f).encode(headphones.SYS_ENCODING), int(headphones.FOLDER_PERMISSIONS, 8)) + except Exception, e: + logger.error("Error trying to change permissions on folder: %s" % temp_f) # If we failed to move all the files out of the directory, this will fail too try: @@ -447,42 +516,68 @@ def moveFiles(albumpath, release, tracks): except Exception, e: logger.error('Could not remove directory: %s. %s' % (albumpath, e)) - return destination_path + destination_paths = [] + + if make_lossy_folder: + destination_paths.append(lossy_destination_path) + if make_lossless_folder: + destination_paths.append(lossless_destination_path) + + return destination_paths def correctMetadata(albumid, release, downloaded_track_list): - logger.info('Writing metadata') - items = [] + logger.info('Preparing to write metadata to tracks....') + lossy_items = [] + lossless_items = [] + + # Process lossless & lossy media formats separately for downloaded_track in downloaded_track_list: + + try: - try: - items.append(beets.library.Item.from_path(downloaded_track)) + if any(downloaded_track.lower().endswith('.' + x.lower()) for x in headphones.LOSSLESS_MEDIA_FORMATS): + lossless_items.append(beets.library.Item.from_path(downloaded_track)) + elif any(downloaded_track.lower().endswith('.' + x.lower()) for x in headphones.LOSSY_MEDIA_FORMATS): + lossy_items.append(beets.library.Item.from_path(downloaded_track)) + else: + logger.warn("Skipping: " + downloaded_track.decode(headphones.SYS_ENCODING) + " because it is not a mutagen friendly file format") except Exception, e: - logger.error("Beets couldn't create an Item from: " + downloaded_track + " - not a media file?" + str(e)) - - try: - cur_artist, cur_album, candidates, rec = autotag.tag_album(items, search_artist=helpers.latinToAscii(release['ArtistName']), search_album=helpers.latinToAscii(release['AlbumTitle'])) - except Exception, e: - logger.error('Error getting recommendation: %s. Not writing metadata' % e) - return - if rec == 'RECOMMEND_NONE': - logger.warn('No accurate album match found for %s, %s - not writing metadata' % (release['ArtistName'], release['AlbumTitle'])) - return - - dist, info, mapping, extra_items, extra_tracks = candidates[0] - logger.debug('Beets recommendation: %s' % rec) - autotag.apply_metadata(info, mapping) - - if len(items) != len(downloaded_track_list): - logger.warn("Mismatch between number of tracks downloaded and the metadata items, but I'll try to write it anyway") - - i = 1 - for item in items: + + logger.error("Beets couldn't create an Item from: " + downloaded_track.decode(headphones.SYS_ENCODING) + " - not a media file?" + str(e)) + + for items in [lossy_items, lossless_items]: + + if not items: + continue + try: - item.write() + cur_artist, cur_album, candidates, rec = autotag.tag_album(items, search_artist=helpers.latinToAscii(release['ArtistName']), search_album=helpers.latinToAscii(release['AlbumTitle'])) except Exception, e: - logger.warn('Error writing metadata to track %i: %s' % (i,e)) - i += 1 + logger.error('Error getting recommendation: %s. Not writing metadata' % e) + return + if rec == 'RECOMMEND_NONE': + logger.warn('No accurate album match found for %s, %s - not writing metadata' % (release['ArtistName'], release['AlbumTitle'])) + return + + if candidates: + dist, info, mapping, extra_items, extra_tracks = candidates[0] + else: + logger.warn('No accurate album match found for %s, %s - not writing metadata' % (release['ArtistName'], release['AlbumTitle'])) + return + + logger.info('Beets recommendation for tagging items: %s' % rec) + + # TODO: Handle extra_items & extra_tracks + + autotag.apply_metadata(info, mapping) + + for item in items: + try: + item.write() + logger.info("Successfully applied metadata to: " + item.path.decode(headphones.SYS_ENCODING)) + except Exception, e: + logger.warn("Error writing metadata to " + item.path.decode(headphones.SYS_ENCODING) + ": " + str(e)) def embedLyrics(downloaded_track_list): logger.info('Adding lyrics') From 4f1052d49e3751e6089cdaf6c65f44f6e5df64a9 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 16:28:28 +0530 Subject: [PATCH 27/35] Move getArtistBio out of init actions so it doesn't append when marking an album as wanted --- data/interfaces/default/artist.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/interfaces/default/artist.html b/data/interfaces/default/artist.html index 1e00b281..b0d8af05 100644 --- a/data/interfaces/default/artist.html +++ b/data/interfaces/default/artist.html @@ -182,7 +182,6 @@ showMsg("Getting artist information",true); %endif getArtistArt(); - getArtistBio(); getAlbumArt(); $('#album_table').dataTable({ "bDestroy": true, @@ -220,6 +219,7 @@ $(document).ready(function() { initActions(); initThisPage(); + getArtistBio(); }); From d5d8906addfe85c482b265f115838c984ef11e7e Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 17:29:20 +0530 Subject: [PATCH 28/35] Fixed files.lower() function in moveFiles --- headphones/postprocessor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 7959dd64..289d72c5 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -387,9 +387,9 @@ def moveFiles(albumpath, release, tracks): for r,d,f in os.walk(albumpath): for files in f: files_to_move.append(os.path.join(r, files)) - if any(files.lower.endswith(x) for x in headphones.LOSSY_MEDIA_FORMATS): + if any(files.lower().endswith('.' + x.lower()) for x in headphones.LOSSY_MEDIA_FORMATS): lossy_media = True - if any(files.lower.endswith(x) for x in headphones.LOSSLESS_MEDIA_FORMATS): + if any(files.lower().endswith('.' + x.lower()) for x in headphones.LOSSLESS_MEDIA_FORMATS): lossless_media = True # Do some sanity checking to see what directories we need to create: @@ -466,6 +466,7 @@ def moveFiles(albumpath, release, tracks): helpers.smartMove(file_to_move, lossless_destination_path) # If it's a non-music file, move it to both dirs + # TODO: Move specific-to-lossless files to the lossless dir only else: moved_to_lossy_folder = helpers.smartMove(file_to_move, lossy_destination_path, delete=False) @@ -582,6 +583,8 @@ def correctMetadata(albumid, release, downloaded_track_list): def embedLyrics(downloaded_track_list): logger.info('Adding lyrics') + # TODO: If adding lyrics for flac & lossy, only fetch the lyrics once + # and apply it to both files for downloaded_track in downloaded_track_list: try: From 95e560413976548929300aaa9fa0caf824948d4d Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 17:37:24 +0530 Subject: [PATCH 29/35] Fixed 'destination_path' -> 'lossless_destination_path' variable --- headphones/postprocessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 289d72c5..f76fbac0 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -418,7 +418,7 @@ def moveFiles(albumpath, release, tracks): while True: newfolder = folder + '[%i]' % i lossless_destination_path = os.path.normpath(os.path.join(headphones.LOSSLESS_DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING) - if os.path.exists(destination_path): + if os.path.exists(lossless_destination_path): i += 1 else: folder = newfolder From ec8c3157d9bc0b9b5515a1d7c0d616e32e87df80 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 17:42:30 +0530 Subject: [PATCH 30/35] More bug fixes. Fixed lower() function later on in moveFiles --- headphones/postprocessor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index f76fbac0..f6fde523 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -459,10 +459,10 @@ def moveFiles(albumpath, release, tracks): for file_to_move in files_to_move: - if any(file_to_move.lower.endswith(x) for x in headphones.LOSSY_MEDIA_FORMATS): + if any(file_to_move.lower().endswith('.' + x.lower()) for x in headphones.LOSSY_MEDIA_FORMATS): helpers.smartMove(file_to_move, lossy_destination_path) - elif any(files.lower.endswith(x) for x in headphones.LOSSLESS_MEDIA_FORMATS): + elif any(file_to_move.lower().endswith('.' + x.lower()) for x in headphones.LOSSLESS_MEDIA_FORMATS): helpers.smartMove(file_to_move, lossless_destination_path) # If it's a non-music file, move it to both dirs From daa9f9703f77798278e5c4f7a4d33ea06145a833 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 18:00:55 +0530 Subject: [PATCH 31/35] Fix for renaming duplicate lossy folders if the lossless folder exists --- headphones/postprocessor.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index f6fde523..27581b93 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -414,14 +414,17 @@ def moveFiles(albumpath, release, tracks): if make_lossless_folder: # Only rename the folder if they use the album name, otherwise merge into existing folder if os.path.exists(lossless_destination_path) and 'album' in last_folder.lower(): + + temp_folder = folder + i = 1 while True: - newfolder = folder + '[%i]' % i + newfolder = temp_folder + '[%i]' % i lossless_destination_path = os.path.normpath(os.path.join(headphones.LOSSLESS_DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING) if os.path.exists(lossless_destination_path): i += 1 else: - folder = newfolder + temp_folder = newfolder break if not os.path.exists(lossless_destination_path): @@ -434,14 +437,17 @@ def moveFiles(albumpath, release, tracks): if make_lossy_folder: if os.path.exists(lossy_destination_path) and 'album' in last_folder.lower(): + + temp_folder = folder + i = 1 while True: - newfolder = folder + '[%i]' % i + newfolder = temp_folder + '[%i]' % i lossy_destination_path = os.path.normpath(os.path.join(headphones.DESTINATION_DIR, newfolder)).encode(headphones.SYS_ENCODING) if os.path.exists(lossy_destination_path): i += 1 else: - folder = newfolder + temp_folder = newfolder break if not os.path.exists(lossy_destination_path): From 44e67256403f0816a310ed88af38de56f2aa7015 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 18:27:05 +0530 Subject: [PATCH 32/35] Don't rename files that are already named correctly --- headphones/postprocessor.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 27581b93..28be4d90 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -623,7 +623,7 @@ def renameFiles(albumpath, downloaded_track_list, release): try: f = MediaFile(downloaded_track) except: - logger.info("MediaFile couldn't parse: " + downloaded_track) + logger.info("MediaFile couldn't parse: " + downloaded_track.decode(headphones.SYS_ENCODING)) continue if not f.track: @@ -665,12 +665,16 @@ def renameFiles(albumpath, downloaded_track_list, release): new_file_name = new_file_name.replace(0, '_') new_file = os.path.join(albumpath, new_file_name) + + if downloaded_track == new_file_name: + logger.info("Renaming for: " + downloaded_track.decode(headphones.SYS_ENCODING) + " is not neccessary") + continue - logger.debug('Renaming %s ---> %s' % (downloaded_track, new_file_name)) + logger.info('Renaming %s ---> %s' % (downloaded_track.decode(headphones.SYS_ENCODING), new_file_name.decode(headphones.SYS_ENCODING))) try: os.rename(downloaded_track, new_file) except Exception, e: - logger.error('Error renaming file: %s. Error: %s' % (downloaded_track, e)) + logger.error('Error renaming file: %s. Error: %s' % (downloaded_track.decode(headphones.SYS_ENCODING), e)) continue def renameUnprocessedFolder(albumpath): From c57d04f97bb83f8349a396bd92ce346c13400930 Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 18:42:55 +0530 Subject: [PATCH 33/35] Changed some logging levels back to debug --- headphones/postprocessor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index 28be4d90..5f0c9842 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -667,10 +667,10 @@ def renameFiles(albumpath, downloaded_track_list, release): new_file = os.path.join(albumpath, new_file_name) if downloaded_track == new_file_name: - logger.info("Renaming for: " + downloaded_track.decode(headphones.SYS_ENCODING) + " is not neccessary") + logger.debug("Renaming for: " + downloaded_track.decode(headphones.SYS_ENCODING) + " is not neccessary") continue - logger.info('Renaming %s ---> %s' % (downloaded_track.decode(headphones.SYS_ENCODING), new_file_name.decode(headphones.SYS_ENCODING))) + logger.debug('Renaming %s ---> %s' % (downloaded_track.decode(headphones.SYS_ENCODING), new_file_name.decode(headphones.SYS_ENCODING))) try: os.rename(downloaded_track, new_file) except Exception, e: From 0bba75554fd917cd6849455f01327cf71feb261b Mon Sep 17 00:00:00 2001 From: rembo10 Date: Fri, 17 Aug 2012 19:04:25 +0530 Subject: [PATCH 34/35] Fixed some wording on the album page to differentiate between trying new versions to download and switching releases TODO: might still need to be a bit clearer what does what.... tooltips? --- data/interfaces/default/album.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/interfaces/default/album.html b/data/interfaces/default/album.html index fce97271..6af0459d 100644 --- a/data/interfaces/default/album.html +++ b/data/interfaces/default/album.html @@ -17,14 +17,14 @@ Retry Download Try New Version %endif - Choose Alternate Version + Choose Alternate Release