From 658cb0403f822d450d08d9a308d930fcbe698abd Mon Sep 17 00:00:00 2001 From: rembo10 Date: Tue, 19 May 2015 23:53:14 -0700 Subject: [PATCH] Added support for series (initial stages - needs some cleanup) --- CHANGELOG.md | 1 + data/interfaces/default/base.html | 1 + data/interfaces/default/searchresults.html | 17 +++++- headphones/__init__.py | 7 ++- headphones/importer.py | 11 +++- headphones/mb.py | 66 +++++++++++++++++++++- headphones/webserve.py | 11 +++- 7 files changed, 106 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbd6a65a..9d91fdfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Released xx xxx 2015 Highlights: * Added: Metacritic scores +* Added: Series support (e.g. Cafe Del Mar, Now That's What I Call Music, etc) * Added: Filter out clean/edited/censored releases (#2198) * Added: Button on the log page to toggle verbose/debug logging * Fixed: Connecting to SABnzbd over https with python >= 2.7.9 diff --git a/data/interfaces/default/base.html b/data/interfaces/default/base.html index 7bed4ac3..536d4430 100644 --- a/data/interfaces/default/base.html +++ b/data/interfaces/default/base.html @@ -62,6 +62,7 @@ diff --git a/data/interfaces/default/searchresults.html b/data/interfaces/default/searchresults.html index 81737326..8fea4992 100644 --- a/data/interfaces/default/searchresults.html +++ b/data/interfaces/default/searchresults.html @@ -17,8 +17,12 @@ Date Score - %else: + %elif type == 'artist': Artist Name + Score + %else: + Series Name + Type Score %endif @@ -40,8 +44,10 @@ %if type == 'album':
- %else: + %elif type == 'artist':
+ %else: + %endif %if type == 'album': ${result['title']} @@ -52,9 +58,14 @@
${result['score']}
${result['albumid']} - %else: + %elif type == 'artist': ${result['uniquename']}
${result['score']}
+ + %else: + ${result['uniquename']} + ${result['type']} +
${result['score']}
%endif diff --git a/headphones/__init__.py b/headphones/__init__.py index a3c359e6..6a45e259 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -352,7 +352,7 @@ def dbcheck(): conn = sqlite3.connect(DB_FILE) c = conn.cursor() c.execute( - 'CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT, IncludeExtras INTEGER, LatestAlbum TEXT, ReleaseDate TEXT, AlbumID TEXT, HaveTracks INTEGER, TotalTracks INTEGER, LastUpdated TEXT, ArtworkURL TEXT, ThumbURL TEXT, Extras TEXT)') + 'CREATE TABLE IF NOT EXISTS artists (ArtistID TEXT UNIQUE, ArtistName TEXT, ArtistSortName TEXT, DateAdded TEXT, Status TEXT, IncludeExtras INTEGER, LatestAlbum TEXT, ReleaseDate TEXT, AlbumID TEXT, HaveTracks INTEGER, TotalTracks INTEGER, LastUpdated TEXT, ArtworkURL TEXT, ThumbURL TEXT, Extras TEXT, Type TEXT)') # ReleaseFormat here means CD,Digital,Vinyl, etc. If using the default # Headphones hybrid release, ReleaseID will equal AlbumID (AlbumID is # releasegroup id) @@ -593,6 +593,11 @@ def dbcheck(): except sqlite3.OperationalError: c.execute('ALTER TABLE albums ADD COLUMN UserScore TEXT DEFAULT NULL') + try: + c.execute('SELECT Type from artists') + except sqlite3.OperationalError: + c.execute('ALTER TABLE artists ADD COLUMN Type TEXT DEFAULT NULL') + conn.commit() c.close() diff --git a/headphones/importer.py b/headphones/importer.py index 16e5efac..dc6e490b 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -111,7 +111,7 @@ def addArtistIDListToDB(artistidlist): addArtisttoDB(artistid) -def addArtisttoDB(artistid, extrasonly=False, forcefull=False): +def addArtisttoDB(artistid, extrasonly=False, forcefull=False, type="artist"): # Putting this here to get around the circular import. We're using this to update thumbnails for artist/albums from headphones import cache @@ -142,12 +142,19 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "Status": "Loading", "IncludeExtras": headphones.CONFIG.INCLUDE_EXTRAS, "Extras": headphones.CONFIG.EXTRAS} + if type=="series": + newValueDict['Type'] = "series" else: newValueDict = {"Status": "Loading"} + if dbartist["Type"] == "series": + type = "series" myDB.upsert("artists", newValueDict, controlValueDict) - artist = mb.getArtist(artistid, extrasonly) + if type=="series": + artist = mb.getSeries(artistid) + else: + artist = mb.getArtist(artistid, extrasonly) if artist and artist.get('artist_name') in blacklisted_special_artist_names: logger.warn('Cannot import blocked special purpose artist: %s' % artist.get('artist_name')) diff --git a/headphones/mb.py b/headphones/mb.py index b0ddddce..5aa71143 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -135,7 +135,6 @@ def findArtist(name, limit=1): }) return artistlist - def findRelease(name, limit=1, artist=None): releaselist = [] releaseResults = None @@ -213,6 +212,39 @@ def findRelease(name, limit=1, artist=None): }) return releaselist +def findSeries(name, limit=1): + serieslist = [] + seriesResults = None + + chars = set('!?*-') + if any((c in chars) for c in name): + name = '"' + name + '"' + + criteria = {'series': name.lower()} + + with mb_lock: + try: + seriesResults = musicbrainzngs.search_series(limit=limit, **criteria)['series-list'] + except musicbrainzngs.WebServiceError as e: + logger.warn('Attempt to query MusicBrainz for %s failed (%s)' % (name, str(e))) + mb_lock.snooze(5) + + if not seriesResults: + return False + for result in seriesResults: + if 'disambiguation' in result: + uniquename = unicode(result['name'] + " (" + result['disambiguation'] + ")") + else: + uniquename = unicode(result['name']) + serieslist.append({ + 'uniquename': uniquename, + 'name': unicode(result['name']), + 'type': unicode(result['type']), + 'id': unicode(result['id']), + 'url': unicode("http://musicbrainz.org/series/" + result['id']),#probably needs to be changed + 'score': int(result['ext:score']) + }) + return serieslist def getArtist(artistid, extrasonly=False): artist_dict = {} @@ -316,6 +348,38 @@ def getArtist(artistid, extrasonly=False): artist_dict['releasegroups'] = releasegroups return artist_dict +def getSeries(seriesid): + series_dict = {} + series = None + try: + with mb_lock: + series = musicbrainzngs.get_series_by_id(seriesid,includes=['release-group-rels'])['series'] + except musicbrainzngs.WebServiceError as e: + logger.warn('Attempt to retrieve series information from MusicBrainz failed for seriesid: %s (%s)' % (seriesid, str(e))) + mb_lock.snooze(5) + except Exception as e: + pass + + if not series: + return False + + if 'disambiguation' in series: + series_dict['artist_name'] = unicode(series['name'] + " (" + unicode(series['disambiguation']) + ")") + else: + series_dict['artist_name'] = unicode(series['name']) + + releasegroups = [] + + for rg in series['release_group-relation-list']: + releasegroup = rg['release-group'] + releasegroups.append({ + 'title':releasegroup['title'], + 'date':releasegroup['first-release-date'], + 'id':releasegroup['id'], + 'type':rg['type'] + }) + series_dict['releasegroups'] = releasegroups + return series_dict def getReleaseGroup(rgid): """ diff --git a/headphones/webserve.py b/headphones/webserve.py index 1608d32f..5a41dc3b 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -145,8 +145,10 @@ class WebInterface(object): raise cherrypy.HTTPRedirect("home") if type == 'artist': searchresults = mb.findArtist(name, limit=100) - else: + elif type == 'album': searchresults = mb.findRelease(name, limit=100) + else: + searchresults = mb.findSeries(name, limit=100) return serve_template(templatename="searchresults.html", title='Search Results for: "' + name + '"', searchresults=searchresults, name=name, type=type) @cherrypy.expose @@ -156,6 +158,13 @@ class WebInterface(object): thread.join(1) raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % artistid) + @cherrypy.expose + def addSeries(self, seriesid): + thread = threading.Thread(target=importer.addArtisttoDB, args=[seriesid, False, False, "series"]) + thread.start() + thread.join(1) + raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % seriesid) + @cherrypy.expose def getExtras(self, ArtistID, newstyle=False, **kwargs): # if calling this function without the newstyle, they're using the old format