Star ✨ on GitHub

Lyrics

API reference for the Lyrics class in Moonlink.js

Lyrics Class

The Lyrics class provides functionality to fetch and display lyrics for currently playing tracks in Moonlink.js.

player.lyrics.getLyrics()

Overview

Properties

Core properties of the Lyrics class.

Available Properties

PropertyTypeDescription
playerPlayerReference to the player instance that owns this lyrics handler

Methods

Methods

Methods available in the Lyrics class.

Available Methods

getLyrics

getLyricsmethod

Get Lyrics

Fetches lyrics for the currently playing track.

Parameters

This method takes no parameters.

Returns & Example

ReturnsPromise<IRESTGetLyrics | null> - Lyrics data or null if no track is playing or NodeLink is not available

// Basic usage
const lyrics = await player.lyrics.getLyrics();

if (lyrics && lyrics.loadType === 'track') {
console.log(`Song: ${lyrics.data.name}`);
console.log(`Synchronized: ${lyrics.data.synced}`);

for (const line of lyrics.data.data) {
  console.log(`[${formatTime(line.startTime)}] ${line.text}`);
}
}

::

Response Structure

Lyrics Response Structure

The structure of the lyrics response object.

interface IRESTGetLyrics {
  loadType: string;        // 'track' if lyrics found
  data: {
    name: string;         // Song name
    synced: boolean;      // Whether lyrics are time-synchronized
    data: Array<{
      startTime: number;  // Start time in milliseconds
      endTime: number;    // End time in milliseconds
      text: string;      // Lyrics line text
    }>;
    rtl: boolean;        // Right-to-left text direction
  };
}

Usage Examples

Advanced Usage Examples

Comprehensive examples of implementing lyrics functionality.

Discord Bot with Lyrics Display

async function displayLyrics(message) {
  const player = manager.players.get(message.guild.id);
  
  if (!player?.playing) {
    return message.channel.send('⚠️ No track is currently playing!');
  }
  
  const loadingMsg = await message.channel.send('🔍 Fetching lyrics...');
  
  try {
    const lyrics = await player.lyrics.getLyrics();
    
    if (!lyrics || lyrics.loadType !== 'track') {
      return loadingMsg.edit('❌ No lyrics found for this track.');
    }
    
    const { name, synced, data } = lyrics.data;
    const currentTrack = player.current;
    
    // Create embeds for paginated lyrics display
    const embeds = [];
    let currentPage = '';
    let lineCount = 0;
    
    for (const line of data) {
      const timestamp = synced ? `\[${formatTimestamp(line.startTime)}\]` : '';
      const lineText = `${timestamp}${line.text}\n`;
      
      if ((currentPage + lineText).length > 4000 || lineCount >= 15) {
        embeds.push({
          title: `📝 Lyrics for ${currentTrack.title}`,
          description: currentPage,
          color: 0x3498db,
          footer: {
            text: `${synced ? 'Synchronized' : 'Unsynchronized'} lyrics | Page ${embeds.length + 1}`
          }
        });
        currentPage = lineText;
        lineCount = 1;
      } else {
        currentPage += lineText;
        lineCount++;
      }
    }
    
    // Add the last page if there's remaining content
    if (currentPage.length > 0) {
      embeds.push({
        title: `📝 Lyrics for ${currentTrack.title}`,
        description: currentPage,
        color: 0x3498db,
        footer: {
          text: `${synced ? 'Synchronized' : 'Unsynchronized'} lyrics | Page ${embeds.length + 1}`
        }
      });
    }
    
    // Setup pagination buttons
    const row = new Discord.ActionRowBuilder()
      .addComponents(
        new Discord.ButtonBuilder()
          .setCustomId('prev')
          .setLabel('Previous')
          .setStyle('Primary')
          .setEmoji('⬅️'),
        new Discord.ButtonBuilder()
          .setCustomId('next')
          .setLabel('Next')
          .setStyle('Primary')
          .setEmoji('➡️')
      );
    
    let currentIndex = 0;
    const initialMessage = await loadingMsg.edit({
      content: null,
      embeds: [embeds[0]],
      components: [row]
    });
    
    // Handle pagination
    const collector = initialMessage.createMessageComponentCollector({
      time: 300000 // 5 minutes
    });
    
    collector.on('collect', async (interaction) => {
      if (interaction.customId === 'prev') {
        currentIndex = Math.max(0, currentIndex - 1);
      } else if (interaction.customId === 'next') {
        currentIndex = Math.min(embeds.length - 1, currentIndex + 1);
      }
      
      await interaction.update({
        embeds: [embeds[currentIndex]],
        components: [row]
      });
    });
    
    collector.on('end', () => {
      initialMessage.edit({ components: [] });
    });
  } catch (error) {
    console.error('Error fetching lyrics:', error);
    loadingMsg.edit('❌ An error occurred while fetching lyrics.');
  }
}

// Helper function to format timestamps
function formatTimestamp(ms) {
  const minutes = Math.floor(ms / 60000);
  const seconds = ((ms % 60000) / 1000).toFixed(0);
  return `${minutes}:${seconds.padStart(2, '0')}`;
}

// Command handler
client.on('messageCreate', async (message) => {
  if (message.content === '!lyrics') {
    await displayLyrics(message);
  }
});

Real-time Lyrics Display

class LyricsDisplay {
  constructor(player) {
    this.player = player;
    this.lyrics = null;
    this.currentLineIndex = 0;
    this.displayInterval = null;
  }
  
  async start() {
    try {
      this.lyrics = await this.player.lyrics.getLyrics();
      
      if (!this.lyrics?.data?.synced) {
        console.log('❌ No synchronized lyrics available');
        return;
      }
      
      this.displayInterval = setInterval(() => {
        this.updateDisplay();
      }, 100);
      
      // Stop display when track ends
      this.player.once('trackEnd', () => this.stop());
    } catch (error) {
      console.error('Error starting lyrics display:', error);
    }
  }
  
  updateDisplay() {
    if (!this.lyrics || !this.player.playing) return;
    
    const currentTime = this.player.position;
    const lines = this.lyrics.data.data;
    
    // Find current line based on timestamp
    const newLineIndex = lines.findIndex((line, index) => {
      return currentTime >= line.startTime && 
             (index === lines.length - 1 || currentTime < lines[index + 1].startTime);
    });
    
    if (newLineIndex !== -1 && newLineIndex !== this.currentLineIndex) {
      this.currentLineIndex = newLineIndex;
      
      // Display current line and next line
      console.clear();
      console.log('🎵 Now Playing:', this.player.current.title);
      console.log('\nCurrent Line:');
      console.log(lines[this.currentLineIndex].text);
      
      if (lines[this.currentLineIndex + 1]) {
        console.log('\nNext Line:');
        console.log(lines[this.currentLineIndex + 1].text);
      }
    }
  }
  
  stop() {
    if (this.displayInterval) {
      clearInterval(this.displayInterval);
      this.displayInterval = null;
    }
    this.lyrics = null;
    this.currentLineIndex = 0;
  }
}

// Usage
player.on('trackStart', async () => {
  const lyricsDisplay = new LyricsDisplay(player);
  await lyricsDisplay.start();
});

Lyrics