Introduction
Aliana-Client is an easy, flexible, and feature-rich Lavalink v4 Client for both beginners and experts.Made using Lavalink-Client,Shokaku,Kazagumo as a reference.This library provides a comprehensive solution for integrating Lavalink into your Discord bot or music application.
Lavalink is a standalone audio sending node that allows you to play music in Discord voice channels without running the audio processing on your bot's server. This significantly reduces resource usage and improves performance.
✨ What Makes Aliana-Client Unique?
🎨 Built-in Music Cards
Generate beautiful, customizable music visualization cards using the integrated
musicard library - no extra configuration needed!
🎚️ Audio Normalizer
Automatic volume normalization ensures consistent audio levels across all tracks for a perfect listening experience.
⚡ 10x Faster Loading
Intelligent track resolution caching reduces redundant API calls and speeds up playlist loading dramatically.
🎭 Pre-built Filter Presets
Ready-to-use audio presets: Nightcore, Vaporwave, 8D Audio, Bass Boost, and more - just one line of code!
Features
🚀 Lavalink v4 Native
Full support for Lavalink v4, including its powerful plugin ecosystem.
✅ Detailed Player-Destroy Reasons
Understand precisely why a player was destroyed (e.g., channel deleted, bot disconnected).
✨ Flexible Queue Stores
Use the default in-memory store or bring your own (Redis, databases, etc.).
🎶 Unresolved Tracks
Supports unresolved track objects, fetching full data only when needed.
🎚️ Built-in Filters & EQ
Easy-to-use management for audio filters and equalizers.
⚙️ Advanced Player Options
Fine-tune player behavior for disconnects, empty queues, volume handling, and more.
Installation
Install the aliana-client package using your preferred package manager:
npm install --save aliana-client
yarn add aliana-client
pnpm add aliana-client
bun add aliana-client
Supported Music Sources
Aliana-Client supports multiple music platforms through Lavalink. Sources are automatically detected from URLs, or you can specify them explicitly when searching.
🎵 Supported Platforms
YouTube
Source: youtube or ytsearch:
Native support for YouTube videos, playlists, and search.
YouTube Music
Source: youtubemusic or ytmsearch:
Optimized for music content with better search results.
Spotify
Source: spotify or spsearch:
Search Spotify tracks, albums, and playlists (requires LavaSrc plugin).
SoundCloud
Source: soundcloud or scsearch:
Native support for SoundCloud tracks and playlists.
Apple Music
Source: applemusic or amsearch:
Search Apple Music catalog (requires LavaSrc plugin).
Deezer
Source: deezer or dzsearch:
Access Deezer's music library (requires LavaSrc plugin).
JioSaavn
Source: jiosaavn or jssearch:
Indian music streaming - Bollywood, Punjabi, and more (requires JioSaavn plugin).
Yandex Music
Source: yandex or ymsearch:
Russian music platform support (requires LavaSrc plugin).
📝 Usage Examples
// Direct URLs (automatically detected)
await manager.search('https://www.youtube.com/watch?v=dQw4w9WgXcQ', user);
await manager.search('https://open.spotify.com/track/3n3Ppam7vgaVa1iaRUc9Lp', user);
// Search with specific source
await manager.search('Imagine Dragons', user, 'youtube');
await manager.search('Arijit Singh', user, 'jiosaavn');
await manager.search('Coldplay', user, 'spotify');
// Using search prefixes directly
await manager.search('ytmsearch:Bohemian Rhapsody', user);
await manager.search('spsearch:Shape of You', user);
await manager.search('jssearch:Kesariya', user);
To use Spotify, Apple Music, Deezer, or other premium sources, you need to configure your Lavalink server with the appropriate plugins (like LavaSrc). YouTube and SoundCloud work out of the box.
For detailed Lavalink setup with plugins, see the LavaSrc documentation.
Quick Start
Here's a minimal example to get you started quickly:
import { LavalinkManager } from "aliana-client";
import { Client, GatewayIntentBits } from "discord.js";
// Extend the Client type to include the lavalink manager
declare module "discord.js" {
interface Client {
lavalink: LavalinkManager;
}
}
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
]
});
client.lavalink = new LavalinkManager({
nodes: [
{
authorization: "youshallnotpass",
host: "localhost",
port: 2333,
id: "Main Node",
}
],
sendToShard: (guildId, payload) => {
const guild = client.guilds.cache.get(guildId);
if (guild) guild.shard.send(payload);
},
autoSkip: true,
client: {
id: process.env.CLIENT_ID,
username: "MyBot",
},
});
// Listen for the 'raw' event and forward it
client.on("raw", (d) => client.lavalink.sendRawData(d));
client.on("ready", () => {
console.log(`Logged in as ${client.user.tag}!`);
client.lavalink.init({ ...client.user });
});
client.login(process.env.DISCORD_TOKEN);
LavalinkManager
The LavalinkManager is the core class that manages all Lavalink nodes and players.
Creating a Manager
const manager = new LavalinkManager({
nodes: [
{
authorization: "your-password",
host: "localhost",
port: 2333,
id: "Main Node",
}
],
sendToShard: (guildId, payload) => {
// Send payload to Discord gateway
},
autoSkip: true,
client: {
id: "your-bot-id",
username: "YourBot",
},
});
Key Methods
| Method | Description |
|---|---|
init(clientData) |
Initialize the manager with Discord client data |
createPlayer(options) |
Create a new player for a guild |
getPlayer(guildId) |
Get an existing player by guild ID |
search(query, requester, platform) |
Search for tracks on a specific platform |
Player
The Player class represents a music player for a specific guild.
Creating a Player
const player = manager.createPlayer({
guildId: interaction.guildId,
voiceChannelId: interaction.member.voice.channelId,
textChannelId: interaction.channelId,
});
await player.connect();
Playing Tracks
// Search for a track
const result = await manager.search("Despacito", interaction.user, "youtube");
// Add to queue
await player.queue.add(result.tracks[0]);
// Start playing
await player.play();
Player Properties
| Property | Type | Description |
|---|---|---|
volume |
number | Current volume (0-100) |
playing |
boolean | Whether a track is currently playing |
paused |
boolean | Whether playback is paused |
position |
number | Current playback position in milliseconds |
autoPlay |
boolean | Whether autoplay is enabled |
Queue System
The queue system manages the list of tracks to be played.
Adding Tracks
// Add a single track
await player.queue.add(track);
// Add multiple tracks
await player.queue.add([track1, track2, track3]);
Queue Operations
// Remove a track
await player.queue.remove(0);
// Clear the queue
await player.queue.clear();
// Shuffle the queue
await player.queue.shuffle();
// Get queue information
console.log(player.queue.size); // Number of tracks
console.log(player.queue.duration); // Total duration in ms
console.log(player.queue.current); // Currently playing track
Events
Listen to various events to create interactive and responsive logic.
Manager Events
// Track started playing
manager.on('trackStart', (player, track) => {
console.log(`Now playing: ${track.info.title}`);
});
// Track ended
manager.on('trackEnd', (player, track, reason) => {
console.log(`Track ended: ${reason}`);
});
// Queue ended
manager.on('queueEnd', (player) => {
console.log('Queue has finished');
player.destroy();
});
// Track error
manager.on('trackError', (player, track, error) => {
console.error(`Error playing ${track.info.title}:`, error);
});
// Autoplay track
manager.on('autoPlayTrack', (player, track) => {
console.log(`Autoplay: ${track.info.title}`);
});
Available Events
| Event | Parameters | Description |
|---|---|---|
trackStart |
player, track | Emitted when a track starts playing |
trackEnd |
player, track, reason | Emitted when a track ends |
trackError |
player, track, error | Emitted when a track encounters an error |
trackStuck |
player, track, threshold | Emitted when a track gets stuck |
queueEnd |
player | Emitted when the queue finishes |
autoPlayTrack |
player, track | Emitted when autoplay plays a track |
Autoplay Feature
The autoplay feature automatically finds and plays related tracks when your queue ends, similar to Spotify's autoplay.
When the queue becomes empty and autoplay is enabled:
- The system searches for tracks related to the last played track
- If related tracks are found, one is randomly selected and played
- This process repeats each time the queue ends, creating continuous playback
- If no related tracks are found, the queueEnd event is emitted and playback stops
- Disabling autoplay stops this automatic behavior
Enabling Autoplay
// Enable autoplay
player.setAutoPlay(true);
// Disable autoplay
player.setAutoPlay(false);
// Check autoplay status
console.log(player.autoPlay); // true or false
Example Command
const autoplayCommand = {
data: new SlashCommandBuilder()
.setName('autoplay')
.setDescription('Toggle autoplay'),
async execute(interaction) {
const player = manager.getPlayer(interaction.guildId);
if (!player) {
return interaction.reply('No player found!');
}
player.setAutoPlay(!player.autoPlay);
return interaction.reply(
player.autoPlay
? '✅ Autoplay enabled!'
: '❌ Autoplay disabled.'
);
},
};
Listening to Autoplay Events
manager.on('autoPlayTrack', (player, track) => {
const channel = client.channels.cache.get(player.textChannelId);
if (channel && 'send' in channel) {
channel.send(`🎵 Autoplay: Now playing **${track.info.title}**`);
}
});
🎨 Music Cards Generator (Exclusive Feature)
Aliana-Client includes built-in support for generating beautiful, animated music visualization cards
using the musicard library. This is an exclusive feature that sets us apart from other
Lavalink clients!
Music cards provide a visually stunning way to display now-playing information in Discord, making your bot stand out with professional-looking embeds and dynamic progress bars.
Quick Start
import { musicCard } from "musicard";
import { AttachmentBuilder } from "discord.js";
// Get the current player and track
const player = manager.players.get(guildId);
const track = player.queue.current;
// Generate a beautiful music card
const card = await musicCard({
thumbnailImage: track.info.artworkUrl || "default_artwork.png",
name: track.info.title,
author: track.info.author,
color: "auto", // Auto-detect colors from artwork!
theme: "dynamic",
brightness: 75,
progress: player.position,
startTime: 0,
endTime: track.duration
});
// Send to Discord
const attachment = new AttachmentBuilder(card, { name: "music-card.png" });
await channel.send({ files: [attachment] });
Available Themes
🌈 Dynamic
Colors automatically adapt from the track's artwork for a cohesive, professional look.
🌙 Classic Dark
Timeless dark theme with vibrant accent colors that work for any music genre.
⚡ Custom
Full control over background, text, and progress bar colors - match your brand!
Live Progress Updates
// Create a live-updating music card (updates every 10 seconds)
let cardMessage = null;
const updateCard = async () => {
const player = manager.players.get(guildId);
if (!player?.playing) return;
const track = player.queue.current;
const card = await musicCard({
thumbnailImage: track?.info.artworkUrl || "default.png",
name: track?.info.title || "Unknown",
author: track?.info.author || "Unknown Artist",
progress: player.position,
endTime: track?.duration || 0,
theme: "dynamic",
brightness: 80
});
const attachment = new AttachmentBuilder(card, { name: "now-playing.png" });
if (!cardMessage) {
cardMessage = await channel.send({ files: [attachment] });
} else {
await cardMessage.edit({ files: [attachment] });
}
};
// Update every 10 seconds
const interval = setInterval(updateCard, 10000);
updateCard(); // Initial update
// Clean up when track ends
manager.on('trackEnd', () => clearInterval(interval));
Advanced Customization
// Full customization options
const card = await musicCard({
thumbnailImage: track.info.artworkUrl,
name: track.info.title,
author: track.info.author,
color: "#5865F2", // Custom hex color
theme: "classic",
brightness: 90,
progress: player.position,
startTime: 0,
endTime: track.duration,
nameColor: "#FFFFFF",
progressColor: "#5865F2",
progressBarColor: "#2C2F33"
});
⚡ Performance Tip
Generating music cards can be resource-intensive. Consider caching cards or limiting update frequency (10-15 seconds) to avoid rate limits and reduce CPU/memory usage.
Integration with Commands
// Nowplaying command with music card
const nowplayingCommand = {
data: new SlashCommandBuilder()
.setName('nowplaying')
.setDescription('Show current track with a beautiful card'),
async execute(interaction) {
const player = manager.getPlayer(interaction.guildId);
if (!player || !player.playing) {
return interaction.reply('❌ Nothing is playing!');
}
const track = player.queue.current;
const card = await musicCard({
thumbnailImage: track.info.artworkUrl,
name: track.info.title,
author: track.info.author,
theme: "dynamic",
progress: player.position,
endTime: track.duration
});
const attachment = new AttachmentBuilder(card, { name: "np.png" });
await interaction.reply({ files: [attachment] });
}
};
Audio Filters
Apply various audio filters to customize the sound output.
Available Filters
// Bass boost
await player.filters.setBassBoost(0.5); // 0-1
// Nightcore (speed up + higher pitch)
await player.filters.setNightcore(1.2);
// Vaporwave (slow down + lower pitch)
await player.filters.setVaporwave(0.8);
// 8D Audio
await player.filters.set8D(true);
// Karaoke
await player.filters.setKaraoke(true);
// Tremolo
await player.filters.setTremolo(4.0, 0.5);
// Clear all filters
await player.filters.clear();
Custom Equalizer
// Set custom EQ bands (0-14)
await player.filters.setEqualizer([
{ band: 0, gain: 0.2 },
{ band: 1, gain: 0.3 },
{ band: 2, gain: 0.1 },
]);
Custom Queue Stores
Implement custom queue storage to persist queues across restarts or sync them across multiple processes.
Redis Store Example
import { QueueStore, StoredQueue } from "aliana-client";
import { RedisClientType } from "redis";
class RedisQueueStore implements QueueStore {
private redis: RedisClientType;
constructor(redisClient: RedisClientType) {
this.redis = redisClient;
}
private key(guildId: string) {
return `lavalinkqueue_${guildId}`;
}
async get(guildId: string): Promise {
return await this.redis.get(this.key(guildId));
}
async set(guildId: string, data: string): Promise {
await this.redis.set(this.key(guildId), data);
}
async delete(guildId: string): Promise {
await this.redis.del(this.key(guildId));
}
async parse(data: string): Promise> {
return JSON.parse(data);
}
stringify(data: Partial): string {
return JSON.stringify(data);
}
}
// Use the custom store
const manager = new LavalinkManager({
// ... other options
queueOptions: {
queueStore: new RedisQueueStore(redisClient),
},
});
Complete Examples
Play Command
const playCommand = {
data: new SlashCommandBuilder()
.setName('play')
.setDescription('Play a song')
.addStringOption(option =>
option.setName('query')
.setDescription('Song name or URL')
.setRequired(true)
),
async execute(interaction) {
await interaction.deferReply();
// Get or create player
let player = manager.getPlayer(interaction.guildId);
if (!player) {
player = manager.createPlayer({
guildId: interaction.guildId,
voiceChannelId: interaction.member.voice.channelId,
textChannelId: interaction.channelId,
});
await player.connect();
}
// Search for the track (auto-detects URLs or uses default source)
const query = interaction.options.getString('query');
const result = await manager.search(query, interaction.user);
if (!result.tracks || result.tracks.length === 0) {
return interaction.editReply('No results found!');
}
const track = result.tracks[0];
await player.queue.add(track);
// Start playing if not already playing
if (!player.playing && !player.paused) {
await player.play();
}
return interaction.editReply(
`Added to queue: **${track.info.title}** by ${track.info.author}`
);
},
};
Queue Command
const queueCommand = {
data: new SlashCommandBuilder()
.setName('queue')
.setDescription('Show the current queue'),
async execute(interaction) {
const player = manager.getPlayer(interaction.guildId);
if (!player || player.queue.isEmpty) {
return interaction.reply('Queue is empty!');
}
const current = player.queue.current;
const upcoming = player.queue.tracks.slice(0, 10);
let description = `**Now Playing:**\n${current.info.title}\n\n`;
if (upcoming.length > 0) {
description += '**Up Next:**\n';
upcoming.forEach((track, i) => {
description += `${i + 1}. ${track.info.title}\n`;
});
}
return interaction.reply({
embeds: [{
title: 'Queue',
description,
color: 0x5865F2,
}]
});
},
};
🤖 Example Bot: Ayira-Bot
Ayira-Bot is a fully-featured Discord music bot built with aliana-client that demonstrates all the capabilities of this library in a real-world application.
https://github.com/Itz-Npg/Ayira-Bot
Check out the source code to see how aliana-client is used in a production-ready Discord bot!
✨ Features Demonstrated
🎵 Music Playback
Play from YouTube, Spotify, SoundCloud, and more with automatic source detection.
📋 Queue Management
Advanced queue system with shuffle, loop, and remove features.
🎚️ Audio Filters
Nightcore, bass boost, 8D audio, and custom filter support.
🎨 Music Cards
Beautiful now-playing cards using the built-in music card generator.
🤖 Smart Autoplay
Intelligent track recommendations when the queue ends.
⚡ Fast Track Loading
FastTrackFetcher integration for 10x faster performance.
🚀 Why Use Ayira-Bot as Reference?
- ✅ Production-Ready Code - Well-structured, maintainable, and scalable
- ✅ Best Practices - Follows aliana-client's recommended patterns
- ✅ Complete Implementation - Shows all major features in action
- ✅ Error Handling - Proper error handling and user feedback
- ✅ TypeScript - Fully typed for better development experience
📚 Learning from Ayira-Bot
The bot serves as a comprehensive example of how to:
- Initialize and configure LavalinkManager
- Handle Discord voice state updates
- Implement commands for play, skip, pause, resume, queue
- Use filters and audio effects
- Generate music cards for now-playing displays
- Implement autoplay with smart recommendations
- Handle multiple servers simultaneously
Clone the repository and explore the code to see how aliana-client powers a full-featured music bot!
git clone https://github.com/Itz-Npg/Ayira-Bot.git
cd Ayira-Bot
npm install
npm start