From 66b9da36ddb582a30f4c87b819dd5ba50671180c Mon Sep 17 00:00:00 2001 From: Ade Date: Sat, 28 Jun 2014 08:18:54 +1200 Subject: [PATCH] Album Search Results Changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mb findRelease get additional info incl Date, Formats, Tracks, Rgid etc, pass to searchresults added optional artist to findrelease, if searching by album can now enter album:artist in the search bar, went for this for now as I didn’t want to change the design too much but maybe in the future we could have 2 boxes, artist album and then there’s no need for the dropdown artist, album - base position at search box and persist selected option (uses local storage) when refreshing, useful if entering multiple albums increased the search box size a little - searchresults album results - new fields from mb, fall back to cover art archive rgid url if last.fm not found (should get more results), musicbrainz album icon link, pass mb rgid to addReleaseById to redirect to album page artist results - musicbrainz artist icon - importer addreleaseById - added rgid param to create the album record upfront with status Loading if from searchresults - webserve redirect to album page using rgid from searchresults - album spinner while album is loading --- data/interfaces/default/album.html | 61 +++++++++++++++++- data/interfaces/default/base.html | 24 ++++++- data/interfaces/default/css/style.css | 57 +++++++++++++++- .../default/images/MusicBrainz_Album_Icon.png | Bin 0 -> 4266 bytes .../images/MusicBrainz_Artist_Icon.png | Bin 0 -> 3823 bytes data/interfaces/default/searchresults.html | 56 ++++++++++------ headphones/cache.py | 6 +- headphones/importer.py | 43 ++++++++++-- headphones/mb.py | 53 +++++++++++++-- headphones/webserve.py | 44 ++++++++++++- 10 files changed, 297 insertions(+), 47 deletions(-) create mode 100644 data/interfaces/default/images/MusicBrainz_Album_Icon.png create mode 100644 data/interfaces/default/images/MusicBrainz_Artist_Icon.png diff --git a/data/interfaces/default/album.html b/data/interfaces/default/album.html index ba8bf33b..5127cf28 100644 --- a/data/interfaces/default/album.html +++ b/data/interfaces/default/album.html @@ -89,9 +89,15 @@
- -

${album['AlbumTitle']}

-

${album['ArtistName']}

+ +

+ ${album['AlbumTitle']} +

+ +

+ ${album['ArtistName']} +

