From 78f1f1e7c0ecda9a2a40f3e5e146c32a5eb2c531 Mon Sep 17 00:00:00 2001 From: sbuser Date: Fri, 5 Aug 2011 11:19:09 -0500 Subject: [PATCH] Added addReleaseById (musicbrainz releaseid), added post processing for Newzbin style and Headphones default folder style. --- headphones/helpers.py | 17 ++++++-- headphones/importer.py | 80 +++++++++++++++++++++++++++++++++- headphones/mb.py | 31 +++++++++---- headphones/postprocessor.py | 86 ++++++++++++++++++++++++++++++++++++- headphones/webserve.py | 5 +++ 5 files changed, 205 insertions(+), 14 deletions(-) diff --git a/headphones/helpers.py b/headphones/helpers.py index 54285a7d..62a093ef 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -101,15 +101,26 @@ def replace_all(text, dic): return text def extract_data(s): + #headphones default format pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\[(?P.*?)\]', re.VERBOSE) - match = pattern.match(s) if match: name = match.group("name") album = match.group("album") year = match.group("year") - return (name, album, year) else: - return (None, None, None) \ No newline at end of file + logger.info("Couldn't parse " + s + " into a valid default format") + + #newzbin default format + pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\((?P\d+?\))', re.VERBOSE) + match = pattern.match(s) + if match: + name = match.group("name") + album = match.group("album") + year = match.group("year") + return (name, album, year) + else: + logger.info("Couldn't parse " + s + " into a valid Newbin format") + return (name, album, year) \ No newline at end of file diff --git a/headphones/importer.py b/headphones/importer.py index 3b9a43ad..0a5171c0 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -251,4 +251,82 @@ def addArtisttoDB(artistid, extrasonly=False): newValueDict = {"Status": "Active"} myDB.upsert("artists", newValueDict, controlValueDict) - logger.info(u"Updating complete for: " + artist['artist_name']) \ No newline at end of file + logger.info(u"Updating complete for: " + artist['artist_name']) + +def addReleaseById(rid): + + myDB = db.DBConnection() + + #we have to make a call to get the release no matter what so we can get the RGID + #need a way around this - a local cache maybe in the future maybe? + try: + release_dict = mb.getRelease(rid) + except Exception, e: + logger.info('Unable to get release information for Release: ' + str(rid) + " " + str(e)) + return + if not release_dict: + logger.info('Unable to get release information for Release: ' + str(rid) + " no dict") + return + + rgid = release_dict['rgid'] + + #we don't want to make more calls to MB here unless we have to, could be happening quite a lot + #TODO: why do I have to str() this here? I don't get it. + rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rid]) + + #make sure the artist exists since I don't know what happens later if it doesn't + artist_exists = myDB.select("SELECT * from artists WHERE ArtistID=?", [release_dict['artist_id']]) + if not artist_exists: + if release_dict['artist_name'].startswith('The '): + sortname = release_dict['artist_name'][4:] + 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'], + "ArtistSortName": sortname, + "DateAdded": helpers.today(), + "Status": "Paused"} + + if headphones.INCLUDE_EXTRAS: + newValueDict['IncludeExtras'] = 1 + + myDB.upsert("artists", newValueDict, controlValueDict) + + if not rg_exists: + logger.info(u"Now adding-by-id album (" + release_dict['title'] + ") from id: " + rgid) + controlValueDict = {"AlbumID": rgid} + + newValueDict = {"ArtistID": release_dict['artist_id'], + "ArtistName": release_dict['artist_name'], + "AlbumTitle": release_dict['rg_title'], + "AlbumASIN": release_dict['asin'], + "ReleaseDate": release_dict['date'], + "DateAdded": helpers.today(), + "Status": 'Wanted', + "Type": release_dict['rg_type'] + } + + myDB.upsert("albums", newValueDict, controlValueDict) + + for track in release_dict['tracks']: + + controlValueDict = {"TrackID": track['id'], + "AlbumID": release_dict['rgid']} + newValueDict = {"ArtistID": release_dict['artist_id'], + "ArtistName": release_dict['artist_name'], + "AlbumTitle": release_dict['rg_title'], + "AlbumASIN": release_dict['asin'], + "TrackTitle": track['title'], + "TrackDuration": track['duration'], + "TrackNumber": track['number'] + } + + myDB.upsert("tracks", newValueDict, controlValueDict) + + + #start a search for the album + import searcher + searcher.searchNZB(rgid, False) \ No newline at end of file diff --git a/headphones/mb.py b/headphones/mb.py index dbf92f6d..16a9da71 100644 --- a/headphones/mb.py +++ b/headphones/mb.py @@ -163,7 +163,7 @@ def getReleaseGroup(rgid): releaselist = [] - inc = ws.ReleaseGroupIncludes(releases=True) + inc = ws.ReleaseGroupIncludes(releases=True, artist=True) releaseGroup = None attempt = 0 @@ -277,7 +277,11 @@ def getReleaseGroup(rgid): 'trackcount' : a[0]['trackscount'], 'tracks' : a[0]['tracks'], 'asin' : a[0]['asin'], - 'releaselist' : releaselist + 'releaselist' : releaselist, + 'artist_name' : releaseGroup.artist.name, + 'artist_id' : u.extractUuid(releaseGroup.artist.id), + 'title' : releaseGroup.title, + 'type' : u.extractFragment(releaseGroup.type) } return release_dict @@ -290,7 +294,7 @@ def getRelease(releaseid): release = {} - inc = ws.ReleaseIncludes(tracks=True, releaseEvents=True) + inc = ws.ReleaseIncludes(tracks=True, releaseEvents=True, releaseGroup=True, artist=True) results = None attempt = 0 @@ -313,6 +317,19 @@ def getRelease(releaseid): release['id'] = u.extractUuid(results.id) release['asin'] = results.asin release['date'] = results.getEarliestReleaseDate() + + + rg = results.getReleaseGroup() + if rg: + release['rgid'] = u.extractUuid(rg.id) + release['rg_title'] = rg.title + release['rg_type'] = u.extractFragment(rg.type) + else: + logger.warn("Release " + releaseid + "had no ReleaseGroup associated") + #so we can start with a releaseID from anywhere and get the artist info + #it looks like MB api v1 only returns 1 artist object - 2.0 returns more... + release['artist_name'] = results.artist.name + release['artist_id'] = u.extractUuid(results.artist.id) tracks = [] @@ -372,11 +389,7 @@ def findArtistbyAlbum(name): def findAlbumID(artist=None, album=None): - - - term = album + ' AND artist:"'+artist+'"' - - f = ws.ReleaseGroupFilter(query=term, limit=1) + f = ws.ReleaseGroupFilter(title=album, artistName=artist, limit=1) results = None attempt = 0 @@ -386,7 +399,7 @@ def findAlbumID(artist=None, album=None): results = q.getReleaseGroups(f) break except WebServiceError, e: - logger.warn('Attempt to query MusicBrainz for %s failed: %s. Sleeping 5 seconds.' % (name, e)) + logger.warn('Attempt to query MusicBrainz for %s - %s failed: %s. Sleeping 5 seconds.' % (artist, album, e)) attempt += 1 time.sleep(5) diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index d2dc53d7..72c22f9d 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -29,6 +29,86 @@ def verify(albumid, albumpath): myDB = db.DBConnection() release = myDB.action('SELECT * from albums WHERE AlbumID=?', [albumid]).fetchone() tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [albumid]) + + if not release or not tracks: + #the result of a manual post-process on an album that hasn't been inserted + #from an RSS feed or etc + #TODO: This should be a call to a class method.. copied it out of importer with only minor changes + #TODO: odd things can happen when there are diacritic characters in the folder name, need to translate them? + import mb + try: + release_dict = mb.getReleaseGroup(albumid) + except Exception, e: + logger.info('Unable to get release information for manual album with rgid: ' + albumid) + + if not release_dict: + logger.warn('Unable to get release information for manual album with rgid: ' + albumid) + return + + logger.info(u"Now adding/updating artist: " + release_dict['artist_name']) + + if release_dict['artist_name'].startswith('The '): + sortname = release_dict['artist_name'][4:] + else: + sortname = release_dict['artist_name'] + + + controlValueDict = {"ArtistID": release_dict['artist_id']} + newValueDict = {"ArtistName": release_dict['artist_name'], + "ArtistSortName": sortname, + "DateAdded": helpers.today(), + "Status": "Paused"} + logger.info("ArtistID:ArtistName: " + release_dict['artist_id'] + " : " + release_dict['artist_name']) + + if headphones.INCLUDE_EXTRAS: + newValueDict['IncludeExtras'] = 1 + + myDB.upsert("artists", newValueDict, controlValueDict) + + logger.info(u"Now adding album: " + release_dict['title']) + controlValueDict = {"AlbumID": albumid} + + newValueDict = {"ArtistID": release_dict['artist_id'], + "ArtistName": release_dict['artist_name'], + "AlbumTitle": release_dict['title'], + "AlbumASIN": release_dict['asin'], + "ReleaseDate": release_dict['releasedate'], + "DateAdded": helpers.today(), + "Type": release_dict['type'], + "Status": "Snatched" + } + + myDB.upsert("albums", newValueDict, controlValueDict) + + # I changed the albumid from releaseid -> rgid, so might need to delete albums that have a releaseid + for rel in release_dict['releaselist']: + myDB.action('DELETE from albums WHERE AlbumID=?', [rel['releaseid']]) + myDB.action('DELETE from tracks WHERE AlbumID=?', [rel['releaseid']]) + + myDB.action('DELETE from tracks WHERE AlbumID=?', [albumid]) + for track in release_dict['tracks']: + + controlValueDict = {"TrackID": track['id'], + "AlbumID": albumid} + newValueDict = {"ArtistID": release_dict['artist_id'], + "ArtistName": release_dict['artist_name'], + "AlbumTitle": release_dict['title'], + "AlbumASIN": release_dict['asin'], + "TrackTitle": track['title'], + "TrackDuration": track['duration'], + "TrackNumber": track['number'] + } + + myDB.upsert("tracks", newValueDict, controlValueDict) + + controlValueDict = {"ArtistID": albumid} + newValueDict = {"Status": "Paused"} + + myDB.upsert("artists", newValueDict, controlValueDict) + logger.info(u"Addition complete for: " + release_dict['title'] + " - " + release_dict['artist_name']) + + release = myDB.action('SELECT * from albums WHERE AlbumID=?', [albumid]).fetchone() + tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [albumid]) downloaded_track_list = [] for r,d,f in os.walk(albumpath): @@ -371,7 +451,11 @@ def forcePostProcess(): for folder in folders: albumpath = unicode(os.path.join(download_dir, folder)) - name, album, year = helpers.extract_data(folder) + try: + name, album, year = helpers.extract_data(folder) + except: + logger.info("Couldn't parse " + folder + " into any valid format.") + continue if name and album and year: myDB = db.DBConnection() diff --git a/headphones/webserve.py b/headphones/webserve.py index 185592f8..ba49b807 100644 --- a/headphones/webserve.py +++ b/headphones/webserve.py @@ -692,6 +692,11 @@ class WebInterface(object): page.append(templates._footer % headphones.CURRENT_VERSION) return page extras.exposed = True + + def addReleaseById(self, rid): + threading.Thread(target=importer.addReleaseById, args=[rid]).start() + raise cherrypy.HTTPRedirect("home") + addReleaseById.exposed = True def updateCloud(self):