Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@persian-caesar/discord-player

Persian-Caesar901MIT1.2.1TypeScript support: included

Type definitions and helpers for a Discord music player (MusicPlayer) without external dependencies

discord, radio, playing music, media player, music, typescript, voice, player, discord-player, Persian-Caesar, mr-sinre, Sobhan-SRZA, discord-player, soundcloud, spotify, youtube, @discordjs/opus, @discordjs/voice, @distube/ytdl-core, discordjs, distube, play-dl, libsodium-wrappers, ytdl-core, ytdl-core-discord, ytdl

readme

@persian-caesar/discord-player

A lightweight, type-safe music player for Discord bots, built with TypeScript and integrated with discord.js. This package provides a robust MusicPlayer class for streaming audio from platforms like YouTube, SoundCloud, Spotify, and Deezer, with optional Lavalink support via Erela.js for enhanced performance and playlist handling. Without Lavalink, it falls back to direct streaming using libraries like play-dl, soundcloud-downloader, and yt-dlp-exec.

Table of Contents

Introduction

@persian-caesar/discord-player is designed to simplify audio playback in Discord bots. It leverages the @discordjs/voice library for voice channel interactions and supports streaming from multiple platforms. Lavalink integration (via Erela.js) is optional for better scalability, playlist support, and performance. Without Lavalink, the player uses direct streaming for flexibility in smaller setups. The package is fully typed, making it ideal for TypeScript projects, and includes JSDoc annotations for JavaScript users. The MusicPlayer class handles all aspects of music playback, including multi-platform search, queue management, history tracking, and event-driven notifications.

Developed by Sobhan-SRZA for Persian Caesar, this package is licensed under MIT and actively maintained.

Features

  • Optional Lavalink Support: Use Erela.js for advanced features like playlist loading and better audio handling, or fallback to direct streaming without it.
  • Multi-Platform Search and Streaming: Supports YouTube, SoundCloud, Spotify, and Deezer. Search prioritizes platforms in order (configurable), returns a list of results.
  • Direct Stream Handling: Streams non-platform URLs (e.g., radio stations) directly without searching.
  • Playlist Support: Detect, search, and load playlists from all supported platforms, automatically enqueues tracks.
  • Queue Management: Add tracks (single or multiple), shuffle, or revert to the original order.
  • Looping Options: Toggle looping for a single track or the entire queue.
  • Volume Control: Adjust playback volume (0–200%).
  • Lyrics Retrieval: Fetch song lyrics from Google search results using html-to-text.
  • Radio Mode: Play a shuffled list of URLs in a continuous loop.
  • Event System: Strongly-typed events for playback status, queue changes, errors, and more.
  • Auto-Disconnect: Configurable options to leave voice channels when the queue is empty or after idle time.
  • Type Safety: Full TypeScript support with defined interfaces and enums in types.ts.
  • Lightweight: Minimal dependencies with no external framework requirements beyond discord.js.

Installation

Install the package:

npm install @persian-caesar/discord-player

Ensure you have Node.js version 16 or higher, as specified in package.json.

Dependencies

The following dependencies are required for the package to function correctly:

Package Version Purpose
@discordjs/voice ^0.18.0 Handles voice channel connections and audio playback in Discord.
@discordjs/opus ^0.10.0 Provides Opus audio encoding/decoding for high-quality audio streaming.
erela.js ^2.4.0 Optional: Access to Lavalink for enhanced audio and playlist support.
play-dl ^1.9.7 Streams audio from Spotify, YouTube, and Deezer with search capabilities (fallback mode).
soundcloud-downloader ^1.0.0 Downloads and streams audio from SoundCloud URLs (fallback mode).
html-to-text ^9.0.5 Converts HTML (from Google lyrics searches) to plain text.
libsodium-wrappers ^0.7.15 Required for secure audio encryption in @discordjs/voice.
ffmpeg-static (peer) Provides FFmpeg for audio processing and stream conversion.

Why these dependencies?

  • @discordjs/voice and @discordjs/opus are core to Discord voice functionality, enabling the bot to join channels and stream audio.
  • erela.js enables optional Lavalink integration for better scalability.
  • play-dl and soundcloud-downloader provide fallback streaming without Lavalink.
  • html-to-text is used for scraping and cleaning lyrics from Google search results.
  • libsodium-wrappers and ffmpeg-static are required for secure and efficient audio processing.

