def _connect(self, username, password, populate_loved=False): """ Connect lastfm @param username as str @param password as str @thread safe """ self._username = username if self._goa is not None or (password != '' and username != ''): self._is_auth = True else: self._is_auth = False try: self._check_for_proxy() if self._goa is None: LastFMNetwork.__init__( self, api_key=self._API_KEY, api_secret=self._API_SECRET, username=self._username, password_hash=md5(password)) else: LastFMNetwork.__init__( self, api_key=self._API_KEY, api_secret=self._API_SECRET, session_key=self._goa.call_get_access_token_sync(None)[0]) if populate_loved: self._populate_loved_tracks() except Exception as e: print("Lastfm::_connect(): %s" % e)
def _scrobble(self, artist, album, title, timestamp, duration, first=True): """ Scrobble track @param artist as str @param title as str @param album_title as str @param timestamp as int @param duration as int @param first is internal @thread safe """ debug("LastFM::_scrobble(): %s, %s, %s, %s, %s" % (artist, album, title, timestamp, duration)) try: LastFMNetwork.scrobble(self, artist=artist, album=album, title=title, timestamp=timestamp) except BadAuthenticationError as e: pass except Exception as e: print("Lastfm::scrobble():", e) # Scrobble sometimes fails if first: self._connect(self._username, self._password)
def _connect(self, username, password, populate_loved=False): """ Connect lastfm @param username as str @param password as str @thread safe """ self._username = username if password != "" and username != "": self._is_auth = True else: self._is_auth = False try: self._check_for_proxy() LastFMNetwork.__init__( self, api_key=self._API_KEY, api_secret=self._API_SECRET, username=Lp.settings.get_value("lastfm-login").get_string(), password_hash=md5(password), ) if populate_loved: self._populate_loved_tracks() except Exception as e: print("Lastfm::_connect(): %s" % e)
def _scrobble(self, artist, album, title, timestamp, duration): """ Scrobble track @param artist as str @param title as str @param album_title as str @param timestamp as int @param duration as int @thread safe """ debug("LastFM::_scrobble(): %s, %s, %s, %s, %s" % (artist, album, title, timestamp, duration)) try: LastFMNetwork.scrobble(self, artist=artist, album=album, title=title, timestamp=timestamp) except BadAuthenticationError: pass except: self._connect(self._username, self._password)
def _now_playing(self, artist, album, title, duration, first=True): """ Now playing track @param artist as str @param title as str @param album as str @param duration as int @param first is internal @thread safe """ debug("LastFM::_now_playing(): %s, %s, %s, %s" % (artist, album, title, duration)) try: LastFMNetwork.update_now_playing(self, artist=artist, album=album, title=title, duration=duration) except BadAuthenticationError: if Lp.notify is not None: GLib.idle_add(Lp.notify.send, _("Wrong Last.fm credentials")) except: # now playing sometimes fails if first: self._connect(self._username, self._password) self._now_playing(artist, album, title, duration, False)
def __init__(self): """ Init lastfm support """ try: self._settings = Gio.Settings.new("org.gnome.system.proxy.http") except: self._settings = None LastFMNetwork.__init__(self, api_key=self._API_KEY, api_secret=self._API_SECRET) self._username = "" self._is_auth = False self._password = None self._check_for_proxy() self.connect(None)
class Scrobbler(object): """ classdocs """ def __init__(self): """ Constructor """ self.API = "b7b91229e4232d9e6dfa232b3937923e" self.secret = "ad646ed84aad9da4f1189454a4872cd9" # Allow users to create a scrobbler without logging in first self.authenticated = False def login(self, username, password): """ Log in using the username and password provided. Use this function before scrobbling. Returns error details in case of failure :param password: :param username: """ password_hash = md5(password) try: self.lastfm = LastFMNetwork(self.API, self.secret, None, username, password_hash) except WSError: raise else: self.authenticated = self.lastfm.get_authenticated_user().get_name() == username def scrobble(self, data): """ Scrobble a list of dicts that each contain a single parsed track to Last.fm :param data: the tracks to scrobble """ if self.authenticated: self.lastfm.scrobble_many(data) else: raise WSError("You are not authenticated yet, please use the login function before scrobbling.") return data
def __init__(self): """ Init lastfm support """ self.__username = '' self.__is_auth = False self.__password = None self.__check_for_proxy() self.__goa = self.__get_goa_oauth() if self.__goa is None and Lp().settings.get_value('use-librefm'): LibreFMNetwork.__init__(self) else: if self.__goa is not None: self.__API_KEY = self.__goa.props.client_id self.__API_SECRET = self.__goa.props.client_secret else: self.__API_KEY = '7a9619a850ccf7377c46cf233c51e3c6' self.__API_SECRET = '9254319364d73bec6c59ace485a95c98' LastFMNetwork.__init__(self, api_key=self.__API_KEY, api_secret=self.__API_SECRET) self.connect(None)
def login(self, username, password): """ Log in using the username and password provided. Use this function before scrobbling. Returns error details in case of failure :param password: :param username: """ password_hash = md5(password) try: self.lastfm = LastFMNetwork(self.API, self.secret, None, username, password_hash) except WSError: raise else: self.authenticated = self.lastfm.get_authenticated_user().get_name() == username
def __init__(self): """ Init lastfm support """ try: self._settings = Gio.Settings.new('org.gnome.system.proxy.http') except: self._settings = None self._username = '' self._is_auth = False self._password = None self._check_for_proxy() self._goa = self._get_goa_oauth() if self._goa is not None: self._API_KEY = self._goa.props.client_id self._API_SECRET = self._goa.props.client_secret else: self._API_KEY = '7a9619a850ccf7377c46cf233c51e3c6' self._API_SECRET = '9254319364d73bec6c59ace485a95c98' LastFMNetwork.__init__(self, api_key=self._API_KEY, api_secret=self._API_SECRET) self.connect(None)
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 __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 get_art(self, song): """Get artwork from LastFM. Before connecting to network, if there exists a file whose name begins with ``_sanitize(song['artist'] + ' ' + song['album'])``, return that file path. Do not overwrite existing files. :param dict song: A dictionary with keys ``'album'`` and ``'artist'`` to correspond with string representations of the album and artist (resp.) of interest. Use ``MPDClient.currentsong()`` to return uch a dictionary . :return: A string representation of the local file path to the image file for ``song`` or ``None`` if no results found """ album_tofind = _sanitize(song['artist'], song['album']) l = [ n for n in os.listdir(self.save_dir) if n.startswith(album_tofind) ] if l != []: file_path = os.path.join(self.save_dir, l[0]) sys.stderr.write("Found {}\n".format(file_path)) return file_path # Define the search network compatible with LastFM API network = LastFMNetwork(api_key=_last_fm_api_key) album_search = AlbumSearch(song['album'], network) #if int(album_search.get_total_result_count()) == 0: if album_search.get_total_result_count() == None: # LastFm does not have this album, links to unknown.png sys.stderr.write("Last.FM: no results\n") unknown = os.path.join(self.save_dir, 'unknown.png') album_tofind += ".png" os.symlink(unknown, os.path.join(self.save_dir, album_tofind)) return album_tofind # Get the first hit, since there is at least one result - # the "I'm feeling lucky" approach. album = album_search.get_next_page()[0] # Get url of album art from ``pylast.AlbumSearch`` object img_url = album.get_cover_image() file_path = os.path.join(self.save_dir, self._get_save_name(song, img_url)) # Check if this file exists in filesystem already if os.path.isfile(file_path): sys.stderr.write( "Last.FM: we already had the album {}!\n".format(file_path)) return file_path else: try: # Download the image urlretrieve(img_url, file_path) except e: sys.stderr.write(e + "\n") return None sys.stderr.write("Last.FM: found {}\n".format(file_path)) return file_path
class NowplayingBot(PineappleBot): def init(self): self.last_posted_track = None def start(self): for k in [ 'lastfm_api_key', 'lastfm_api_secret', 'lastfm_username', 'lastfm_password_hash', 'youtube_key', 'use_last_played' ]: if k not in self.config: raise ConfigurationError(f"NowplayingBot requires a '{k}'") self.lastfm = LastFMNetwork( api_key=self.config.lastfm_api_key, api_secret=self.config.lastfm_api_secret, username=self.config.lastfm_username, password_hash=self.config.lastfm_password_hash) self.post_np() @interval(30) def post_np(self): # grab the track from the last.fm api if self.config.use_last_played == 'yes': currently_playing = self.lastfm.get_user( self.config.lastfm_username).get_recent_tracks(1)[0][0] else: currently_playing = self.lastfm.get_user( self.config.lastfm_username).get_now_playing() # don't try to post if nothing is being played if currently_playing is None: return # don't repost if we've already posted about this track if currently_playing.__hash__() == self.last_posted_track: return else: self.last_posted_track = currently_playing.__hash__() # make a best-effort guess at the youtube link for this track yt_search = search_youtube(str(currently_playing), key=self.config.youtube_key) if len(yt_search) > 0: yt_link = f"https://www.youtube.com/watch?v={yt_search[0]['id']}" else: yt_link = '🎇 no youtube link, sorry 🎇' # template the post post_template = '''\ #np #nowplaying #fediplay {artist} - {track} {yt_link}''' post = post_template.format( artist=currently_playing.get_artist().get_name( properly_capitalized=True), track=currently_playing.get_title(), yt_link=yt_link) # do the thing self.mastodon.status_post(post, visibility='unlisted')
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
from flask import Flask app = Flask(__name__) app.config.from_pyfile("../config.cfg") from pylast import LastFMNetwork lastfm = LastFMNetwork(api_key=app.config["LASTFM_API_KEY"], api_secret=app.config["LASTFM_API_SECRET"]) from . import views, models
def lastfm(keyword): network = LastFMNetwork(api_key=API_KEY, api_secret=API_SECRET) suggestion = network.search_for_track("", keyword).get_next_page()[0] artist, title = suggestion.artist.get_name(), suggestion.title print(artist, "-", title) return artist, title
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
def get_last(): with open('instance/config.yaml', 'r') as fp: config = yaml.load(fp) return LastFMNetwork(api_key=config['lastfm_api_key'], api_secret=config['lastfm_api_secret'])
# Clean Welcome CLEAN_WELCOME = sb(os.environ.get("CLEAN_WELCOME", "True")) # Last.fm Module BIO_PREFIX = os.environ.get("BIO_PREFIX", None) DEFAULT_BIO = os.environ.get("DEFAULT_BIO", None) LASTFM_API = os.environ.get("LASTFM_API", None) LASTFM_SECRET = os.environ.get("LASTFM_SECRET", None) LASTFM_USERNAME = os.environ.get("LASTFM_USERNAME", None) LASTFM_PASSWORD_PLAIN = os.environ.get("LASTFM_PASSWORD", None) LASTFM_PASS = md5(LASTFM_PASSWORD_PLAIN) if LASTFM_API and LASTFM_SECRET and LASTFM_USERNAME and LASTFM_PASS: lastfm = LastFMNetwork(api_key=LASTFM_API, api_secret=LASTFM_SECRET, username=LASTFM_USERNAME, password_hash=LASTFM_PASS) else: lastfm = None # Google Drive Module G_DRIVE_CLIENT_ID = os.environ.get("G_DRIVE_CLIENT_ID", None) G_DRIVE_CLIENT_SECRET = os.environ.get("G_DRIVE_CLIENT_SECRET", None) G_DRIVE_AUTH_TOKEN_DATA = os.environ.get("G_DRIVE_AUTH_TOKEN_DATA", None) GDRIVE_FOLDER_ID = os.environ.get("GDRIVE_FOLDER_ID", None) TEMP_DOWNLOAD_DIRECTORY = os.environ.get("TMP_DOWNLOAD_DIRECTORY", "./downloads") # Setting Up CloudMail.ru and MEGA.nz extractor binaries, # and giving them correct perms to work properly. if not os.path.exists('bin'):
def get_art(self, song): """Get artwork from LastFM. Before connecting to network, if there exists a file whose name begins with ``_sanitize(song['artist'] + ' ' + song['album'])``, return that file path. Do not overwrite existing files. Set ``os.path.join(save_dir, 'current')`` as symlink to image file. :param dict song: A dictionary with keys ``'album'`` and ``'artist'`` to correspond with string representations of the album and artist (resp.) of interest. Use ``MPDClient.currentsong()`` to return uch a dictionary . :return: A string representation of the local file path to the image file for ``song`` or ``None`` if no results found """ l = [ n for n in os.listdir(self.save_dir) if n.startswith(song['artist'] + "_" + song['album']) ] if l != []: sys.stderr.write("Already have this album\n") file_path = os.path.join(self.save_dir, l[0]) # We have album art - check if it's linked if os.path.realpath(self.link_path) != file_path: sys.stderr.write("Linking...\n") self.remove_current_link() self.set_current_link(file_path) sys.stderr.write("Exiting.\n") return file_path # Define the search network compatible with LastFM API network = LastFMNetwork(api_key=_last_fm_api_key) album_search = AlbumSearch(song['artist'] + ' ' + song['album'], network) if album_search.get_total_result_count() == 0: # Remove current album link, and return, since no art was found for # given query. sys.stderr.write("No results from Last.FM\n") self.remove_current_link() return None # Get the first hit, since there is at least one result - # the "I'm feeling lucky" approach. album = album_search.get_next_page()[0] # Get url of album art from ``pylast.AlbumSearch`` object img_url = album.get_cover_image() file_path = os.path.join(self.save_dir, self._get_save_name(song, img_url)) # Check if this file exists in filesystem already and is linked if os.path.isfile(file_path): if os.path.realpath(self.link_path) == file_path: return file_path else: try: # Download the image urlretrieve(img_url, file_path) self.remove_current_link() except HTTPError as e: sys.stderr.write(e + "\n") self.remove_current_link() return None self.set_current_link(file_path) return file_path
def Client(): global _network if not _network: _network = LastFMNetwork(api_key=api_k, api_secret=api_s) _network.enable_rate_limit() return _network
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)
def __init__(self, username=''): self._username = username self._api = LastFMNetwork(api_key=get_last_fm_api_key()) self._user = self._api.get_user(self._username)
TZ_NUMBER = int(os.environ.get("TZ_NUMBER", 1)) # Clean Welcome CLEAN_WELCOME = sb(os.environ.get("CLEAN_WELCOME", "True")) # Last.fm Module BIO_PREFIX = os.environ.get("BIO_PREFIX", None) DEFAULT_BIO = os.environ.get("DEFAULT_BIO", None) LASTFM_API = os.environ.get("LASTFM_API", None) LASTFM_SECRET = os.environ.get("LASTFM_SECRET", None) LASTFM_USERNAME = os.environ.get("LASTFM_USERNAME", None) LASTFM_PASSWORD_PLAIN = os.environ.get("LASTFM_PASSWORD", None) LASTFM_PASS = md5(LASTFM_PASSWORD_PLAIN) if LASTFM_API and LASTFM_SECRET and LASTFM_USERNAME and LASTFM_PASS: lastfm = LastFMNetwork(api_key=LASTFM_API, api_secret=LASTFM_SECRET, username=LASTFM_USERNAME, password_hash=LASTFM_PASS) else: lastfm = None # Google Drive Module G_DRIVE_CLIENT_ID = os.environ.get("G_DRIVE_CLIENT_ID", None) G_DRIVE_CLIENT_SECRET = os.environ.get("G_DRIVE_CLIENT_SECRET", None) G_DRIVE_AUTH_TOKEN_DATA = os.environ.get("G_DRIVE_AUTH_TOKEN_DATA", None) GDRIVE_FOLDER_ID = os.environ.get("GDRIVE_FOLDER_ID", None) TEMP_DOWNLOAD_DIRECTORY = os.environ.get("TMP_DOWNLOAD_DIRECTORY", "./downloads") # Setting Up CloudMail.ru and MEGA.nz extractor binaries,