+ <% totalduration = myDB.action("SELECT SUM(TrackDuration) FROM tracks WHERE AlbumID=?", [album['AlbumID']]).fetchone()[0] totaltracks = len(myDB.select("SELECT TrackTitle from tracks WHERE AlbumID=?", [album['AlbumID']])) @@ -275,9 +281,58 @@ feedback.fadeIn(); } + var loadingMessage = false; + var spinner_active = false; + var loadingtext_active = false; + var refreshInterval; + var wasLoading = false; + var x = 0; + + function checkAlbumStatus() { + $.getJSON("getAlbumjson?AlbumID=${album['AlbumID']}", function(data) { + if (data['Status'] == "Loading"){ + wasLoading = true; + $('#albumnamelink').text(data["AlbumTitle"]); + $('#artistnamelink').text(data["ArtistName"]); + if (loadingMessage == false){ + $("#ajaxMsg").after( "
" ); + showArtistMsg("Getting album information"); + loadingMessage = true; + } + if (spinner_active == false){ + $('#albumname').prepend('') + spinner_active = true; + } + if (loadingtext_active == false){ + $('#albumname').append('

(Album information is currently being loaded)

') + loadingtext_active = true; + } + } + else{ + if (++x === 5) { + clearInterval(refreshInterval); + } + var sts = $("#artistname").text(); + if (wasLoading == true || sts == "Loading"){ + location.reload(); + $('#albumnamespinner').remove() + $('#loadingtext').remove() + $('#ajaxMsg2').remove() + spinner_active = false + loadingtext_active = false + loadingMessage = false + } + } + }); + } + $(document).ready(function() { getAlbumInfo(); initThisPage(); + checkAlbumStatus(); + refreshInterval = setInterval(function(){ + checkAlbumStatus(); + }, 3000); }); diff --git a/data/interfaces/default/base.html b/data/interfaces/default/base.html index e606a02b..2920f0a5 100644 --- a/data/interfaces/default/base.html +++ b/data/interfaces/default/base.html @@ -58,7 +58,7 @@
- @@ -115,3 +115,25 @@ <%def name="javascriptIncludes()"> <%def name="headIncludes()"> <%def name="headerIncludes()"> + + + diff --git a/data/interfaces/default/css/style.css b/data/interfaces/default/css/style.css index 6789726f..60baca70 100644 --- a/data/interfaces/default/css/style.css +++ b/data/interfaces/default/css/style.css @@ -758,7 +758,7 @@ div#searchbar input[type=text] { line-height: normal; margin-right: 5px; padding: 4px 5px 4px 25px; - width: 150px; + width: 200px; } div#searchbar .mini-icon { color: #999; @@ -1126,13 +1126,34 @@ div#artistheader h2 a { padding: 2px 10px; } #searchresults_table th#albumname { - min-width: 225px; + min-width: 250px; text-align: left; + font-size: 14px; } #searchresults_table th#artistname { min-width: 325px; text-align: left; } +#searchresults_table th#artistnamesmall { + min-width: 125px; + text-align: left; + font-size: 14px; +} +#searchresults_table th#reldate { + min-width: 80px; + text-align: center; + font-size: 14px; +} +#searchresults_table th#format { + min-width: 50px; + text-align: left; + font-size: 14px; +} +#searchresults_table th#tracks { + min-width: 50px; + text-align: left; + font-size: 14px; +} #searchresults_table #artistImg { background: url("../images/loader_black.gif") no-repeat scroll center center #ffffff; border: 3px solid #FFFFFF; @@ -1144,15 +1165,40 @@ div#artistheader h2 a { width: 50px; } #searchresults_table td#albumname { - min-width: 200px; + min-width: 250px; text-align: left; vertical-align: middle; + font-size: 14px; } #searchresults_table td#artistname { min-width: 300px; text-align: left; vertical-align: middle; } +#searchresults_table td#artistnamesmall { + min-width: 100px; + text-align: left; + vertical-align: middle; + font-size: 14px; +} +#searchresults_table td#reldate { + min-width: 80px; + text-align: center; + vertical-align: middle; + font-size: 14px; +} +#searchresults_table td#format { + min-width: 50px; + text-align: left; + vertical-align: middle; + font-size: 14px; +} +#searchresults_table td#tracks { + min-width: 50px; + text-align: left; + vertical-align: middle; + font-size: 12px; +} #searchresults_table td#add { vertical-align: middle; } @@ -1416,6 +1462,11 @@ div#artistheader h3 span { min-width: 75px; text-align: center; } +#searchresults_table th#scoresmall { + min-width: 50px; + text-align: center; + font-size: 14px; +} #track_table td#bitrate, #track_table td#format { font-size: 12px; diff --git a/data/interfaces/default/images/MusicBrainz_Album_Icon.png b/data/interfaces/default/images/MusicBrainz_Album_Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..16abf29b38f2372fce6238986253209e772670ab GIT binary patch literal 4266 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 literal 0 HcmV?d00001 diff --git a/data/interfaces/default/searchresults.html b/data/interfaces/default/searchresults.html index b790f644..2c772b41 100644 --- a/data/interfaces/default/searchresults.html +++ b/data/interfaces/default/searchresults.html @@ -11,37 +11,51 @@ %if type == 'album': Album Name + Artist Name + Format + Tracks + Date + Score + %else: + Artist Name + Score %endif - Artist Name - Score - + %if searchresults: - %for result in searchresults: - <% - if result['score'] == 100: - grade = 'A' - else: - grade = 'Z' - %> + %for result in searchresults: + <% + if result['score'] == 100: + grade = 'A' + else: + grade = 'Z' + + if type == 'album': + albuminfo = 'Type: ' + result['rgtype'] + ', Country: ' + result['country'] + caa_group_url = "http://coverartarchive.org/release-group/%s/front-250.jpg" %result['rgid'] + %> %if type == 'album': -
+
%else:
%endif %if type == 'album': - ${result['title']} - %endif - ${result['uniquename']} -
${result['score']}
- %if type == 'album': - Add this album - %else: - Add this artist + ${result['title']} + ${result['uniquename']} + ${result['formats']} + ${result['tracks']} + ${result['date']} +
${result['score']}
+ + %else: + ${result['uniquename']} +
${result['score']}
+ %endif + %endfor %endif @@ -75,7 +89,7 @@ { "bDestroy": true, "aoColumnDefs": [ - { 'bSortable': false, 'aTargets': [ 0,3 ] } + { 'bSortable': false, 'aTargets': [ 0 ] } ], "oLanguage": { "sLengthMenu":"Show _MENU_ results per page", @@ -91,7 +105,7 @@ resetFilters("album"); } $(document).ready(function(){ - initThisPage(); + initThisPage(); }); $(window).load(function(){ initFancybox(); diff --git a/headphones/cache.py b/headphones/cache.py index a762fd20..4eba6034 100644 --- a/headphones/cache.py +++ b/headphones/cache.py @@ -233,15 +233,15 @@ class Cache(object): return try: - image_url = data['artist']['image'][-1]['#text'] + image_url = data['album']['image'][-1]['#text'] except Exception: - logger.debug('No artist image found') + logger.debug('No album image found on last.fm') image_url = None thumb_url = self._get_thumb_url(data) if not thumb_url: - logger.debug('No artist thumbnail image found') + logger.debug('No album thumbnail image found on last.fm') return {'artwork' : image_url, 'thumbnail' : thumb_url } diff --git a/headphones/importer.py b/headphones/importer.py index 6a7f79ed..525935b2 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -389,6 +389,8 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): releaseid = rg['id'] else: releaseid = rg_exists['ReleaseID'] + if not releaseid: + releaseid = rg['id'] album = myDB.action('SELECT * from allalbums WHERE ReleaseID=?', [releaseid]).fetchone() @@ -526,10 +528,23 @@ def finalize_update(artistid, artistname, errors=False): myDB.upsert("artists", newValueDict, controlValueDict) -def addReleaseById(rid): +def addReleaseById(rid, rgid=None): myDB = db.DBConnection() + # Create minimum info upfront if added from searchresults + status = '' + if rgid: + dbalbum = myDB.select("SELECT * from albums WHERE AlbumID=?", [rgid]) + if not dbalbum: + status = 'Loading' + controlValueDict = {"AlbumID": rgid} + newValueDict = {"AlbumTitle": rgid, + "ArtistName": status, + "Status": status} + myDB.upsert("albums", newValueDict, controlValueDict) + time.sleep(1) + rgid = None artistid = None release_dict = None @@ -545,9 +560,13 @@ def addReleaseById(rid): release_dict = mb.getRelease(rid) except Exception, e: logger.info('Unable to get release information for Release %s: %s', rid, e) + if status == 'Loading': + myDB.action("DELETE FROM albums WHERE AlbumID=?", [rgid]) return if not release_dict: logger.info('Unable to get release information for Release %s: no dict', rid) + if status == 'Loading': + myDB.action("DELETE FROM albums WHERE AlbumID=?", [rgid]) return rgid = release_dict['rgid'] @@ -565,7 +584,6 @@ def addReleaseById(rid): else: sortname = release_dict['artist_name'] - logger.info(u"Now manually adding: " + release_dict['artist_name'] + " - with status Paused") controlValueDict = {"ArtistID": release_dict['artist_id']} newValueDict = {"ArtistName": release_dict['artist_name'], @@ -581,12 +599,16 @@ def addReleaseById(rid): elif not artist_exists and not release_dict: logger.error("Artist does not exist in the database and did not get a valid response from MB. Skipping release.") + if status == 'Loading': + myDB.action("DELETE FROM albums WHERE AlbumID=?", [rgid]) return - if not rg_exists and release_dict: #it should never be the case that we have an rg and not the artist - #but if it is this will fail + if not rg_exists and release_dict or status == 'Loading' and release_dict: #it should never be the case that we have an rg and not the artist + #but if it is this will fail logger.info(u"Now adding-by-id album (" + release_dict['title'] + ") from id: " + rgid) controlValueDict = {"AlbumID": rgid} + if status != 'Loading': + status = 'Wanted' newValueDict = {"ArtistID": release_dict['artist_id'], "ArtistName": release_dict['artist_name'], @@ -594,8 +616,9 @@ def addReleaseById(rid): "AlbumASIN": release_dict['asin'], "ReleaseDate": release_dict['date'], "DateAdded": helpers.today(), - "Status": 'Wanted', - "Type": release_dict['rg_type'] + "Status": status, + "Type": release_dict['rg_type'], + "ReleaseID": rid } myDB.upsert("albums", newValueDict, controlValueDict) @@ -635,11 +658,19 @@ def addReleaseById(rid): myDB.upsert("tracks", newValueDict, controlValueDict) + # Reset status + if status == 'Loading': + controlValueDict = {"AlbumID": rgid} + newValueDict = {"Status": "Wanted"} + myDB.upsert("albums", newValueDict, controlValueDict) + #start a search for the album import searcher 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.") + if status == 'Loading': + myDB.action("DELETE FROM albums WHERE AlbumID=?", [rgid]) return else: logger.info('Release ' + str(rid) + " already exists in the database!") diff --git a/headphones/mb.py b/headphones/mb.py index d9cc96e6..80467e5f 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -23,6 +23,8 @@ from headphones.helpers import multikeysort, replace_all import lib.musicbrainzngs as musicbrainzngs from lib.musicbrainzngs import WebServiceError +from lib.simplejson import OrderedDict + mb_lock = threading.Lock() @@ -117,7 +119,7 @@ def findArtist(name, limit=1): }) return artistlist -def findRelease(name, limit=1): +def findRelease(name, limit=1, artist=None): with mb_lock: releaselist = [] @@ -126,25 +128,62 @@ def findRelease(name, limit=1): chars = set('!?') if any((c in chars) for c in name): name = '"'+name+'"' - + + # additional artist search + if not artist and ':' in name: + name, artist = name.rsplit(":",1) + try: - releaseResults = musicbrainzngs.search_releases(query=name,limit=limit)['release-list'] + releaseResults = musicbrainzngs.search_releases(query=name,limit=limit,artist=artist)['release-list'] except WebServiceError, e: #need to update exceptions logger.warn('Attempt to query MusicBrainz for "%s" failed: %s' % (name, str(e))) time.sleep(5) if not releaseResults: return False + for result in releaseResults: - releaselist.append({ + + title = result['title'] + if 'disambiguation' in result: + title += ' (' + result['disambiguation'] + ')' + + # Get formats and track counts + format_dict = OrderedDict() + formats = '' + tracks = '' + for medium in result['medium-list']: + if 'format' in medium: + format = medium['format'] + if format not in format_dict: + format_dict[format] = 0 + format_dict[format] += 1 + if 'track-count' in medium: + if tracks: + tracks += ' + ' + tracks += str(medium['track-count']) + for format, count in format_dict.items(): + if formats: + formats += ' + ' + if count > 1: + formats += str(count) + 'x' + formats += format + + releaselist.append({ 'uniquename': unicode(result['artist-credit'][0]['artist']['name']), - 'title': unicode(result['title']), + 'title': unicode(title), 'id': unicode(result['artist-credit'][0]['artist']['id']), 'albumid': unicode(result['id']), 'url': unicode("http://musicbrainz.org/artist/" + result['artist-credit'][0]['artist']['id']),#probably needs to be changed 'albumurl': unicode("http://musicbrainz.org/release/" + result['id']),#probably needs to be changed - 'score': int(result['ext:score']) - }) + 'score': int(result['ext:score']), + 'date': unicode(result['date']) if 'date' in result else '', + 'country': unicode(result['country']) if 'country' in result else '', + 'formats': unicode(formats), + 'tracks': unicode(tracks), + 'rgid': unicode(result['release-group']['id']), + 'rgtype': unicode(result['release-group']['type']) if 'type' in result['release-group'] else '' + }) return releaselist def getArtist(artistid, extrasonly=False): diff --git a/headphones/webserve.py b/headphones/webserve.py index 709de4f0..92779e56 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -110,6 +110,19 @@ class WebInterface(object): album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone() tracks = myDB.select('SELECT * from tracks WHERE AlbumID=? ORDER BY CAST(TrackNumber AS INTEGER)', [AlbumID]) description = myDB.action('SELECT * from descriptions WHERE ReleaseGroupID=?', [AlbumID]).fetchone() + + retry = 0 + while retry < 5: + if not album: + time.sleep(1) + album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone() + retry += 1 + else: + break + + if not album: + raise cherrypy.HTTPRedirect("home") + if not album['ArtistName']: title = ' - ' else: @@ -864,6 +877,17 @@ class WebInterface(object): return artist_json getArtistjson.exposed=True + def getAlbumjson(self, AlbumID, **kwargs): + myDB = db.DBConnection() + album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone() + album_json = json.dumps({ + 'AlbumTitle': album['AlbumTitle'], + 'ArtistName': album['ArtistName'], + 'Status': album['Status'] + }) + return album_json + getAlbumjson.exposed=True + def clearhistory(self, type=None, date_added=None, title=None): myDB = db.DBConnection() if type: @@ -1395,9 +1419,12 @@ class WebInterface(object): return page extras.exposed = True - def addReleaseById(self, rid): - threading.Thread(target=importer.addReleaseById, args=[rid]).start() - raise cherrypy.HTTPRedirect("home") + def addReleaseById(self, rid, rgid=None): + threading.Thread(target=importer.addReleaseById, args=[rid, rgid]).start() + if rgid: + raise cherrypy.HTTPRedirect("albumPage?AlbumID=%s" % rgid) + else: + raise cherrypy.HTTPRedirect("home") addReleaseById.exposed = True def updateCloud(self): @@ -1450,6 +1477,17 @@ class WebInterface(object): from headphones import cache image_dict = cache.getImageLinks(ArtistID, AlbumID) + # Return the Cover Art Archive urls if not found on last.fm + if AlbumID and not image_dict: + image_url = "http://coverartarchive.org/release/%s/front-500.jpg" % AlbumID + thumb_url = "http://coverartarchive.org/release/%s/front-250.jpg" % AlbumID + image_dict = {'artwork' : image_url, 'thumbnail' : thumb_url} + elif AlbumID and (not image_dict['artwork'] or not image_dict['thumbnail']): + if not image_dict['artwork']: + image_dict['artwork'] = "http://coverartarchive.org/release/%s/front-500.jpg" % AlbumID + if not image_dict['thumbnail']: + image_dict['thumbnail'] = "http://coverartarchive.org/release/%s/front-250.jpg" % AlbumID + return simplejson.dumps(image_dict) getImageLinks.exposed = True