def getTagTopArtists(tag, limit=50): myDB = db.DBConnection() results = myDB.select("SELECT ArtistID from artists") logger.info("Fetching top artists from Last.FM for tag: %s", tag) data = request_lastfm("tag.gettopartists", limit=limit, tag=tag) if data and "topartists" in data: artistlist = [] artists = data["topartists"]["artist"] logger.debug("Fetched %d artists from Last.FM", len(artists)) for artist in artists: try: artist_mbid = artist["mbid"] except KeyError: continue if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) from headphones import importer for artistid in artistlist: importer.addArtisttoDB(artistid) logger.debug("Added %d new artists from Last.FM", len(artistlist))
def history(self): myDB = db.DBConnection() history = myDB.select( '''SELECT * from snatched order by DateAdded DESC''') return serve_template(templatename="history.html", title="History", history=history)
def findArtistbyAlbum(name): myDB = db.DBConnection() artist = myDB.action( 'SELECT AlbumTitle from have WHERE ArtistName=? AND AlbumTitle IS NOT NULL ORDER BY RANDOM()', [name]).fetchone() if not artist: return False # Probably not neccessary but just want to double check if not artist['AlbumTitle']: return False term = '"' + artist['AlbumTitle'] + '" AND artist:"' + name + '"' results = None try: results = musicbrainzngs.search_release_groups(term).get( 'release-group-list') except WebServiceError, e: logger.warn('Attempt to query MusicBrainz for %s failed (%s)' % (name, str(e))) time.sleep(5)
def unqueueAlbum(self, AlbumID, ArtistID): logger.info(u"Marking album: " + AlbumID + "as skipped...") myDB = db.DBConnection() controlValueDict = {'AlbumID': AlbumID} newValueDict = {'Status': 'Skipped'} myDB.upsert("albums", newValueDict, controlValueDict) raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
def manageArtists(self): myDB = db.DBConnection() artists = myDB.select( 'SELECT * from artists order by ArtistSortName COLLATE NOCASE') return serve_template(templatename="manageartists.html", title="Manage Artists", artists=artists)
def getExtras(self, ArtistID): myDB = db.DBConnection() controlValueDict = {'ArtistID': ArtistID} newValueDict = {'IncludeExtras': 1} myDB.upsert("artists", newValueDict, controlValueDict) threading.Thread(target=importer.addArtisttoDB, args=[ArtistID, True]).start() raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
def getExtras(self, ArtistID, newstyle=False, **kwargs): # if calling this function without the newstyle, they're using the old format # which doesn't separate extras, so we'll grab all of them # # If they are, we need to convert kwargs to string format if not newstyle: extras = "1,2,3,4,5,6,7,8" else: temp_extras_list = [] # TODO: Put these extras as a global variable i = 1 for extra in [ "single", "ep", "compilation", "soundtrack", "live", "remix", "spokenword", "audiobook" ]: if extra in kwargs: temp_extras_list.append(i) i += 1 extras = ','.join(str(n) for n in temp_extras_list) myDB = db.DBConnection() controlValueDict = {'ArtistID': ArtistID} newValueDict = {'IncludeExtras': 1, 'Extras': extras} myDB.upsert("artists", newValueDict, controlValueDict) threading.Thread(target=importer.addArtisttoDB, args=[ArtistID, True]).start() raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
def searchforalbum(albumid=None, new=False, lossless=False): if not albumid: myDB = db.DBConnection() results = myDB.select('SELECT AlbumID, Status from albums WHERE Status="Wanted" OR Status="Wanted Lossless"') new = True for result in results: foundNZB = "none" if (headphones.NZBMATRIX or headphones.NEWZNAB or headphones.NZBSORG or headphones.NEWZBIN) and (headphones.SAB_HOST or headphones.BLACKHOLE): if result['Status'] == "Wanted Lossless": foundNZB = searchNZB(result['AlbumID'], new, losslessOnly=True) else: foundNZB = searchNZB(result['AlbumID'], new) if (headphones.KAT or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES) and foundNZB == "none": if result['Status'] == "Wanted Lossless": searchTorrent(result['AlbumID'], new, losslessOnly=True) else: searchTorrent(result['AlbumID'], new) else: foundNZB = "none" if (headphones.NZBMATRIX or headphones.NEWZNAB or headphones.NZBSORG or headphones.NEWZBIN) and (headphones.SAB_HOST or headphones.BLACKHOLE): foundNZB = searchNZB(albumid, new, lossless) if (headphones.KAT or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES) and foundNZB == "none": searchTorrent(albumid, new, lossless)
def checkTorrentFinished(): """ Remove Torrent + data if Post Processed and finished Seeding """ logger.info("Checking if any torrents have finished seeding and can be removed") with postprocessor_lock: myDB = db.DBConnection() results = myDB.select('SELECT * from snatched WHERE Status="Seed_Processed"') for album in results: hash = album['FolderName'] albumid = album['AlbumID'] torrent_removed = False if headphones.CONFIG.TORRENT_DOWNLOADER == 1: torrent_removed = transmission.removeTorrent(hash, True) else: torrent_removed = utorrent.removeTorrent(hash, True) if torrent_removed: myDB.action('DELETE from snatched WHERE status = "Seed_Processed" and AlbumID=?', [albumid]) logger.info("Checking finished torrents completed")
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: # Need to check for variations due to sab renaming. Ideally we'd check the sab config via api to # figure out which options are checked, but oh well nzb_album_possibilities = [ album['FolderName'], sab_replace_dots(album['FolderName']), sab_replace_spaces(album['FolderName']), sab_replace_dots(sab_replace_spaces(album['FolderName'])) ] torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) for nzb_folder_name in nzb_album_possibilities: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING) if os.path.exists(nzb_album_path): logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path) if os.path.exists(torrent_album_path): logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path)
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select( 'SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, album['FolderName']).encode( headphones.SYS_ENCODING) torrent_album_path = os.path.join( headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) if os.path.exists(nzb_album_path): logger.debug( 'Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path) elif os.path.exists(torrent_album_path): logger.debug( 'Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path)
def findArtistbyAlbum(name): myDB = db.DBConnection() artist = myDB.action( 'SELECT AlbumTitle from have WHERE ArtistName=? AND AlbumTitle IS NOT NULL ORDER BY RANDOM()', [name]).fetchone() if not artist: return False # Probably not neccessary but just want to double check if not artist['AlbumTitle']: return False term = '"' + artist['AlbumTitle'] + '" AND artist:"' + name + '"' f = ws.ReleaseGroupFilter(query=term, limit=1) results = None attempt = 0 while attempt < 5: try: results = q.getReleaseGroups(f) break except WebServiceError, e: logger.warn( 'Attempt to query MusicBrainz for %s failed: %s. Sleeping 5 seconds.' % (name, e)) attempt += 1 time.sleep(5)
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: # We're now checking sab config options after sending to determine renaming - but we'll keep the # iterations in just in case we can't read the config for some reason nzb_album_possibilities = [ album['FolderName'], sab_replace_dots(album['FolderName']), sab_replace_spaces(album['FolderName']), sab_replace_spaces(sab_replace_dots(album['FolderName'])) ] torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) for nzb_folder_name in nzb_album_possibilities: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING) if os.path.exists(nzb_album_path): logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path, album['Kind']) if os.path.exists(torrent_album_path): logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path, album['Kind'])
def finalize_update(artistid, artistname, errors=False): # Moving this little bit to it's own function so we can update have tracks & latest album when deleting extras myDB = db.DBConnection() latestalbum = myDB.action('SELECT AlbumTitle, ReleaseDate, AlbumID from albums WHERE ArtistID=? order by ReleaseDate DESC', [artistid]).fetchone() totaltracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND AlbumID IN (SELECT AlbumID FROM albums WHERE Status != "Ignored")', [artistid])) #havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist['artist_name']])) 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 ? AND Matched = "Failed"', [artistname])) controlValueDict = {"ArtistID": artistid} if latestalbum: newValueDict = {"Status": "Active", "LatestAlbum": latestalbum['AlbumTitle'], "ReleaseDate": latestalbum['ReleaseDate'], "AlbumID": latestalbum['AlbumID'], "TotalTracks": totaltracks, "HaveTracks": havetracks} else: newValueDict = {"Status": "Active", "TotalTracks": totaltracks, "HaveTracks": havetracks} if not errors: newValueDict['LastUpdated'] = helpers.now() myDB.upsert("artists", newValueDict, controlValueDict)
def artistPage(self, ArtistID): myDB = db.DBConnection() artist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [ArtistID]).fetchone() albums = myDB.select('SELECT * from albums WHERE ArtistID=? order by ReleaseDate DESC', [ArtistID]) if artist is None: raise cherrypy.HTTPRedirect("home") return serve_template(templatename="artist.html", title=artist['ArtistName'], artist=artist, albums=albums)
def updateFormat(): myDB = db.DBConnection() tracks = myDB.select( 'SELECT * from tracks WHERE Location IS NOT NULL and Format IS NULL') if len(tracks) > 0: logger.info('Finding media format for %s files' % len(tracks)) for track in tracks: try: f = MediaFile(track['Location']) except Exception as e: logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) continue controlValueDict = {"TrackID": track['TrackID']} newValueDict = {"Format": f.format} myDB.upsert("tracks", newValueDict, controlValueDict) logger.info('Finished finding media format for %s files' % len(tracks)) havetracks = myDB.select( 'SELECT * from have WHERE Location IS NOT NULL and Format IS NULL') if len(havetracks) > 0: logger.info('Finding media format for %s files' % len(havetracks)) for track in havetracks: try: f = MediaFile(track['Location']) except Exception as e: logger.info("Exception from MediaFile for: " + track['Location'] + " : " + str(e)) continue controlValueDict = {"TrackID": track['TrackID']} newValueDict = {"Format": f.format} myDB.upsert("have", newValueDict, controlValueDict) logger.info('Finished finding media format for %s files' % len(havetracks))
def albumPage(self, AlbumID): myDB = db.DBConnection() album = myDB.action('SELECT * from albums WHERE AlbumID=?', [AlbumID]).fetchone() tracks = myDB.select('SELECT * from tracks WHERE AlbumID=?', [AlbumID]) description = myDB.action('SELECT * from descriptions WHERE ReleaseGroupID=?', [AlbumID]).fetchone() title = album['ArtistName'] + ' - ' + album['AlbumTitle'] return serve_template(templatename="album.html", title=title, album=album, tracks=tracks, description=description)
def artistlist_to_mbids(artistlist, forced=False): for artist in artistlist: if not artist and not (artist == ' '): continue results = mb.findArtist(artist, limit=1) if not results: logger.info('No results found for: %s' % artist) continue try: artistid = results[0]['id'] except IndexError: logger.info('MusicBrainz query turned up no matches for: %s' % artist) continue # Check if it's blacklisted/various artists (only check if it's not forced, e.g. through library scan auto-add.) # Forced example = Adding an artist from Manage New Artists myDB = db.DBConnection() if not forced: bl_artist = myDB.action('SELECT * FROM blacklist WHERE ArtistID=?', [artistid]).fetchone() if bl_artist or artistid in blacklisted_special_artists: logger.info( "Artist ID for '%s' is either blacklisted or Various Artists. To add artist, you must do it manually (Artist ID: %s)" % (artist, artistid)) continue # Add to database if it doesn't exist if not is_exists(artistid): addArtisttoDB(artistid) # Just update the tracks if it does else: havetracks = len( myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [ artistid ])) + len( myDB.select( 'SELECT TrackTitle from have WHERE ArtistName like ?', [artist])) myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artistid]) # Delete it from the New Artists if the request came from there if forced: myDB.action('DELETE from newartists WHERE ArtistName=?', [artist]) # Update the similar artist tag cloud: logger.info('Updating artist information from Last.fm') try: lastfm.getSimilar() except Exception, e: logger.warn('Failed to update arist information from Last.fm: %s' % e)
def extras(self): myDB = db.DBConnection() cloudlist = myDB.select('SELECT * from lastfmcloud') return serve_template(templatename="extras.html", title="Extras", cloudlist=cloudlist) return page
def getTagTopArtists(tag, limit=50): myDB = db.DBConnection() results = myDB.select('SELECT ArtistID from artists') url = 'http://ws.audioscrobbler.com/2.0/?method=tag.gettopartists&limit=%s&tag=%s&api_key=%s' % ( limit, tag, api_key) data = urllib2.urlopen(url, timeout=20).read() try: d = minidom.parseString(data) except: logger.error("Could not parse artist list from last.fm data") return artists = d.getElementsByTagName("artist") artistlist = [] for artist in artists: mbidnode = artist.getElementsByTagName("mbid")[0].childNodes for node in mbidnode: artist_mbid = node.data try: if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) except: continue from headphones import importer for artistid in artistlist: importer.addArtisttoDB(artistid)
def resumeArtist(self, ArtistID): logger.info(u"Resuming artist: " + ArtistID) myDB = db.DBConnection() controlValueDict = {'ArtistID': ArtistID} newValueDict = {'Status': 'Active'} myDB.upsert("artists", newValueDict, controlValueDict) raise cherrypy.HTTPRedirect("artistPage?ArtistID=%s" % ArtistID)
def addReleaseById(rid): myDB = db.DBConnection() rgid = None artistid = None release_dict = None results = myDB.select("SELECT albums.ArtistID, releases.ReleaseGroupID from releases, albums WHERE releases.ReleaseID=? and releases.ReleaseGroupID=albums.AlbumID LIMIT 1", [rid]) for result in results: rgid = result['ReleaseGroupID'] artistid = result['ArtistID'] logger.debug("Found a cached releaseid : releasegroupid relationship: " + rid + " : " + rgid) if not rgid: #didn't find it in the cache, get the information from MB logger.debug("Didn't find releaseID " + rid + " in the cache. Looking up its ReleaseGroupID") 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'] artistid = release_dict['artist_id']
def manage(self): myDB = db.DBConnection() emptyArtists = myDB.select( "SELECT * FROM artists WHERE LatestAlbum IS NULL") return serve_template(templatename="manage.html", title="Manage", emptyArtists=emptyArtists)
def getArtists(): myDB = db.DBConnection() results = myDB.select("SELECT ArtistID from artists") if not headphones.CONFIG.LASTFM_USERNAME: logger.warn("Last.FM username not set, not importing artists.") return logger.info("Fetching artists from Last.FM for username: %s", headphones.CONFIG.LASTFM_USERNAME) data = request_lastfm("library.getartists", limit=10000, user=headphones.CONFIG.LASTFM_USERNAME) if data and "artists" in data: artistlist = [] artists = data["artists"]["artist"] logger.debug("Fetched %d artists from Last.FM", len(artists)) for artist in artists: artist_mbid = artist["mbid"] if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) from headphones import importer for artistid in artistlist: importer.addArtisttoDB(artistid) logger.info("Imported %d new artists from Last.FM", len(artistlist))
def markArtists(self, action=None, **args): myDB = db.DBConnection() artistsToAdd = [] for ArtistID in args: if action == 'delete': myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID]) elif action == 'pause': controlValueDict = {'ArtistID': ArtistID} newValueDict = {'Status': 'Paused'} myDB.upsert("artists", newValueDict, controlValueDict) elif action == 'resume': controlValueDict = {'ArtistID': ArtistID} newValueDict = {'Status': 'Active'} myDB.upsert("artists", newValueDict, controlValueDict) else: artistsToAdd.append(ArtistID) if len(artistsToAdd) > 0: logger.debug("Refreshing artists: %s" % artistsToAdd) threading.Thread(target=importer.addArtistIDListToDB, args=[artistsToAdd]).start() raise cherrypy.HTTPRedirect("home")
def _queueAlbum(self, **kwargs): if 'id' not in kwargs: self.data = 'Missing parameter: id' return else: self.id = kwargs['id'] if 'new' in kwargs: new = kwargs['new'] else: new = False if 'lossless' in kwargs: lossless = kwargs['lossless'] else: lossless = False myDB = db.DBConnection() controlValueDict = {'AlbumID': self.id} if lossless: newValueDict = {'Status': 'Wanted Lossless'} else: newValueDict = {'Status': 'Wanted'} myDB.upsert("albums", newValueDict, controlValueDict) searcher.searchforalbum(self.id, new)
def home(self): myDB = db.DBConnection() artists = myDB.select( 'SELECT * from artists order by ArtistSortName COLLATE NOCASE') return serve_template(templatename="index.html", title="Home", artists=artists)
def deleteArtist(self, ArtistID): logger.info(u"Deleting all traces of artist: " + ArtistID) myDB = db.DBConnection() myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) raise cherrypy.HTTPRedirect("home")
def get_info_from_cache(self, ArtistID=None, AlbumID=None): self.query_type = 'info' myDB = db.DBConnection() if ArtistID: self.id = ArtistID self.id_type = 'artist' db_info = myDB.action( 'SELECT Summary, Content, LastUpdated FROM descriptions WHERE ArtistID=?', [self.id]).fetchone() else: self.id = AlbumID self.id_type = 'album' db_info = myDB.action( 'SELECT Summary, Content, LastUpdated FROM descriptions WHERE ReleaseGroupID=?', [self.id]).fetchone() if not db_info or not db_info['LastUpdated'] or not self._is_current( date=db_info['LastUpdated']): self._update_cache() info_dict = { 'Summary': self.info_summary, 'Content': self.info_content } return info_dict else: info_dict = { 'Summary': db_info['Summary'], 'Content': db_info['Content'] } return info_dict
def editSearchTerm(self, AlbumID, SearchTerm): logger.info(u"Updating search term for albumid: " + AlbumID) myDB = db.DBConnection() controlValueDict = {'AlbumID': AlbumID} newValueDict = {'SearchTerm': SearchTerm} myDB.upsert("albums", newValueDict, controlValueDict) raise cherrypy.HTTPRedirect("albumPage?AlbumID=%s" % AlbumID)