Usage Examples

Below are examples demonstrating how to integrate @persian-caesar/discord-player with discord.js in both TypeScript and JavaScript. These examples assume you have a Discord bot set up with discord.js.

This example uses Lavalink for playlist support and better performance.

import { Client, GatewayIntentBits, TextChannel, VoiceChannel } from 'discord.js';
import { MusicPlayer, MusicPlayerEvent, LavalinkManager } from '@persian-caesar/discord-player';

// Initialize Discord client with necessary intents
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildVoiceStates,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent
  ]
});

// Initialize Erela.js Manager for Lavalink
const manager = new LavalinkManager(client, {
  nodes: [
    {
      host: "lava-v4.ajieblogs.eu.org",
      port: 443,
      password: "https://dsc.gg/ajidevserver",
      secure: true
    }
  ]
});

// Bot configuration
const PREFIX = '!';
const TOKEN = 'YOUR_BOT_TOKEN'; // Replace with your bot token

client.on('ready', () => {
  console.log(`Logged in as ${client.user?.tag}`);
});

client.on('messageCreate', async (message) => {
  if (!message.content.startsWith(PREFIX) || message.author.bot) return;

  const args = message.content.slice(PREFIX.length).trim().split(/ +/);
  const command = args.shift()?.toLowerCase();

  if (!message.guild || !message.member?.voice.channel) return;

  const voiceChannel = message.member.voice.channel as VoiceChannel;
  const player = new MusicPlayer(voiceChannel, message.channel as TextChannel, manager, {
    autoLeaveOnEmptyQueue: true,
    autoLeaveOnIdleMs: 300_000 // 5 minutes
  });

  // Event listeners for music player
  player.on(MusicPlayerEvent.Start, ({ metadata }) => {
    message.channel.send(`▶️ Now playing: ${metadata.title || metadata.url}`);
  });

  player.on(MusicPlayerEvent.QueueAdd, ({ metadata, metadatas, queue }) => {
    const added = metadatas ? metadatas.length + ' tracks' : metadata?.title || metadata?.url;
    message.channel.send(`➕ Added: ${added} (${queue.length} in queue)`);
  });

  player.on(MusicPlayerEvent.Error, (error) => {
    message.channel.send(`❌ Error: ${error.message}`);
  });

  player.on(MusicPlayerEvent.Finish, () => {
    message.channel.send('⏹️ Playback finished.');
  });

  // Command handling
  if (command === 'play') {
    const query = args.join(' ');
    if (!query) {
      message.channel.send('Please provide a URL or search query.');
      return;
    }

    await player.play(query);
  } 

  else if (command === 'search') {
    const query = args.join(' ');
    const results = await player.search(query, 'youtube');
    const resultList = results.map((r, i) => `${i + 1}. ${r.title || r.url}`).join('\n');
    message.channel.send(resultList || 'No results.');
  }

  else if (command === 'playlists') {
    const query = args.join(' ');
    const playlists = await player.searchPlaylists(query, 'spotify');
    const list = playlists.map((p, i) => `${i + 1}. ${p.title} (${p.trackCount} tracks): ${p.url}`).join('\n');
    message.channel.send(list || 'No playlists found.');
  } 

  else if (command === 'isplaylist') {
    const url = args[0];
    const isPlaylist = await player.isPlaylist(url);
    message.channel.send(isPlaylist ? 'Yes, this is a playlist.' : 'No, this is not a playlist.');
  } // Add other commands as needed
});

client.login(TOKEN);

This example uses fallback streaming without Lavalink.

import { Client, GatewayIntentBits, TextChannel, VoiceChannel } from 'discord.js';
import { MusicPlayer, MusicPlayerEvent } from '@persian-caesar/discord-player';

// Initialize Discord client with necessary intents
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildVoiceStates,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent
  ]
});

// Bot configuration
const PREFIX = '!';
const TOKEN = 'YOUR_BOT_TOKEN'; // Replace with your bot token

client.on('ready', () => {
  console.log(`Logged in as ${client.user?.tag}`);
});

