Fetching Lyrics
This section explains how to use Salada’s built-in lyrics feature to get synced or plain lyrics for any track, enhanced with metadata from Spotify.Prerequisites
To use the lyrics feature, you must provide Spotify API credentials in your node configuration. This is required to fetch song metadata like album art, which enriches the lyrics display.nodes = [{
'host': 'your_lavalink_host',
'port': 2333,
'auth': 'your_lavalink_password',
'ssl': False,
'spotify_client_id': 'YOUR_SPOTIFY_CLIENT_ID',
'spotify_client_secret': 'YOUR_SPOTIFY_CLIENT_SECRET'
}]
# Make sure to install the required library
# pip install syncedlyrics
Complete Lyrics Command Example
Here is a complete example of a/lyrics command for a Discord bot. It fetches lyrics for the currently playing song and displays them in a paginated embed.
from discord import app_commands
import discord
import re
# You'll need a way to handle pagination. This is a simplified example.
# For a real implementation, consider a pagination file from discord.py or any other library you are using!
class LyricsView(discord.ui.View):
def __init__(self, embeds):
super().__init__(timeout=120)
self.embeds = embeds
self.current_page = 0
@discord.ui.button(label="◀", style=discord.ButtonStyle.primary)
async def previous_button(self, interaction: discord.Interaction, button: discord.ui.Button):
self.current_page = (self.current_page - 1) % len(self.embeds)
await interaction.response.edit_message(embed=self.embeds[self.current_page])
@discord.ui.button(label="▶", style=discord.ButtonStyle.primary)
async def next_button(self, interaction: discord.Interaction, button: discord.ui.Button):
self.current_page = (self.current_page + 1) % len(self.embeds)
await interaction.response.edit_message(embed=self.embeds[self.current_page])
def format_lyrics_for_embed(lyrics_text: str, is_synced: bool):
"""Splits lyrics into chunks for Discord embeds."""
if not lyrics_text:
return ["No lyrics available."]
chunks = []
current_chunk = ""
lines = lyrics_text.split('\\n')
for line in lines:
# For synced lyrics remove the timestamp
if is_synced and line.strip().startswith('['):
if ']' in line:
line = line[line.index(']') + 1:].strip()
if not current_chunk and not line.strip():
continue
if len(current_chunk) + len(line) + 1 > 1024: # Embed field value limit
chunks.append(current_chunk.strip())
current_chunk = line + '\\n'
else:
current_chunk += line + '\\n'
if current_chunk.strip():
chunks.append(current_chunk.strip())
return chunks
@bot.tree.command(name='lyrics')
@app_commands.describe(song='Song name (optional, defaults to current track)')
async def lyrics_command(interaction: discord.Interaction, song: str = None):
await interaction.response.defer()
player = bot.salad.getPlayer(interaction.guild.id)
track = None
artist_name = None
if song:
# If a song is provided, we need to search for it first
result = await bot.salad.resolve(song, requester=interaction.user)
if result and result.get('tracks'):
track = result['tracks'][0]
elif player and player.currentTrackObj:
track = player.currentTrackObj
else:
await interaction.followup.send('❌ No song is playing and no song was specified.')
return
# Clean up artist name for better search results
artist_name = getattr(track, 'author', None)
if artist_name:
artist_name = re.sub(r'\\s*-\\s*Topic', '', artist_name)
artist_name = re.sub(r'\\s*VEVO', '', artist_name, flags=re.IGNORECASE)
# Fetch lyrics
lyrics_text, is_synced = await track.fetch_lyrics(player)
if not lyrics_text:
await interaction.followup.send(f"❌ No lyrics found for **{track.title}**.")
return
# Get song info from Spotify
lyrics_handler = player.get_lyrics_handler()
song_info = {}
if lyrics_handler:
song_info = await lyrics_handler.get_song_info(track.title, artist_name)
# Create paginated embeds
chunks = format_lyrics_for_embed(lyrics_text, is_synced)
embeds = []
song_display = f"**[{track.title}]({song_info.get('song_url')})**" if song_info.get('song_url') else f"**{track.title}**"
for i, chunk in enumerate(chunks):
embed = discord.Embed(
title=f"Lyrics for {track.title}",
description=f"{song_display} by **{track.author}**\n\n{chunk}",
color=discord.Color.blue()
)
if song_info.get("thumbnail_url"):
embed.set_thumbnail(url=song_info["thumbnail_url"])
embed.set_footer(text=f"Page {i + 1}/{len(chunks)} | {'Synced' if is_synced else 'Plain Text'}")
embeds.append(embed)
if len(embeds) == 1:
await interaction.followup.send(embed=embeds[0])
else:
view = LyricsView(embeds)
await interaction.followup.send(embed=embeds[0], view=view)
Core Concepts
Fetching Lyrics from a Track
The primary method for getting lyrics istrack.fetch_lyrics(player). It automatically handles fetching and caching.
player = bot.salad.getPlayer(guild_id)
current_track = player.currentTrackObj
if current_track:
# Returns a tuple: (lyrics_string, is_synced_bool)
lyrics, is_synced = await current_track.fetch_lyrics(player)
if lyrics:
print("Lyrics found!")
else:
print("Lyrics not found.")
Accessing the Lyrics Handler
If you need to access the Spotify metadata functions directly, you can get the lyrics handler from the player.player = bot.salad.getPlayer(guild_id)
lyrics_handler = player.get_lyrics_handler()
if lyrics_handler:
# Fetch Spotify info for a song
song_info = await lyrics_handler.get_song_info("Bohemian Rhapsody", "Queen")
if song_info and song_info.get("thumbnail_url"):
print("Album Art URL:", song_info["thumbnail_url"])
The
syncedlyrics library performs web scraping and can sometimes be slow or fail. Always wrap your lyrics-fetching calls in try...except blocks and handle cases where no lyrics are found gracefully.Since lyrics can be very long, always paginate them for a better user experience in Discord. Splitting the text into multiple embeds or using Discord’s Paginator is highly recommended.