def search_song(self, title, artist="", get_full_info=True): """ Search Genius.com for lyrics to a specific song :param title: Song title to search for :param artist: Name of the artist :param get_full_info: Get full info for each song (slower) """ # Search the Genius API for the specified song if self.verbose: if artist: print('Searching for "{s}" by {a}...'.format(s=title, a=artist)) else: print('Searching for "{s}"...'.format(s=title)) search_term = "{s} {a}".format(s=title, a=artist).strip() response = self.search_genius_web(search_term) # Use old song search method if search_genius_web fails if not response: if self.verbose: print("\nsearch_genius_web failed, using old search method.") return self._search_song_old(title, artist, get_full_info, True) # Otherwise, move forward with processing the search results result = self._get_item_from_search_response(response, type_="song") # Exit search if there were no results returned from API if not result: if self.verbose: print("No results found for: '{s}'".format(s=search_term)) return None # Reject non-songs (Liner notes, track lists, etc.) if self.skip_non_songs: song_is_valid = self._result_is_lyrics(result['title']) else: song_is_valid = True if not song_is_valid: if self.verbose: print('Specified song does not contain lyrics. Rejecting.') return None # Download full song info (an API call) unless told not to by user if get_full_info: song_info = self.get_song(result['id'])['song'] else: song_info = result lyrics = self._scrape_song_lyrics_from_url(song_info['url']) # Skip results when URL is a 404 or lyrics are missing if not lyrics: if self.verbose: print('Specified song does not have a valid URL with lyrics. Rejecting.') return None # Return a Song object with lyrics if we've made it this far song = Song(song_info, lyrics) if self.verbose: print('Done.') return song
def create_song_for_db(song: GSong, artistId, albumId, mb_track: Track): new_song = Song(title=song.title, lyrics=song.lyrics, genius_id=song._id, artist_id=artistId, album_id=albumId, genius_data=song.to_dict(), music_brainz_data=mb_track.mb_data.to_dict()) return new_song
def parse(self, title, artist=None): msg = "Token invalid or missing, skipping Genius" self.validate(self.genius, msg, verbose_gte=3) try: if artist: response = self.genius.search_songs(f"{artist} {title}") else: response = self.genius.search_songs(title) except TypeError: # token wasn't passed in or found in env print("Genius token invalid or missing") self.genius = None return None results = response["hits"] self.validate_lyrics_found(results, title) # Reject non-songs (Liner notes, track lists, etc.), look for a best title match # take the first result if no exact match found get_lyrics = self.genius._result_is_lyrics results = [r["result"] for r in results if get_lyrics(r["result"])] result = None if results: cln_title = _clean_str(title) result = next( (r for r in results if _clean_str(r["title"]) == cln_title), results[0], ) self.validate(result, f"No valid Genius result for {title}.", verbose_gte=2) lyrics = self.genius.lyrics(result["id"]) # Skip results when URL is a 404 or lyrics are missing self.validate(lyrics, f"No valid URL with lyrics for {title}", verbose_gte=2) result = Song(result, lyrics) if artist: self.validate_artist(artist, result.artist) self.validate_title(title, result.title) return result.lyrics
def _get_song_object(self, song_info): # Check if song is valid (e.g. has title, contains lyrics) has_title = ('title' in song_info) has_lyrics = self._result_is_lyrics(song_info['title']) valid = has_title and (has_lyrics or (not self.skip_non_songs)) # Reject non-song results (e.g. Linear Notes, Tracklists, etc.) if not valid: if self.verbose: s = song_info['title'] if has_title else "MISSING TITLE" print('"{s}" is not valid. Skipping.'.format(s=s)) return None # Create the Song object from lyrics and metadata lyrics = self._scrape_song_lyrics_from_url(song_info['url']) if not lyrics: return None try: info = self.get_song(song_info['id']) song = Song(info, lyrics) return song except Exception as e: return None
def search_artist(self, artist_name, verbose=True, max_songs=None, take_first_result=False, get_full_song_info=True, remove_section_headers=False, remove_non_songs=True): """Allow user to search for an artist on the Genius.com database by supplying an artist name. Returns an Artist() object containing all songs for that particular artist.""" if verbose: print('Searching for songs by {0}...\n'.format(artist_name)) # Perform a Genius API search for the artist json_search = self._make_api_request((artist_name, 'search')) first_result, artist_id = None, None for hit in json_search['hits']: found_artist = hit['result']['primary_artist'] if first_result is None: first_result = found_artist artist_id = found_artist['id'] if take_first_result or self._clean_str( found_artist['name'].lower()) == self._clean_str( artist_name.lower()): artist_name = found_artist['name'] break else: # check for searched name in alternate artist names json_artist = self._make_api_request( (artist_id, 'artist'))['artist'] if artist_name.lower() in [ s.lower() for s in json_artist['alternate_names'] ]: if verbose: print("Found alternate name. Changing name to {}.". format(json_artist['name'])) artist_name = json_artist['name'] break artist_id = None if first_result is not None and artist_id is None and verbose: if input("Couldn't find {}. Did you mean {}? (y/n): ".format( artist_name, first_result['name'])).lower() == 'y': artist_name, artist_id = first_result['name'], first_result[ 'id'] assert (not isinstance( artist_id, type(None))), "Could not find artist. Check spelling?" # Make Genius API request for the determined artist ID json_artist = self._make_api_request((artist_id, 'artist')) # Create the Artist object artist = Artist(json_artist) if max_songs is None or max_songs > 0: # Access the api_path found by searching artist_search_results = self._make_api_request( (artist_id, 'artist-songs')) # Download each song by artist, store as Song objects in Artist object keep_searching = True next_page = 0 n = 0 while keep_searching: for json_song in artist_search_results['songs']: # TODO: Shouldn't I use self.search_song() here? # Songs must have a title if 'title' not in json_song: json_song['title'] = 'MISSING TITLE' # Remove non-song results (e.g. Linear Notes, Tracklists, etc.) song_is_valid = self._result_is_lyrics( json_song['title']) if remove_non_songs else True if song_is_valid: # Scrape song lyrics from the song's HTML lyrics = self._scrape_song_lyrics_from_url( json_song['url'], remove_section_headers) # Create song object for current song if get_full_song_info: song = Song( self._make_api_request( (json_song['id'], 'song')), lyrics) else: song = Song({'song': json_song}, lyrics) # Faster, less info from API # Add song to the Artist object if artist.add_song(song, verbose=False) == 0: # print("Add song: {}".format(song.title)) n += 1 if verbose: print('Song {0}: "{1}"'.format(n, song.title)) else: # Song does not contain lyrics if verbose: print( '"{title}" does not contain lyrics. Rejecting.' .format(title=json_song['title'])) # Check if user specified a max number of songs for the artist if not isinstance(max_songs, type(None)): if artist.num_songs >= max_songs: keep_searching = False if verbose: print( '\nReached user-specified song limit ({0}).' .format(max_songs)) break # Move on to next page of search results next_page = artist_search_results['next_page'] if next_page == None: break else: # Get next page of artist song results artist_search_results = self._make_api_request( (artist_id, 'artist-songs'), page=next_page) if verbose: print( 'Found {n_songs} songs.'.format(n_songs=artist.num_songs)) if verbose: print('Done.') return artist
def search_song(self, song_title, artist_name="", take_first_result=False, verbose=True, remove_section_headers=True, remove_non_songs=True): # TODO: Should search_song() be a @classmethod? """Search Genius.com for *song_title* by *artist_name*""" # Perform a Genius API search for the song if verbose: if artist_name != "": print('Searching for "{0}" by {1}...'.format( song_title, artist_name)) else: print('Searching for "{0}"...'.format(song_title)) search_term = "{} {}".format(song_title, artist_name) json_search = self._make_api_request((search_term, 'search')) # Loop through search results, stopping as soon as title and artist of # result match request n_hits = min(10, len(json_search['hits'])) for i in range(n_hits): search_hit = json_search['hits'][i]['result'] found_song = self._clean_str(search_hit['title']) found_artist = self._clean_str( search_hit['primary_artist']['name']) # Download song from Genius.com if title and artist match the request if take_first_result or found_song == self._clean_str( song_title) and found_artist == self._clean_str( artist_name) or artist_name == "": # Remove non-song results (e.g. Linear Notes, Tracklists, etc.) song_is_valid = self._result_is_lyrics( found_song) if remove_non_songs else True if song_is_valid: # Found correct song, accessing API ID json_song = self._make_api_request( (search_hit['id'], 'song')) # Scrape the song's HTML for lyrics lyrics = self._scrape_song_lyrics_from_url( json_song['song']['url'], remove_section_headers) # Create the Song object song = Song(json_song, lyrics) if verbose: print('Done.') return song else: if verbose: print( 'Specified song does not contain lyrics. Rejecting.' ) return None if verbose: print('Specified song was not first result :(') return None
def search_artist(self, artist_name, max_songs=None, sort='popularity', per_page=20, get_full_info=True, allow_name_change=True, artist_id=None, include_features=False): """Searches Genius.com for songs by the specified artist. This method looks for the artist by the name or by the ID if it's provided. It returrns an :class:`Artist <lyricsgenius.artist.Artist>` object if the search is successful. If :obj:`allow_name_change` is True, the name of the artist is changed to the artist name on Genius. Args: artist_name (:obj:`str`): Name of the artist to search for. max_songs (obj:`int`, optional): Maximum number of songs to search for. sort (:obj:`str`, optional): Sort by 'title' or 'popularity'. per_page (:obj:`int`, optional): Number of results to return per search page. It can't be more than 50. get_full_info (:obj:`bool`, optional): Get full info for each song (slower). allow_name_change (:obj:`bool`, optional): If True, search attempts to switch to intended artist name. artist_id (:obj:`int`, optional): Allows user to pass an artist ID. include_features (:obj:`bool`, optional): If True, includes tracks featuring the artist. Returns: :class:`Artist <lyricsgenius.artist.Artist>`: Artist object containing artist's songs. Examples: .. code:: python # printing the lyrics of all of the artist's songs genius = Genius(token) artist = genius.search_artist('Andy Shauf') for song in artist.songs: print(song.lyrics) Visit :class:`Aritst <lyricsgenius.artist.Artist>` for more examples. """ def find_artist_id(search_term): """Finds the ID of the artist, returns the first result if none match the search term or returns None if there were not results """ if self.verbose: print('Searching for songs by {0}...\n'.format(search_term)) # Perform a Genius API search for the artist found_artist = None response = self.search_genius_web(search_term) found_artist = self._get_item_from_search_response( response, search_term, type_="artist", result_type="name") # Exit the search if we couldn't find an artist by the given name if not found_artist: if self.verbose: print("No results found for '{a}'.".format(a=search_term)) return None # Assume the top search result is the intended artist return found_artist['id'] # Get the artist ID (or use the one supplied) artist_id = artist_id if artist_id else find_artist_id(artist_name) if not artist_id: return None artist_info = self.get_artist(artist_id) found_name = artist_info['artist']['name'] if found_name != artist_name and allow_name_change: if self.verbose: print("Changing artist name to '{a}'".format(a=found_name)) artist_name = found_name # Create the Artist object artist = Artist(artist_info) # Download each song by artist, stored as Song objects in Artist object page = 1 reached_max_songs = True if max_songs == 0 else False while not reached_max_songs: songs_on_page = self.get_artist_songs(artist_id, sort, per_page, page) # Loop through each song on page of search results for song_info in songs_on_page['songs']: # Check if song is valid (e.g. has title, contains lyrics) has_title = ('title' in song_info) has_lyrics = self._result_is_lyrics(song_info['title']) valid = has_title and (has_lyrics or (not self.skip_non_songs)) # Reject non-song results (e.g. Linear Notes, Tracklists, etc.) if not valid: if self.verbose: s = song_info['title'] if has_title else "MISSING TITLE" print('"{s}" is not valid. Skipping.'.format(s=s)) continue # Create the Song object from lyrics and metadata lyrics = self._scrape_song_lyrics_from_url(song_info['url']) if get_full_info: info = self.get_song(song_info['id']) else: info = {'song': song_info} song = Song(info, lyrics) # Attempt to add the Song to the Artist result = artist.add_song(song, verbose=False, include_features=include_features) if result == 0 and self.verbose: print('Song {n}: "{t}"'.format(n=artist.num_songs, t=song.title)) # Exit search if the max number of songs has been met reached_max_songs = max_songs and artist.num_songs >= max_songs if reached_max_songs: if self.verbose: print(('\nReached user-specified song limit ({m}).'. format(m=max_songs))) break # Move on to next page of search results page = songs_on_page['next_page'] if page is None: break # Exit search when last page is reached if self.verbose: print('Done. Found {n} songs.'.format(n=artist.num_songs)) return artist
def search_song(self, title, artist="", get_full_info=True): """Searches Genius.com for the lyrics to a specific song. Args: title (:obj:`str`): Song title to search for. artist (:obj:`str`, optional): Name of the artist. get_full_info (:obj:`bool`, optional): Get full info for each song (slower). Returns: :class:`Song <lyricsgenius.song.Song>` \\| :obj:`None`: On success, the song object is returned, otherwise `None`. Tip: Set :attr:`Genius.verbose` to `True` to read why the search fails. Examples: .. code:: python genius = Genius(token) artist = genius.search_artist('Andy Shauf', max_songs=0) song = genius.search_song('Toy You', artist.name) # same as: song = genius.search_song('To You', 'Andy Shauf') print(song['lyrics']) """ if self.verbose: if artist: print('Searching for "{s}" by {a}...'.format(s=title, a=artist)) else: print('Searching for "{s}"...'.format(s=title)) search_term = "{s} {a}".format(s=title, a=artist).strip() response = self.search_genius_web(search_term) # Otherwise, move forward with processing the search results result = self._get_item_from_search_response(response, title, type_="song", result_type="title") # Exit search if there were no results returned from API if not result: if self.verbose: print("No results found for: '{s}'".format(s=search_term)) return None # Reject non-songs (Liner notes, track lists, etc.) valid = self._result_is_lyrics( result['title']) if self.skip_non_songs else True if not valid: if self.verbose: print('Specified song does not contain lyrics. Rejecting.') return None # Download full song info (an API call) unless told not to by user song_info = result.copy() if get_full_info: song_info.update(self.get_song(result['id'])['song']) lyrics = self._scrape_song_lyrics_from_url(song_info['url']) # Skip results when URL is a 404 or lyrics are missing if not lyrics: if self.verbose: print(('Specified song does not have a valid URL with lyrics.' 'Rejecting.')) return None # Return a Song object with lyrics if we've made it this far song = Song(song_info, lyrics) if self.verbose: print('Done.') return song
def search_artist(self, artist_name, max_songs=None, sort='popularity', per_page=20, get_full_info=True, allow_name_change=True, artist_id=None): """Search Genius.com for songs by the specified artist. Returns an Artist object containing artist's songs. :param artist_name: Name of the artist to search for :param max_songs: Maximum number of songs to search for :param sort: Sort by 'title' or 'popularity' :param per_page: Number of results to return per search page :param get_full_info: Get full info for each song (slower) :param allow_name_change: (bool) If True, search attempts to switch to intended artist name. :param artist_id: Allows user to pass a Genius.com artist ID. """ def find_artist_id(search_term): if self.verbose: print('Searching for songs by {0}...\n'.format(search_term)) # Perform a Genius API search for the artist found_artist = None response = self.search_genius_web(search_term) found_artist = self._get_item_from_search_response( response, search_term, type_="artist", result_type="name") # Exit the search if we couldn't find an artist by the given name if not found_artist: if self.verbose: print("No results found for '{a}'.".format(a=search_term)) return None # Assume the top search result is the intended artist return found_artist['id'] # Get the artist ID (or use the one supplied) artist_id = artist_id if artist_id else find_artist_id(artist_name) if artist_id == None: return None artist_info = self.get_artist(artist_id) found_name = artist_info['artist']['name'] if found_name != artist_name and allow_name_change: if self.verbose: print("Changing artist name to '{a}'".format(a=found_name)) artist_name = found_name # Create the Artist object artist = Artist(artist_info) # Download each song by artist, stored as Song objects in Artist object page = 1 reached_max_songs = False while not reached_max_songs: songs_on_page = self.get_artist_songs(artist_id, sort, per_page, page) # Loop through each song on page of search results for song_info in songs_on_page['songs']: # Check if song is valid (e.g. has title, contains lyrics) has_title = ('title' in song_info) has_lyrics = self._result_is_lyrics(song_info['title']) valid = has_title and (has_lyrics or (not self.skip_non_songs)) # Reject non-song results (e.g. Linear Notes, Tracklists, etc.) if not valid: if self.verbose: s = song_info['title'] if has_title else "MISSING TITLE" print('"{s}" is not valid. Skipping.'.format(s=s)) continue # Create the Song object from lyrics and metadata lyrics = self._scrape_song_lyrics_from_url(song_info['url']) if get_full_info: info = self.get_song(song_info['id']) else: info = {'song': song_info} song = Song(info, lyrics) # Attempt to add the Song to the Artist result = artist.add_song(song, verbose=False) if result == 0 and self.verbose: print('Song {n}: "{t}"'.format(n=artist.num_songs, t=song.title)) # Exit search if the max number of songs has been met reached_max_songs = max_songs and artist.num_songs >= max_songs if reached_max_songs: if self.verbose: print('\nReached user-specified song limit ({m}).'. format(m=max_songs)) break # Move on to next page of search results page = songs_on_page['next_page'] if page is None: break # Exit search when last page is reached if self.verbose: print('Done. Found {n} songs.'.format(n=artist.num_songs)) return artist
def _search_song_old(self, title, artist="", get_full_info=True, take_first_result=False): """ Search Genius.com for lyrics to a specific song :param title: Song title to search for :param artist: Name of the artist :param get_full_info: Get full info for each song (slower) :param take_first_result: Force search to choose first result """ # Search the Genius API for the specified song if self.verbose: if artist: print('Searching for "{s}" by {a}...'.format(s=title, a=artist)) else: print('Searching for "{s}"...'.format(s=title)) search_term = "{s} {a}".format(s=title, a=artist).strip() response = self.search_genius(search_term) # Exit search if there were no results returned from API if (response is None) or (len(response['hits']) == 0): if self.verbose: print("No results found for: '{s}'".format(s=search_term)) return None # Always take first search result if user didn't specify an artist take_first_result = True if artist == "" else take_first_result # Loop through the API search results, looking for desired song results = [ r['result'] for r in response['hits'] if r['type'] == 'song' ] for result in results: # Skip to next search result if current result is not a match if not (take_first_result or self._result_is_match(result, title, artist)): continue # Reject non-songs (Liner notes, track lists, etc.) if self.skip_non_songs: song_is_valid = self._result_is_lyrics(result['title']) else: song_is_valid = True if not song_is_valid: if self.verbose: print('Specified song does not contain lyrics. Rejecting.') return None # Download full song info (an API call) unless told not to by user if get_full_info: song_info = self.get_song(result['id'])['song'] else: song_info = result lyrics = self._scrape_song_lyrics_from_url(song_info['url']) # Skip results when URL is a 404 or lyrics are missing if not lyrics: if self.verbose: print( 'Specified song does not have a valid URL with lyrics. Rejecting.' ) return None # Return a Song object with lyrics if we've made it this far song = Song(song_info, lyrics) if self.verbose: print('Done.') return song if self.verbose: print("Could not find specified song. Check spelling?") return None
def lg_request(artist, title, verbose=False): global genius if genius is None: return None # print(title) # breakpoint() try: response = genius.search_songs(f"{artist} {title}") except TypeError: # token wasn't passed in or found in env print("Genius token invalid or not found") genius = None return None results = response["hits"] # Exit search if there were no results returned from API if not results: if verbose: print(f"No results found for {title}") return None # Reject non-songs (Liner notes, track lists, etc.) result = next( (r["result"] for r in results if genius._result_is_lyrics(r["result"]["title"])), None, ) if not result: if verbose: print(f"Found no lyrics for {title}.") return None lyrics = genius.lyrics(result["url"]) # Skip results when URL is a 404 or lyrics are missing if not lyrics: if verbose: print(f"{title} does not have a valid URL with lyrics. Rejecting.") return None result = Song(result, lyrics) if not (result and result.lyrics): if verbose: print(f'No Genius lyrics found for "{title}"') return None r_artist = _clean_str(result.artist) c_artist = _clean_str(artist) if not (c_artist in r_artist or r_artist in c_artist): if verbose: print( f"Incorrect artist from Genius: {artist} became {result.artist}" ) return None r_title = _clean_str(result.title) c_title = _clean_str(title) if not (c_title in r_title or r_title in c_title): if verbose: print( f"Incorrect title from Genius: {title} became {result.title}") return None if verbose: print(f'Result found in Genius for "{title}"') return result.lyrics
async def clyrics(self, ctx: commands.Context, query=None): """Get the lyrics not ANY(susceptible to terms and conditions) song.""" state = TempState(ctx.author.guild) if not query and state.queue == []: await ctx.send("no song sad lyf") elif not query: query = state.queue[0].title title = query response = genius.search_genius_web(title) hits = response['sections'][0]['hits'] sections = sorted(response['sections'], key=lambda sect: sect['type'] == "song", reverse=True) hits = [ hit for section in sections for hit in section['hits'] if hit['type'] == "song" ][0:5] if hits == []: await ctx.send("Can't Find lyrics. Use different name of the song." ) return async def reactions_add(message, reactions): for reaction in reactions: await message.add_reaction(reaction) wait_time = 60 reactions = {"1️⃣": 1, "2️⃣": 2, "3️⃣": 3, "4️⃣": 4, "5️⃣": 5} embed = discord.Embed(title="Search returned the following", color=discord.Colour.dark_green()) embed.set_author(name="Me!Me!Me!", icon_url=self.client.user.avatar_url) embed.set_footer( text=f"Requested By: {ctx.message.author.display_name}", icon_url=ctx.message.author.avatar_url) embed.set_thumbnail(url=self.music_logo) embed_msg = await ctx.send(embed=embed) for index, result in enumerate(hits): result = result["result"] embed.add_field( name=f"*{index + 1}.*", value= f"**{result['title_with_featured']} - {result['primary_artist']['name']}**", inline=False) await embed_msg.edit(content="", embed=embed) self.client.loop.create_task( reactions_add(embed_msg, list(reactions.keys())[:len(hits)])) while True: try: reaction, user = await self.client.wait_for( 'reaction_add', timeout=wait_time, check=lambda reaction, user: user == ctx.author and reaction.message.id == embed_msg.id) except TimeoutError: await ctx.send( f">>> I guess no ones wants to see some sweet lyrics.") await embed_msg.delete() return None else: await embed_msg.remove_reaction(str(reaction.emoji), ctx.author) if str(reaction.emoji) in reactions.keys(): await embed_msg.delete(delay=3) hit = hits[reactions[str(reaction.emoji)] - 1] song_info = hit["result"] lyrics = genius._scrape_song_lyrics_from_url( song_info['url']) song = Song(song_info, lyrics) embed = discord.Embed( title= f"LYRICS - {song.title} - {song.artist}", # TODO make a function url=song.url, description="", color=discord.Colour.blurple()) embed.set_author(name="Me!Me!Me!", icon_url=self.client.user.avatar_url) embed.set_footer( text=f"Requested By: {ctx.message.author.display_name}", icon_url=ctx.message.author.avatar_url) embed_msg = await ctx.send(embed=embed) await self.client.get_cog("Queue").embed_pages( ctx=ctx, _content=lyrics, embed_msg=embed_msg, wait_time=120)