class Similarity(object): """Here the actual similarity computation and lookup happens.""" def __init__(self): data_dir = player_get_data_dir() self.db_path = os.path.join(data_dir, "similarity.db") self.gaia_db_path = os.path.join(data_dir, "gaia.db") self.db_queue = PriorityQueue() self._db_wrapper = DatabaseWrapper() self._db_wrapper.daemon = True self._db_wrapper.set_path(self.db_path) self._db_wrapper.set_queue(self.db_queue) self._db_wrapper.start() self.create_db() self.network = LastFMNetwork(api_key=API_KEY) self.cache_time = 90 if GAIA: self.gaia_queue = LifoQueue() self.gaia_analyser = GaiaAnalysis(self.gaia_db_path, self.gaia_queue) self.gaia_analyser.daemon = True self.gaia_analyser.start() def execute_sql(self, sql=None, priority=1, command=None): """Put sql command on the queue to be executed.""" if command is None: command = SQLCommand(sql) self.db_queue.put((priority, command)) def get_sql_command(self, sql, priority=1): """Build a SQLCommand, put it on the queue and return it.""" command = SQLCommand(sql) self.execute_sql(command=command, priority=priority) return command def remove_track_by_filename(self, filename): if not filename: return if GAIA: self.gaia_queue.put((REMOVE, filename)) def get_ordered_gaia_tracks_by_request(self, filename, number, request): start_time = time() tracks = self.gaia_analyser.get_tracks(filename, number, request=request) print("finding gaia matches took %f s" % (time() - start_time, )) return tracks def get_ordered_gaia_tracks(self, filename, number): """Get neighbours for track.""" start_time = time() tracks = self.gaia_analyser.get_tracks(filename, number) print("finding gaia matches took %f s" % (time() - start_time, )) return tracks def get_artist(self, artist_name): """Get artist information from the database.""" sql = ("SELECT * FROM artists WHERE name = ?;", (artist_name, )) command = self.get_sql_command(sql, priority=1) for row in command.result_queue.get(): return row sql2 = ("INSERT INTO artists (name) VALUES (?);", (artist_name, )) command = self.get_sql_command(sql2, priority=0) command.result_queue.get() command = self.get_sql_command(sql, priority=1) for row in command.result_queue.get(): return row def get_track_from_artist_and_title(self, artist_name, title): """Get track information from the database.""" artist_id = self.get_artist(artist_name)[0] sql = ("SELECT * FROM tracks WHERE artist = ? AND title = ?;", (artist_id, title)) command = self.get_sql_command(sql, priority=3) for row in command.result_queue.get(): return row sql2 = ("INSERT INTO tracks (artist, title) VALUES (?, ?);", (artist_id, title)) command = self.get_sql_command(sql2, priority=2) command.result_queue.get() command = self.get_sql_command(sql, priority=3) for row in command.result_queue.get(): return row def get_similar_tracks(self, track_id): """Get similar tracks from the database. Sorted by descending match score. """ sql = ( "SELECT track_2_track.match, artists.name, tracks.title" " FROM track_2_track INNER JOIN tracks ON" " track_2_track.track2 = tracks.id INNER JOIN artists ON" " artists.id = tracks.artist WHERE track_2_track.track1 = ? UNION " "SELECT track_2_track.match, artists.name, tracks.title" " FROM track_2_track INNER JOIN tracks ON" " track_2_track.track1 = tracks.id INNER JOIN artists ON" " artists.id = tracks.artist WHERE track_2_track.track2" " = ? ORDER BY track_2_track.match DESC;", (track_id, track_id)) command = self.get_sql_command(sql, priority=0) return command.result_queue.get() def get_similar_artists(self, artist_id): """Get similar artists from the database. Sorted by descending match score. """ sql = ("SELECT match, name FROM artist_2_artist INNER JOIN" " artists ON artist_2_artist.artist2 = artists.id WHERE" " artist_2_artist.artist1 = ? UNION " "SELECT match, name FROM artist_2_artist INNER JOIN" " artists ON artist_2_artist.artist1 = artists.id WHERE" " artist_2_artist.artist2 = ? ORDER BY match DESC;", (artist_id, artist_id)) command = self.get_sql_command(sql, priority=0) return command.result_queue.get() def get_artist_match(self, artist1, artist2): """Get artist match score from database.""" sql = ("SELECT match FROM artist_2_artist WHERE artist1 = ?" " AND artist2 = ?;", (artist1, artist2)) command = self.get_sql_command(sql, priority=2) for row in command.result_queue.get(): return row[0] return 0 def get_track_match(self, track1, track2): """Get track match score from database.""" sql = ( "SELECT match FROM track_2_track WHERE track1 = ? AND track2 = ?;", (track1, track2)) command = self.get_sql_command(sql, priority=2) for row in command.result_queue.get(): return row[0] return 0 def update_artist_match(self, artist1, artist2, match): """Write match score to the database.""" self.execute_sql( ("UPDATE artist_2_artist SET match = ? WHERE artist1 = ? AND" " artist2 = ?;", (match, artist1, artist2)), priority=10) def update_track_match(self, track1, track2, match): """Write match score to the database.""" self.execute_sql( ("UPDATE track_2_track SET match = ? WHERE track1 = ? AND" " track2 = ?;", (match, track1, track2)), priority=10) def insert_artist_match(self, artist1, artist2, match): """Write match score to the database.""" self.execute_sql( ("INSERT INTO artist_2_artist (artist1, artist2, match) VALUES" " (?, ?, ?);", (artist1, artist2, match)), priority=10) def insert_track_match(self, track1, track2, match): """Write match score to the database.""" self.execute_sql( ("INSERT INTO track_2_track (track1, track2, match) VALUES" " (?, ?, ?);", (track1, track2, match)), priority=10) def update_artist(self, artist_id): """Write artist information to the database.""" self.execute_sql( ("UPDATE artists SET updated = DATETIME('now') WHERE id = ?;", (artist_id, )), priority=10) def update_track(self, track_id): """Write track information to the database.""" self.execute_sql( ("UPDATE tracks SET updated = DATETIME('now') WHERE id = ?", (track_id, )), priority=10) def update_similar_artists(self, artists_to_update): """Write similar artist information to the database.""" for artist_id, similar in list(artists_to_update.items()): for artist in similar: row = self.get_artist(artist['artist']) if row: id2 = row[0] if self.get_artist_match(artist_id, id2): self.update_artist_match(artist_id, id2, artist['score']) continue self.insert_artist_match(artist_id, id2, artist['score']) self.update_artist(artist_id) def update_similar_tracks(self, tracks_to_update): """Write similar track information to the database.""" for track_id, similar in list(tracks_to_update.items()): for track in similar: row = self.get_track_from_artist_and_title( track['artist'], track['title']) if row: id2 = row[0] if self.get_track_match(track_id, id2): self.update_track_match(track_id, id2, track['score']) continue self.insert_track_match(track_id, id2, track['score']) self.update_track(track_id) def create_db(self): """Set up a database for the artist and track similarity scores.""" self.execute_sql( ('CREATE TABLE IF NOT EXISTS artists (id INTEGER PRIMARY KEY, name' ' VARCHAR(100), updated DATE, UNIQUE(name));', ), priority=0) self.execute_sql( ('CREATE TABLE IF NOT EXISTS artist_2_artist (artist1 INTEGER,' ' artist2 INTEGER, match INTEGER, UNIQUE(artist1, artist2));', ), priority=0) self.execute_sql(( 'CREATE TABLE IF NOT EXISTS tracks (id INTEGER PRIMARY KEY, artist' ' INTEGER, title VARCHAR(100), updated DATE, ' 'UNIQUE(artist, title));', ), priority=0) self.execute_sql( ('CREATE TABLE IF NOT EXISTS track_2_track (track1 INTEGER, track2' ' INTEGER, match INTEGER, UNIQUE(track1, track2));', ), priority=0) self.execute_sql( ("CREATE INDEX IF NOT EXISTS a2aa1x ON artist_2_artist " "(artist1);", ), priority=0) self.execute_sql( ("CREATE INDEX IF NOT EXISTS a2aa2x ON artist_2_artist " "(artist2);", ), priority=0) self.execute_sql( ("CREATE INDEX IF NOT EXISTS t2tt1x ON track_2_track (track1);", ), priority=0) self.execute_sql( ("CREATE INDEX IF NOT EXISTS t2tt2x ON track_2_track (track2);", ), priority=0) def delete_orphan_artist(self, artist): """Delete artists that have no tracks.""" sql = ('SELECT artists.id FROM artists WHERE artists.name = ? AND ' 'artists.id NOT IN (SELECT tracks.artist from tracks);', (artist, )) command = self.get_sql_command(sql, priority=10) for row in command.result_queue.get(): artist_id = row[0] self.execute_sql( ('DELETE FROM artist_2_artist WHERE artist1 = ? OR artist2 = ' '?;', (artist_id, artist_id)), priority=10) self.execute_sql( ('DELETE FROM artists WHERE id = ?', (artist_id, )), priority=10) def analyze_track(self, filename): """Perform gaia analysis of a track.""" if not filename: return if GAIA: self.gaia_queue.put((ADD, filename)) def analyze_tracks(self, filenames): """Analyze audio files.""" if not filenames: return if GAIA: for filename in filenames: self.gaia_queue.put((ADD, filename)) def get_similar_tracks_from_lastfm(self, artist_name, title, track_id, cutoff=0): """Get similar tracks.""" try: lastfm_track = self.network.get_track(artist_name, title) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] tracks_to_update = {} results = [] try: for similar in lastfm_track.get_similar(): match = int(100 * similar.match) if match <= cutoff: continue item = similar.item similar_artist = item.artist.get_name() similar_title = item.title tracks_to_update.setdefault(track_id, []).append({ 'score': match, 'artist': similar_artist, 'title': similar_title }) results.append((match, similar_artist, similar_title)) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] self.update_similar_tracks(tracks_to_update) return results def get_similar_artists_from_lastfm(self, artist_name, artist_id, cutoff=0): """Get similar artists from lastfm.""" try: lastfm_artist = self.network.get_artist(artist_name) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] artists_to_update = {} results = [] try: for similar in lastfm_artist.get_similar(): match = int(100 * similar.match) if match <= cutoff: continue name = similar.item.get_name() artists_to_update.setdefault(artist_id, []).append({ 'score': match, 'artist': name }) results.append((match, name)) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] self.update_similar_artists(artists_to_update) return results def get_ordered_similar_tracks(self, artist_name, title): """Get similar tracks from last.fm/the database. Sorted by descending match score. """ now = datetime.now() track = self.get_track_from_artist_and_title(artist_name, title) track_id, updated = track[0], track[3] if updated: updated = datetime(*strptime(updated, "%Y-%m-%d %H:%M:%S")[0:6]) if updated + timedelta(self.cache_time) > now: print("Getting similar tracks from db for: %s - %s" % (artist_name, title)) return self.get_similar_tracks(track_id) return self.get_similar_tracks_from_lastfm(artist_name, title, track_id) def get_ordered_similar_artists(self, artists): """Get similar artists from the database. Sorted by descending match score. """ results = [] now = datetime.now() for name in artists: artist_name = name result = None artist = self.get_artist(artist_name) artist_id, updated = artist[0], artist[2] if updated: updated = datetime( *strptime(updated, "%Y-%m-%d %H:%M:%S")[0:6]) if updated + timedelta(self.cache_time) > now: print("Getting similar artists from db for: %s " % artist_name) result = self.get_similar_artists(artist_id) if not result: result = self.get_similar_artists_from_lastfm( artist_name, artist_id) results.extend(result) results.sort(reverse=True) return results def miximize(self, filenames): """Return ideally ordered list of filenames.""" if not GAIA: return [] return self.gaia_analyser.get_miximized_tracks(filenames)
class Similarity(object): """Here the actual similarity computation and lookup happens.""" def __init__(self): data_dir = player_get_data_dir() self.db_path = os.path.join(data_dir, "similarity.db") self.gaia_db_path = os.path.join(data_dir, "gaia.db") self.db_queue = PriorityQueue() self._db_wrapper = DatabaseWrapper() self._db_wrapper.daemon = True self._db_wrapper.set_path(self.db_path) self._db_wrapper.set_queue(self.db_queue) self._db_wrapper.start() self.create_db() self.network = LastFMNetwork(api_key=API_KEY) self.cache_time = 90 if GAIA: self.gaia_queue = LifoQueue() self.gaia_analyser = GaiaAnalysis( self.gaia_db_path, self.gaia_queue) self.gaia_analyser.daemon = True self.gaia_analyser.start() def execute_sql(self, sql=None, priority=1, command=None): """Put sql command on the queue to be executed.""" if command is None: command = SQLCommand(sql) self.db_queue.put((priority, command)) def get_sql_command(self, sql, priority=1): """Build a SQLCommand, put it on the queue and return it.""" command = SQLCommand(sql) self.execute_sql(command=command, priority=priority) return command def remove_track_by_filename(self, filename): if not filename: return if GAIA: self.gaia_queue.put((REMOVE, filename)) def get_ordered_gaia_tracks_by_request(self, filename, number, request): start_time = time() tracks = self.gaia_analyser.get_tracks( filename, number, request=request) print("finding gaia matches took %f s" % (time() - start_time,)) return tracks def get_ordered_gaia_tracks(self, filename, number): """Get neighbours for track.""" start_time = time() tracks = self.gaia_analyser.get_tracks(filename, number) print("finding gaia matches took %f s" % (time() - start_time,)) return tracks def get_artist(self, artist_name): """Get artist information from the database.""" sql = ("SELECT * FROM artists WHERE name = ?;", (artist_name,)) command = self.get_sql_command(sql, priority=1) for row in command.result_queue.get(): return row sql2 = ("INSERT INTO artists (name) VALUES (?);", (artist_name,)) command = self.get_sql_command(sql2, priority=0) command.result_queue.get() command = self.get_sql_command(sql, priority=1) for row in command.result_queue.get(): return row def get_track_from_artist_and_title(self, artist_name, title): """Get track information from the database.""" artist_id = self.get_artist(artist_name)[0] sql = ( "SELECT * FROM tracks WHERE artist = ? AND title = ?;", (artist_id, title)) command = self.get_sql_command(sql, priority=3) for row in command.result_queue.get(): return row sql2 = ( "INSERT INTO tracks (artist, title) VALUES (?, ?);", (artist_id, title)) command = self.get_sql_command(sql2, priority=2) command.result_queue.get() command = self.get_sql_command(sql, priority=3) for row in command.result_queue.get(): return row def get_similar_tracks(self, track_id): """Get similar tracks from the database. Sorted by descending match score. """ sql = ( "SELECT track_2_track.match, artists.name, tracks.title" " FROM track_2_track INNER JOIN tracks ON" " track_2_track.track2 = tracks.id INNER JOIN artists ON" " artists.id = tracks.artist WHERE track_2_track.track1 = ? UNION " "SELECT track_2_track.match, artists.name, tracks.title" " FROM track_2_track INNER JOIN tracks ON" " track_2_track.track1 = tracks.id INNER JOIN artists ON" " artists.id = tracks.artist WHERE track_2_track.track2" " = ? ORDER BY track_2_track.match DESC;", (track_id, track_id)) command = self.get_sql_command(sql, priority=0) return command.result_queue.get() def get_similar_artists(self, artist_id): """Get similar artists from the database. Sorted by descending match score. """ sql = ( "SELECT match, name FROM artist_2_artist INNER JOIN" " artists ON artist_2_artist.artist2 = artists.id WHERE" " artist_2_artist.artist1 = ? UNION " "SELECT match, name FROM artist_2_artist INNER JOIN" " artists ON artist_2_artist.artist1 = artists.id WHERE" " artist_2_artist.artist2 = ? ORDER BY match DESC;", (artist_id, artist_id)) command = self.get_sql_command(sql, priority=0) return command.result_queue.get() def get_artist_match(self, artist1, artist2): """Get artist match score from database.""" sql = ( "SELECT match FROM artist_2_artist WHERE artist1 = ?" " AND artist2 = ?;", (artist1, artist2)) command = self.get_sql_command(sql, priority=2) for row in command.result_queue.get(): return row[0] return 0 def get_track_match(self, track1, track2): """Get track match score from database.""" sql = ( "SELECT match FROM track_2_track WHERE track1 = ? AND track2 = ?;", (track1, track2)) command = self.get_sql_command(sql, priority=2) for row in command.result_queue.get(): return row[0] return 0 def update_artist_match(self, artist1, artist2, match): """Write match score to the database.""" self.execute_sql(( "UPDATE artist_2_artist SET match = ? WHERE artist1 = ? AND" " artist2 = ?;", (match, artist1, artist2)), priority=10) def update_track_match(self, track1, track2, match): """Write match score to the database.""" self.execute_sql(( "UPDATE track_2_track SET match = ? WHERE track1 = ? AND" " track2 = ?;", (match, track1, track2)), priority=10) def insert_artist_match(self, artist1, artist2, match): """Write match score to the database.""" self.execute_sql(( "INSERT INTO artist_2_artist (artist1, artist2, match) VALUES" " (?, ?, ?);", (artist1, artist2, match)), priority=10) def insert_track_match(self, track1, track2, match): """Write match score to the database.""" self.execute_sql(( "INSERT INTO track_2_track (track1, track2, match) VALUES" " (?, ?, ?);", (track1, track2, match)), priority=10) def update_artist(self, artist_id): """Write artist information to the database.""" self.execute_sql(( "UPDATE artists SET updated = DATETIME('now') WHERE id = ?;", (artist_id,)), priority=10) def update_track(self, track_id): """Write track information to the database.""" self.execute_sql(( "UPDATE tracks SET updated = DATETIME('now') WHERE id = ?", (track_id,)), priority=10) def update_similar_artists(self, artists_to_update): """Write similar artist information to the database.""" for artist_id, similar in list(artists_to_update.items()): for artist in similar: row = self.get_artist(artist['artist']) if row: id2 = row[0] if self.get_artist_match(artist_id, id2): self.update_artist_match( artist_id, id2, artist['score']) continue self.insert_artist_match(artist_id, id2, artist['score']) self.update_artist(artist_id) def update_similar_tracks(self, tracks_to_update): """Write similar track information to the database.""" for track_id, similar in list(tracks_to_update.items()): for track in similar: row = self.get_track_from_artist_and_title( track['artist'], track['title']) if row: id2 = row[0] if self.get_track_match(track_id, id2): self.update_track_match(track_id, id2, track['score']) continue self.insert_track_match(track_id, id2, track['score']) self.update_track(track_id) def create_db(self): """Set up a database for the artist and track similarity scores.""" self.execute_sql(( 'CREATE TABLE IF NOT EXISTS artists (id INTEGER PRIMARY KEY, name' ' VARCHAR(100), updated DATE, UNIQUE(name));',), priority=0) self.execute_sql( ('CREATE TABLE IF NOT EXISTS artist_2_artist (artist1 INTEGER,' ' artist2 INTEGER, match INTEGER, UNIQUE(artist1, artist2));',), priority=0) self.execute_sql(( 'CREATE TABLE IF NOT EXISTS tracks (id INTEGER PRIMARY KEY, artist' ' INTEGER, title VARCHAR(100), updated DATE, ' 'UNIQUE(artist, title));',), priority=0) self.execute_sql(( 'CREATE TABLE IF NOT EXISTS track_2_track (track1 INTEGER, track2' ' INTEGER, match INTEGER, UNIQUE(track1, track2));',), priority=0) self.execute_sql(( "CREATE INDEX IF NOT EXISTS a2aa1x ON artist_2_artist " "(artist1);",), priority=0) self.execute_sql(( "CREATE INDEX IF NOT EXISTS a2aa2x ON artist_2_artist " "(artist2);",), priority=0) self.execute_sql( ("CREATE INDEX IF NOT EXISTS t2tt1x ON track_2_track (track1);",), priority=0) self.execute_sql( ("CREATE INDEX IF NOT EXISTS t2tt2x ON track_2_track (track2);",), priority=0) def delete_orphan_artist(self, artist): """Delete artists that have no tracks.""" sql = ( 'SELECT artists.id FROM artists WHERE artists.name = ? AND ' 'artists.id NOT IN (SELECT tracks.artist from tracks);', (artist,)) command = self.get_sql_command(sql, priority=10) for row in command.result_queue.get(): artist_id = row[0] self.execute_sql(( 'DELETE FROM artist_2_artist WHERE artist1 = ? OR artist2 = ' '?;', (artist_id, artist_id)), priority=10) self.execute_sql( ('DELETE FROM artists WHERE id = ?', (artist_id,)), priority=10) def analyze_track(self, filename): """Perform gaia analysis of a track.""" if not filename: return if GAIA: self.gaia_queue.put((ADD, filename)) def analyze_tracks(self, filenames): """Analyze audio files.""" if not filenames: return if GAIA: for filename in filenames: self.gaia_queue.put((ADD, filename)) def get_similar_tracks_from_lastfm(self, artist_name, title, track_id, cutoff=0): """Get similar tracks.""" try: lastfm_track = self.network.get_track(artist_name, title) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] tracks_to_update = {} results = [] try: for similar in lastfm_track.get_similar(): match = int(100 * similar.match) if match <= cutoff: continue item = similar.item similar_artist = item.artist.get_name() similar_title = item.title tracks_to_update.setdefault(track_id, []).append({ 'score': match, 'artist': similar_artist, 'title': similar_title}) results.append((match, similar_artist, similar_title)) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] self.update_similar_tracks(tracks_to_update) return results def get_similar_artists_from_lastfm(self, artist_name, artist_id, cutoff=0): """Get similar artists from lastfm.""" try: lastfm_artist = self.network.get_artist(artist_name) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] artists_to_update = {} results = [] try: for similar in lastfm_artist.get_similar(): match = int(100 * similar.match) if match <= cutoff: continue name = similar.item.get_name() artists_to_update.setdefault(artist_id, []).append({ 'score': match, 'artist': name}) results.append((match, name)) except (WSError, NetworkError, MalformedResponseError) as e: print(e) return [] self.update_similar_artists(artists_to_update) return results def get_ordered_similar_tracks(self, artist_name, title): """Get similar tracks from last.fm/the database. Sorted by descending match score. """ now = datetime.now() track = self.get_track_from_artist_and_title( artist_name, title) track_id, updated = track[0], track[3] if updated: updated = datetime(*strptime(updated, "%Y-%m-%d %H:%M:%S")[0:6]) if updated + timedelta(self.cache_time) > now: print( "Getting similar tracks from db for: %s - %s" % ( artist_name, title)) return self.get_similar_tracks(track_id) return self.get_similar_tracks_from_lastfm( artist_name, title, track_id) def get_ordered_similar_artists(self, artists): """Get similar artists from the database. Sorted by descending match score. """ results = [] now = datetime.now() for name in artists: artist_name = name result = None artist = self.get_artist(artist_name) artist_id, updated = artist[0], artist[2] if updated: updated = datetime( *strptime(updated, "%Y-%m-%d %H:%M:%S")[0:6]) if updated + timedelta(self.cache_time) > now: print( "Getting similar artists from db for: %s " % artist_name) result = self.get_similar_artists(artist_id) if not result: result = self.get_similar_artists_from_lastfm( artist_name, artist_id) results.extend(result) results.sort(reverse=True) return results def miximize(self, filenames): """Return ideally ordered list of filenames.""" if not GAIA: return [] return self.gaia_analyser.get_miximized_tracks(filenames)
class LastFm: # You have to have your own unique two values for API_KEY and API_SECRET # Obtain yours from http://www.last.fm/api/account for Last.fm # API_KEY = '2b532992c84242d372f5c0044d6883e5' # API_SECRET = '3c6688ac84deda063a697f5662a93eb0' API_KEY = '8fc05a68240dadf4c2430392768053fe' API_SECRET = 'bc2d48b14f3e864c6a07bbb6f9a0b690' URL_AUTH = 'http://www.last.fm/api/auth/?api_key={}'.format(API_KEY) URL_CALLBACK = 'http%3A%2F%2F127.0.0.1%3A5656%2Flastfm%2Fcallback' network = None LOVE_CUTOFF = 0.97 def __init__(self, token=''): """Always create network""" with shelve.open('lastfm') as db: session_key = db.get('session_key') self.network = LastFMNetwork( api_key=self.API_KEY, api_secret=self.API_SECRET, session_key=session_key, token=token ) if token: app.logger.info('saving session key: {}'.format(self.network.session_key)) db['session_key'] = self.network.session_key def scrobble(self, history): """Scrobble song to lastfm""" params = { 'artist': history.song.artist.name, 'album': history.song.album.name, 'title': history.song.name, 'track_number': history.song.track_number, 'timestamp': int(history.played_at.timestamp()), } app.logger.info('scrobbling: {}'.format(params)) self.network.scrobble(**params) def show_some_love(self, songs): """Sets track to love or not""" app.logger.info('showing some love for {} songs'.format(len(songs))) for song in songs: db.session.refresh(song) network_track = self.network.get_track(song.artist.name, song.name) is_loved = song.rating >= self.LOVE_CUTOFF app.logger.debug('[{:.0f}%] {} loving {}'.format( song.rating * 100, is_loved, network_track)) if is_loved: network_track.love() else: network_track.unlove() # is_loved = network_track.get_userloved() # app.logger.debug('found network track {} loved {}'.format(network_track, is_loved)) # if is_loved: # if song.rating < self.LOVE_CUTOFF: # app.logger.info('lost love {} [{:.0f}%]'.format(network_track, song.rating * # 100)) # res = network_track.unlove() # app.logger.debug(res) # else: # app.logger.info('still loving {} [{:.0f}%]'.format(network_track, song.rating * # 100)) # else: # res = network_track.unlove() # app.logger.debug(res) # if song.rating >= self.LOVE_CUTOFF: # app.logger.info('new love {} [{:.0f}%]'.format(network_track, song.rating * # 100)) # res = network_track.love() # app.logger.debug(res) # else: # app.logger.info('still no love for {} [{:.0f}%]'.format(network_track, # song.rating * 100)) def get_user_top_albums(self, user_name, period=None): """Get top albums for user""" period = period or PERIOD_12MONTHS user = self.network.get_user(user_name) return user.get_top_albums(period) def get_user_playcount(self, user): """Get playcount of user""" def get_similar_tracks(self, artist, title): """Get similar tracks to this song""" track = self.network.get_track(artist, title) similar = track.get_similar() app.logger.info('Found {} similar tracks for {} {}'.format(len(similar), artist, title)) return similar
class LastFm: # You have to have your own unique two values for API_KEY and API_SECRET # Obtain yours from http://www.last.fm/api/account for Last.fm # API_KEY = '2b532992c84242d372f5c0044d6883e5' # API_SECRET = '3c6688ac84deda063a697f5662a93eb0' API_KEY = '8fc05a68240dadf4c2430392768053fe' API_SECRET = 'bc2d48b14f3e864c6a07bbb6f9a0b690' URL_AUTH = 'http://www.last.fm/api/auth/?api_key={}'.format(API_KEY) URL_CALLBACK = 'http%3A%2F%2F127.0.0.1%3A5656%2Flastfm%2Fcallback' network = None LOVE_CUTOFF = 0.97 def __init__(self, token=''): """Always create network""" with shelve.open('lastfm') as db: session_key = db.get('session_key') self.network = LastFMNetwork(api_key=self.API_KEY, api_secret=self.API_SECRET, session_key=session_key, token=token) if token: app.logger.info('saving session key: {}'.format( self.network.session_key)) db['session_key'] = self.network.session_key def scrobble(self, history): """Scrobble song to lastfm""" params = { 'artist': history.song.artist.name, 'album': history.song.album.name, 'title': history.song.name, 'track_number': history.song.track_number, 'timestamp': int(history.played_at.timestamp()), } app.logger.info('scrobbling: {}'.format(params)) self.network.scrobble(**params) def show_some_love(self, songs): """Sets track to love or not""" app.logger.info('showing some love for {} songs'.format(len(songs))) for song in songs: db.session.refresh(song) network_track = self.network.get_track(song.artist.name, song.name) is_loved = song.rating >= self.LOVE_CUTOFF app.logger.debug('[{:.0f}%] {} loving {}'.format( song.rating * 100, is_loved, network_track)) if is_loved: network_track.love() else: network_track.unlove() # is_loved = network_track.get_userloved() # app.logger.debug('found network track {} loved {}'.format(network_track, is_loved)) # if is_loved: # if song.rating < self.LOVE_CUTOFF: # app.logger.info('lost love {} [{:.0f}%]'.format(network_track, song.rating * # 100)) # res = network_track.unlove() # app.logger.debug(res) # else: # app.logger.info('still loving {} [{:.0f}%]'.format(network_track, song.rating * # 100)) # else: # res = network_track.unlove() # app.logger.debug(res) # if song.rating >= self.LOVE_CUTOFF: # app.logger.info('new love {} [{:.0f}%]'.format(network_track, song.rating * # 100)) # res = network_track.love() # app.logger.debug(res) # else: # app.logger.info('still no love for {} [{:.0f}%]'.format(network_track, # song.rating * 100)) def get_user_top_albums(self, user_name, period=None): """Get top albums for user""" period = period or PERIOD_12MONTHS user = self.network.get_user(user_name) return user.get_top_albums(period) def get_user_playcount(self, user): """Get playcount of user""" def get_similar_tracks(self, artist, title): """Get similar tracks to this song""" track = self.network.get_track(artist, title) similar = track.get_similar() app.logger.info('Found {} similar tracks for {} {}'.format( len(similar), artist, title)) return similar