client.on('messageCreate', async (message) => {
  if (!message.content.startsWith(PREFIX) || message.author.bot) return;

  const args = message.content.slice(PREFIX.length).trim().split(/ +/);
  const command = args.shift()?.toLowerCase();

  if (!message.guild || !message.member?.voice.channel) return;

  const voiceChannel = message.member.voice.channel as VoiceChannel;
  const player = new MusicPlayer(voiceChannel, message.channel as TextChannel, undefined, {
    autoLeaveOnEmptyQueue: true,
    autoLeaveOnIdleMs: 300_000, // 5 minutes
    youtubeCookie: 'YOUR_YOUTUBE_COOKIE', // Optional for age-restricted content
  });

  // Event listeners for music player
  player.on(MusicPlayerEvent.Start, ({ metadata }) => {
    message.channel.send(`▶️ Now playing: ${metadata.title || metadata.url}`);
  });

  player.on(MusicPlayerEvent.QueueAdd, ({ metadata, metadatas, queue }) => {
    const added = metadatas ? metadatas.length + ' tracks' : metadata?.title || metadata?.url;
    message.channel.send(`➕ Added: ${added} (${queue.length} in queue)`);
  });

  player.on(MusicPlayerEvent.Error, (error) => {
    message.channel.send(`❌ Error: ${error.message}`);
  });

  player.on(MusicPlayerEvent.Finish, () => {
    message.channel.send('⏹️ Playback finished.');
  });

  // Command handling
  if (command === 'play') {
    const query = args.join(' ');
    if (!query) {
      message.channel.send('Please provide a URL or search query.');
      return;
    }
    await player.play(query);
  } 

  else if (command === 'search') {
    const query = args.join(' ');
    const results = await player.search(query, 'spotify'); // Optional platform
    const resultList = results.map((r, i) => `${i + 1}. ${r.title || r.url}`).join('\n');
    message.channel.send(resultList || 'No results.');
  }

  else if (command === 'playlists') {
    const query = args.join(' ');
    const playlists = await player.searchPlaylists(query, 'deezer');
    const list = playlists.map((p, i) => `${i + 1}. ${p.title} (${p.trackCount} tracks): ${p.url}`).join('\n');
    message.channel.send(list || 'No playlists found.');
  }

  else if (command === 'isplaylist') {
    const url = args[0];
    const isPlaylist = await player.isPlaylist(url);
    message.channel.send(isPlaylist ? 'Yes, this is a playlist.' : 'No, this is not a playlist.');
  } // Add other commands as needed
});

client.login(TOKEN);

JavaScript Example

This example uses plain JavaScript with optional Lavalink.

const { Client, GatewayIntentBits } = require('discord.js');
const { MusicPlayer, MusicPlayerEvent, LavalinkManager } = require('@persian-caesar/discord-player');

// Initialize Discord client with necessary intents
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildVoiceStates,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent
  ]
});

// Optional Lavalink Manager
const manager = new LavalinkManager(client, {
  nodes: [
    {
      host: "lava-v4.ajieblogs.eu.org",
      port: 443,
      password: "https://dsc.gg/ajidevserver",
      secure: true
    }
  ]
});

// Bot configuration
const PREFIX = '!';
const TOKEN = 'YOUR_BOT_TOKEN'; // Replace with your bot token

client.on('ready', () => {
  console.log(`Logged in as ${client.user?.tag}`);
});

client.on('messageCreate', async (message) => {
  if (!message.content.startsWith(PREFIX) || message.author.bot) return;

  const args = message.content.slice(PREFIX.length).trim().split(/ +/);
  const command = args.shift()?.toLowerCase();

  if (!message.guild || !message.member?.voice.channel) return;

  /** @type {import('@persian-caesar/discord-player').VoiceChannel} */
  const voiceChannel = message.member.voice.channel;
  const player = new MusicPlayer(voiceChannel, message.channel, manager, { // Pass manager if using Lavalink
    autoLeaveOnEmptyQueue: true,
    autoLeaveOnIdleMs: 300_000 // 5 minutes
  });

  // Event listeners for music player
  player.on(MusicPlayerEvent.Start, ({ metadata }) => {
    message.channel.send(`▶️ Now playing: ${metadata.title || metadata.url}`);
  });

  player.on(MusicPlayerEvent.QueueAdd, ({ metadata, metadatas, queue }) => {
    const added = metadatas ? metadatas.length + ' tracks' : metadata?.title || metadata?.url;
    message.channel.send(`➕ Added: ${added} (${queue.length} in queue)`);
  });

  player.on(MusicPlayerEvent.Error, (error) => {
    message.channel.send(`❌ Error: ${error.message}`);
  });

  player.on(MusicPlayerEvent.Finish, () => {
    message.channel.send('⏹️ Playback finished.');
  });

  // Command handling
  if (command === 'play') {
    const query = args.join(' ');
    if (!query) {
      message.channel.send('Please provide a URL or search query.');
      return;
    }

    await player.play(query);
  } 

  else if (command === 'search') {
    const query = args.join(' ');
    const results = await player.search(query);
    const resultList = results.map((r, i) => `${i + 1}. ${r.title || r.url}`).join('\n');
    message.channel.send(resultList || 'No results.');
  }

  else if (command === 'playlists') {
    const query = args.join(' ');
    const playlists = await player.searchPlaylists(query);
    const list = playlists.map((p, i) => `${i + 1}. ${p.title} (${p.trackCount} tracks): ${p.url}`).join('\n');
    message.channel.send(list || 'No playlists found.');
  } 

  else if (command === 'isplaylist') {
    const url = args[0];
    const isPlaylist = await player.isPlaylist(url);
    message.channel.send(isPlaylist ? 'Yes, this is a playlist.' : 'No, this is not a playlist.');
  } // Add other commands as needed
});

