async def lastfm(event): if event.fwd_from: return username = event.pattern_match.group(1) if Config.LAST_FM_API_KEY is None: await event.edit("`Setup LAST_FM_API_KEY first.`") return if username is None and Config.LAST_FM_USERNAME is None: await event.edit("`Setup LAST_FM_USERNAME or Provide username first.`") return if not username: username = Config.LAST_FM_USERNAME network = LastFMNetwork(api_key=Config.LAST_FM_API_KEY) user = network.get_user(username) current_track = user.get_now_playing() if current_track: try: track_cover = current_track.get_cover_image() except IndexError: track_cover = None pass msg = "**{}** is currently listening to:\n [🎧]({}) `{}`".format( user, track_cover, current_track) else: msg = "**{}** was listening to:\n".format(username) recent_tracks = user.get_recent_tracks(limit=3) for played_track in recent_tracks: msg += "🎧 `{}`\n".format(played_track.track) await event.delete() await borg.send_message(event.chat_id, msg, link_preview=True)
def authenticate(): """Authenticate to use LastFM API.""" try: API_KEY = os.environ['LASTFM_API_KEY'] API_SECRET = os.environ['LASTFM_API_SECRET'] LASTFM_USERNAME = os.environ['LASTFM_USERNAME'] LASTFM_PASSWORD_HASH = os.environ['LASTFM_PASSWORD_HASH'] LASTFM_NETWORK = LastFMNetwork(api_key=API_KEY, api_secret=API_SECRET, username=LASTFM_USERNAME, password_hash=LASTFM_PASSWORD_HASH) user = LASTFM_NETWORK.get_user(LASTFM_USERNAME) except KeyError: print("Run setup.sh before trying this!") exit(1) return user
class LastFM: 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) def try_download_cover(self, performer, title, path): title = self._api.get_album(performer, title) try: cover = title.get_cover_image() except WSError: return False cover_data = urlopen(cover).read() with open(path.joinpath('cover.png'), 'wb') as f: f.write(cover_data) return True def get_weekly_top_albums(self): return self._user.get_weekly_album_charts() def search_album(self, performer, title): suitable = [] for album in self._api.search_for_album(title).get_next_page(): if album.artist.name.lower() == performer.lower(): suitable.append(album) if len(suitable) > 0: return max(*suitable, key=lambda a: len(a.get_tracks())) def get_recommended_albums(self, library, max=20) -> List[Album]: result = [] top_albums = self.get_weekly_top_albums() existed_albums = {performer: set(alb.name.lower() for alb in albums) for performer, albums in library.library} for album, weight in top_albums: if len(result) >= max: break performer = album.artist.get_name().lower() title = album.get_name().lower() if title not in existed_albums.get(performer, set()): result.append(album) return result
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 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