client.login(TOKEN);

API Reference

MusicPlayer Class

Constructor:

new MusicPlayer(
  channel: VoiceChannel,
  textChannel: TextChannel,
  lavaLinkManager?: Manager, // Optional Erela.js Manager for Lavalink
  options?: MusicPlayerOptions // { initialVolume?: number, autoLeaveOnEmptyQueue?: boolean, autoLeaveOnIdleMs?: number, youtubeCookie?: string, logError?: boolean }
)

Methods: | Method | Description | | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | isPlaylist(url: string): Promise<boolean> | Checks if the given URL is a playlist. | | searchPlaylists(query: string, platform?: SearchPlatform, limit?: number): Promise<PlaylistMetadata[]> | Searches for playlists across platforms. | | search(query: string, platform?: SearchPlatform, limit?: number): Promise<TrackMetadata[]> | Searches for tracks across platforms. | | play(input: string | TrackMetadata | TrackMetadata[], radio?: boolean): Promise<void> | Plays input (string searches first result or loads playlist, metadata/array direct). Supports playlists. | | pause(): void | Pauses the current track. | | resume(): void | Resumes playback. | | setVolume(percent: number): number | Sets volume (0–200%), returns new volume. | | skip(): void | Skips to the next track in the queue. | | previous(): Promise<void> | Plays the previous track from history. | | shuffle(): void | Shuffles the queue, saving the original order. | | undoShuffle(): void | Restores the queue to its pre-shuffle order. | | toggleLoopQueue(): boolean | Toggles queue looping, returns new state. | | toggleLoopTrack(): boolean | Toggles single-track looping, returns new state. | | startRadio(urls: string[]): Promise<void> | Starts radio mode with shuffled URLs. | | stop(noLeave?: boolean): void | Stops playback, optionally disconnects. | | disconnect(): void | Disconnects from the voice channel. | | join(): VoiceConnection | Joins the voice channel without subscribing player. | | getQueue(): TrackMetadata[] | Returns a copy of the current queue. | | getVolume(): number | Returns the current volume (0–200%). | | isPlaying(): boolean | Checks if a track is playing. | | isPaused(): boolean | Checks if playback is paused. | | isShuffiled(): boolean | Checks if the queue is shuffled. | | isConnected(guildId?: string): boolean | Checks if connected to a voice channel. | | searchLyrics(title: string, artist?: string): Promise<string | null> | Fetches song lyrics from Google. |

MusicPlayerEvent Enum

export enum MusicPlayerEvent {
  Start = "start",
  QueueAdd = "queueAdd",
  Pause = "pause",
  Resume = "resume",
  Stop = "stop",
  Skip = "skip",
  Previous = "previous",
  Shuffle = "shuffle",
  LoopQueue = "loopQueue",
  LoopTrack = "loopTrack",
  VolumeChange = "volumeChange",
  Finish = "finish",
  Disconnect = "disconnect",
  Error = "error"
}

Event Payloads:

  • Start: { metadata: TrackMetadata, queue: TrackMetadata[] }
  • QueueAdd: { metadata?: TrackMetadata, metadatas?: TrackMetadata[], queue: TrackMetadata[] }
  • VolumeChange: { volume: number }
  • Skip: { queue: TrackMetadata[], history: string[] }
  • Previous: { metadata: TrackMetadata, queue: TrackMetadata[], history: string[] }
  • Shuffle: { queue: TrackMetadata[] }
  • LoopQueue: { enabled: boolean }
  • LoopTrack: { enabled: boolean }
  • Finish: { queue: TrackMetadata[], history: string[] }
  • Error: Error
  • Others: No payload

See types.ts for full type definitions.

Method Usage and Examples

This section provides detailed explanations and code snippets for each MusicPlayer method, demonstrating their usage within a Discord bot context using discord.js. The examples assume a MusicPlayer instance is created as shown in the Usage Examples section.

isPlaylist(url: string): Promise<boolean>

Checks if the given URL is a playlist.

Example:

if (command === 'isplaylist') {
  const url = args[0];
  const isPlaylist = await player.isPlaylist(url);
  message.channel.send(isPlaylist ? 'Yes, this is a playlist.' : 'No, this is not a playlist.');
}

searchPlaylists(query: string, platform?: SearchPlatform, limit?: number): Promise<PlaylistMetadata[]>

Searches for playlists across platforms.

Example:

if (command === 'playlists') {
  const query = args.join(' ');
  const playlists = await player.searchPlaylists(query, 'spotify', 5);
  if (playlists.length === 0) {
    message.channel.send('No playlists found.');
    return;
  }
  const list = playlists.map((p, i) => `${i + 1}. ${p.title} (${p.trackCount} tracks): ${p.url}`).join('\n');
  message.channel.send(`Playlists:\n${list}`);
}

search(query: string, platform?: SearchPlatform, limit?: number): Promise<TrackMetadata[]>

Searches for tracks across platforms.

Example:

if (command === 'search') {
  const query = args.join(' ');
  const results = await player.search(query, 'youtube', 5);
  if (results.length === 0) {
    message.channel.send('No results found.');
    return;
  }
  const resultList = results.map((r, i) => `${i + 1}. ${r.title || r.url} (${r.source})`).join('\n');
  message.channel.send(`Results:\n${resultList}`);
}

play(input: string | TrackMetadata | TrackMetadata[], radio?: boolean): Promise<void>

Plays input. If string, searches and plays first result or loads playlist. If TrackMetadata or array, plays directly.

Example:

if (command === 'play') {
  const query = args.join(' ');
  if (!query) {
    message.channel.send('Please provide a URL or search query.');
    return;
  }
  await player.play(query);
}
player.on(MusicPlayerEvent.Start, ({ metadata }) => {
  message.channel.send(`▶️ Now playing: ${metadata.title || metadata.url}`);
});
player.on(MusicPlayerEvent.QueueAdd, ({ metadata, metadatas, queue }) => {
  const added = metadatas ? metadatas.length + ' tracks' : metadata?.title || metadata?.url;
  message.channel.send(`➕ Added: ${added} (${queue.length} in queue)`);
});

pause(): void

Pauses the current track.

Example:

if (command === 'pause') {
  player.pause();
  message.channel.send('⏸️ Playback paused.');
}

resume(): void

Resumes playback.

Example:

if (command === 'resume') {
  player.resume();
  message.channel.send('▶️ Playback resumed.');
}

setVolume(percent: number): number

Sets volume (0–200%), returns new volume.

Example:

if (command === 'volume') {
  const volume = parseInt(args[0]);
  if (isNaN(volume)) {
    message.channel.send('Please provide a valid volume (0–200).');
    return;
  }
  const newVolume = player.setVolume(volume);
  message.channel.send(`🔊 Volume set to ${newVolume}%`);
}
player.on(MusicPlayerEvent.VolumeChange, ({ volume }) => {
  message.channel.send(`🔊 Volume changed to ${volume}%`);
});

skip(): void

Skips to the next track.

Example:

if (command === 'skip') {
  player.skip();
  message.channel.send('⏭️ Skipped to next track.');
}
player.on(MusicPlayerEvent.Skip, ({ queue }) => {
  message.channel.send(`⏭️ Skipped. ${queue.length} tracks remaining.`);
});

previous(): Promise<void>

Plays the previous track.

Example:

if (command === 'previous') {
  await player.previous();
}
player.on(MusicPlayerEvent.Previous, ({ metadata }) => {
  message.channel.send(`⏮️ Playing previous: ${metadata.title || metadata.url}`);
});

shuffle(): void

Shuffles the queue.

Example:

if (command === 'shuffle') {
  player.shuffle();
  message.channel.send('🔀 Queue shuffled.');
}
player.on(MusicPlayerEvent.Shuffle, ({ queue }) => {
  message.channel.send(`🔀 Shuffled. ${queue.length} tracks in new order.`);
});

undoShuffle(): void

Restores pre-shuffle order.

Example:

if (command === 'unshuffle') {
  player.undoShuffle();
  message.channel.send('🔄 Queue restored.');
}

toggleLoopQueue(): boolean

Toggles queue loop, returns state.

Example:

if (command === 'loopqueue') {
  const enabled = player.toggleLoopQueue();
  message.channel.send(`🔁 Queue loop ${enabled ? 'enabled' : 'disabled'}.`);
}
player.on(MusicPlayerEvent.LoopQueue, ({ enabled }) => {
  message.channel.send(`🔁 Queue loop ${enabled ? 'enabled' : 'disabled'}.`);
});

toggleLoopTrack(): boolean

Toggles track loop, returns state.

Example:

if (command === 'looptrack') {
  const enabled = player.toggleLoopTrack();
  message.channel.send(`🔂 Track loop ${enabled ? 'enabled' : 'disabled'}.`);
}
player.on(MusicPlayerEvent.LoopTrack, ({ enabled }) => {
  message.channel.send(`🔂 Track loop ${enabled ? 'enabled' : 'disabled'}.`);
});

startRadio(urls: string[]): Promise<void>

Starts radio mode.

Example:

if (command === 'radio') {
  const urls = args; // Array of URLs
  await player.startRadio(urls);
  message.channel.send('📻 Radio mode started.');
}

stop(noLeave?: boolean): void

Stops playback.

Example:

if (command === 'stop') {
  player.stop(true); // Stay connected
  message.channel.send('⏹️ Stopped.');
}
player.on(MusicPlayerEvent.Stop, () => {
  message.channel.send('⏹️ Stopped.');
});

disconnect(): void

Disconnects from voice.

Example:

if (command === 'leave') {
  player.disconnect();
  message.channel.send('🔌 Disconnected.');
}
player.on(MusicPlayerEvent.Disconnect, () => {
  message.channel.send('🔌 Disconnected.');
});

join(): VoiceConnection

Joins voice channel without player subscribe.

Example:

if (command === 'join') {
  const connection = player.join();
  message.channel.send('🔗 Joined voice channel.');
}

getQueue(): TrackMetadata[]

Gets queue copy.

Example:

if (command === 'queue') {
  const queue = player.getQueue();
  const list = queue.map((t, i) => `${i + 1}. ${t.title || t.url}`).join('\n');
  message.channel.send(`📃 Queue:\n${list || 'Empty'}`);
}

getVolume(): number

Gets volume.

Example:

if (command === 'volume') {
  message.channel.send(`🔊 Volume: ${player.getVolume()}%`);
}

isPlaying(): boolean

Checks playing.

Example:

if (command === 'status') {
  message.channel.send(`🎵 ${player.isPlaying() ? 'Playing' : 'Not playing'}.`);
}

isPaused(): boolean

Checks paused.

Example:

if (command === 'status') {
  message.channel.send(`⏯️ ${player.isPaused() ? 'Paused' : 'Not paused'}.`);
}

isShuffiled(): boolean

Checks shuffled.

Example:

if (command === 'status') {
  message.channel.send(`🔀 Queue is ${player.isShuffiled() ? 'shuffled' : 'not shuffled'}.`);
}

isConnected(guildId?: string): boolean

Checks connected.

Example:

if (command === 'status') {
  message.channel.send(`🔗 ${player.isConnected() ? 'Connected' : 'Not connected'}.`);
}

searchLyrics(title: string, artist?: string): Promise<string | null>

Fetches lyrics.

Example:

if (command === 'lyrics') {
  const title = args.join(' ');
  const lyrics = await player.searchLyrics(title, 'artist');
  message.channel.send(lyrics ? `🎵 Lyrics:\n${lyrics}` : 'No lyrics found.');
}

Support and Contributions

License

This project is licensed under the MIT License. See the LICENSE file or the repository for details.


⌨️ Built with ❤️ by Sobhan-SRZA for Persian Caesar. Star the repo if you find it useful!

Contact