✨ Features
🎯 Core Features
- 🎵 Lavalink v4 Integration - Robust client with automatic reconnection and failover
- 🔍 Multi-Source Support - YouTube, Spotify, SoundCloud, Deezer, Apple Music, and more
- 🚀 Auto-Detection - Automatically detects platform from URLs (works with any source!)
- 💪 TypeScript Support - Full TypeScript definitions included
- 🔄 Smart Fallback - Automatically tries alternative sources if primary fails
- ⚡ High Performance - Optimized batch processing for large playlists (1000+ tracks)
🎛️ Advanced Features
- Audio Filters - 13+ presets (Bass Boost, Nightcore, 8D, Vaporwave, etc.) + custom filters
- Advanced Playback - Replay, seek forward/backward, jump to track, back to previous
- Queue Management - 30+ methods including search, sort, filter, remove duplicates
- Statistics - Detailed player stats with real-time position tracking
- History System - Track playback history with configurable size
- Loop Modes - Off, Track, Queue with full control
- Rich Events - 25+ events for complete player monitoring
- Smart Search - Intelligent search with similarity scoring and Levenshtein distance
- Playlist Progress - Real-time progress tracking for large playlists
- Auto-Retry on Stuck - Automatically attempts to resume stuck tracks before skipping
- Ping Monitoring - Real-time latency monitoring for all nodes (every 30s)
- Health Checks - Automatic health monitoring for nodes (CPU, memory, ping)
🎨 Player States
IDLE - Player is idle
CONNECTING - Connecting to voice channel
CONNECTED - Connected and ready
PLAYING - Currently playing
PAUSED - Playback paused
ENDED - Track ended
ERRORED - Error occurred
STUCK - Track stuck
DESTROYED - Player destroyed
📦 Installation
npm install suwaku
Requirements
- Node.js v16.9.0 or higher (v22+ recommended for full ESM support)
- Discord.js v14.x
- Lavalink v4 server
Important Notes
- ✅ ES Modules (ESM) - Uses modern ES modules syntax (import/export)
- ✅ Discord.js v14 Compatible - Fully compatible with Discord.js v14
- ✅ Lavalink v4 Compatible - Uses the standard Lavalink v4 REST API
- ⚠️ Spotify Support - Requires Lavalink with LavaSrc plugin for Spotify URLs
🚀 Quick Start
import { Client, GatewayIntentBits } from 'discord.js';
import { SuwakuClient } from 'suwaku';
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates
]
});
const suwaku = new SuwakuClient(client, {
nodes: [{
host: 'localhost',
port: 2333,
password: 'youshallnotpass',
secure: false,
identifier: 'main-node'
}],
searchEngine: 'youtube',
defaultVolume: 80,
autoLeave: true,
autoLeaveDelay: 30000,
loadBalancer: true,
trackPlayerMoved: true
});
client.once('clientReady', async () => {
console.log(`Logged in as ${client.user.tag}`);
await suwaku.init();
});
await suwaku.play({
query: 'INERTIA - To Be Hero X',
voiceChannel: member.voice.channel,
textChannel: interaction.channel,
member: interaction.member
});
await suwaku.join({
voiceChannel: member.voice.channel,
textChannel: interaction.channel
});
await suwaku.leave(guildId);
client.login('YOUR_BOT_TOKEN');
📚 API Documentation
SuwakuClient
Constructor Options
new SuwakuClient(discordClient, {
nodes: [{
host: 'localhost',
port: 2333,
password: 'youshallnotpass',
secure: false,
identifier: 'main-node',
region: 'us'
}],
searchEngine: 'youtube',
defaultVolume: 80,
autoPlay: false,
autoLeave: true,
autoLeaveDelay: 30000,
leaveOnEmpty: false,
leaveOnEmptyDelay: 60000,
leaveOnEnd: false,
historySize: 50,
maxQueueSize: 1000,
maxPlaylistSize: 500,
allowDuplicates: true,
enableFilters: true,
enableSourceFallback: true,
loadBalancer: true,
trackPlayerMoved: true,
reconnectDelay: 5000,
reconnectAttempts: 5,
retryOnStuck: true,
stuckThreshold: 10000,
defaultYoutubeThumbnail: 'maxresdefault'
});
Methods
await suwaku.play({
query: 'song name or URL',
voiceChannel: voiceChannel,
textChannel: textChannel,
member: member,
source: 'youtube'
});
await suwaku.join({
voiceChannel: voiceChannel,
textChannel: textChannel,
deaf: false,
mute: false
});
await suwaku.leave(guildId, destroy = true);
const result = await suwaku.search('query', {
source: 'youtube',
limit: 10,
requester: member
});
const player = suwaku.getPlayer(guildId);
const player = suwaku.createPlayer({
guildId: guildId,
voiceChannelId: voiceChannelId,
textChannelId: textChannelId
});
await suwaku.destroyPlayer(guildId);
SuwakuPlayer
Playback Control
await player.play(track);
await player.pause();
await player.resume();
await player.stop();
await player.skip();
await player.previous();
await player.seek(30000);
await player.seekForward(10000);
await player.seekBackward(10000);
await player.replay();
await player.setVolume(80);
player.setLoop('off');
player.setLoop('track');
player.setLoop('queue');
Queue Management
player.addTrack(track);
player.addTracks([track1, track2, track3]);
await player.addTracksBatch(tracks, {
batchSize: 100,
playlistInfo: { name: 'My Playlist' }
});
player.removeTrack(position);
player.queue.clear();
player.shuffleQueue();
player.removeDuplicates();
await player.jumpTo(5);
player.moveTrack(from, to);
player.queue.size;
player.queue.duration;
player.queue.isEmpty;
player.current;
player.queue.tracks;
Audio Filters
await player.filters.applyPreset('nightcore');
await player.filters.applyPreset('bassboost');
await player.filters.applyPreset('8d');
await player.filters.applyPreset('vaporwave');
await player.filters.setEqualizer([
{ band: 0, gain: 0.25 },
{ band: 1, gain: 0.25 }
]);
await player.filters.setTimescale({
speed: 1.2,
pitch: 1.2,
rate: 1.0
});
await player.filters.clearFilters();
Player Info
const stats = player.getStats();
const health = player.healthCheck();
player.getCurrentPosition();
player.getRemainingTime();
player.getTotalQueueDuration();
🎭 Events
Player Events
suwaku.on('trackStart', (player, track) => {
console.log(`Now playing: ${track.title}`);
});
suwaku.on('trackEnd', (player, track, reason) => {
console.log(`Track ended: ${reason}`);
});
suwaku.on('trackError', (player, track, error) => {
console.error(`Track error: ${error.message}`);
});
suwaku.on('trackStuck', (player, track, threshold) => {
console.log(`Track stuck for ${threshold}ms`);
});
suwaku.on('trackAdd', (player, track) => {
console.log(`Added: ${track.title}`);
});
suwaku.on('tracksAdd', (player, tracks) => {
console.log(`Added ${tracks.length} tracks`);
});
suwaku.on('trackAddPlaylist', (player, playlistData) => {
console.log(`Added playlist: ${playlistData.name} (${playlistData.trackCount} tracks)`);
});
suwaku.on('playlistProgress', (player, progress) => {
console.log(`Loading playlist: ${progress.percentage}% (${progress.added}/${progress.total})`);
});
suwaku.on('queueEnd', (player) => {
console.log('Queue ended');
});
suwaku.on('playerCreate', (player) => {
console.log(`Player created for guild ${player.guildId}`);
});
suwaku.on('playerDestroy', (player) => {
console.log(`Player destroyed for guild ${player.guildId}`);
});
suwaku.on('playerJoin', (player, voiceChannel) => {
console.log(`Bot joined ${voiceChannel.name}`);
});
suwaku.on('playerLeave', (player) => {
console.log('Bot left voice channel');
});
suwaku.on('playerMoved', (player, state, channels) => {
console.log(`Bot ${state}: ${channels.oldChannelId} -> ${channels.newChannelId}`);
});
Node Events
suwaku.on('nodeConnect', (node) => {
console.log(`Node ${node.identifier} connected`);
});
suwaku.on('nodeDisconnect', (node, data) => {
console.log(`Node ${node.identifier} disconnected`);
});
suwaku.on('nodeError', (node, error) => {
console.error(`Node ${node.identifier} error:`, error);
});
suwaku.on('nodeReady', (node, data) => {
console.log(`Node ${node.identifier} ready`);
});
suwaku.on('nodeStats', (node, stats) => {
console.log(`Node ${node.identifier} stats:`, stats);
});
Debug Events
suwaku.on('debug', (message) => {
console.log(`[DEBUG] ${message}`);
});
suwaku.on('warn', (message) => {
console.warn(`[WARN] ${message}`);
});
suwaku.on('error', (error) => {
console.error(`[ERROR]`, error);
});
🌐 Multi-Platform Support
Suwaku automatically detects the platform from URLs, regardless of your default search engine!
Supported Platforms
| YouTube | ✅ | ✅ | ❌ | ✅ |
| YouTube Music | ✅ | ✅ | ✅ | ✅ |
| Spotify* | ✅ | ✅ | ✅ | ✅ |
| SoundCloud | ✅ | ✅ | ❌ | ✅ |
| Deezer | ✅ | ✅ | ✅ | ✅ |
| Apple Music | ✅ | ✅ | ✅ | ✅ |
| Bandcamp | ✅ | ✅ | ✅ | ✅ |
| Twitch | ✅ | ❌ | ❌ | ✅ |
| HTTP/Direct | ✅ | ❌ | ❌ | ✅ |
*Spotify requires Lavalink with LavaSrc plugin
Examples
await suwaku.play({
query: 'https://youtube.com/playlist?list=...',
voiceChannel, textChannel, member
});
await suwaku.play({
query: 'https://open.spotify.com/playlist/...',
voiceChannel, textChannel, member
});
await suwaku.play({
query: 'https://soundcloud.com/user/sets/...',
voiceChannel, textChannel, member
});
await suwaku.play({
query: 'imagine dragons',
voiceChannel, textChannel, member
});
⚡ Performance
Optimized for Large Playlists
Suwaku uses batch processing for large playlists:
- 50-100 tracks: ~100ms
- 200 tracks: ~200ms (6x faster than v1.0)
- 500 tracks: ~500ms (7x faster than v1.0)
- 1000+ tracks: Automatic chunking with progress events
Features
- ✅ Non-blocking event loop
- ✅ Automatic batch size optimization
- ✅ Real-time progress tracking
- ✅ Memory efficient
- ✅ Load balancing across nodes
- ✅ Automatic ping monitoring (every 30s)
- ✅ Auto-retry on stuck tracks
📊 Node Monitoring
Automatic Ping Monitoring
Suwaku automatically monitors node latency every 30 seconds:
const node = suwaku.nodes.get('main-node');
console.log(`Ping: ${node.ping}ms`);
const health = node.getHealth();
suwaku.on('debug', (message) => {
if (message.includes('Ping:')) {
console.log(message);
}
});
suwaku.on('warn', (message) => {
if (message.includes('High ping')) {
console.warn(message);
}
});
Health Checks
const nodes = suwaku.nodes.getConnected();
nodes.forEach(node => {
const health = node.getHealth();
if (!health.healthy) {
console.error(`❌ ${node.identifier} unhealthy:`, health.issues);
} else {
console.log(`✅ ${node.identifier} healthy (${node.ping}ms)`);
}
});
function getBestNode() {
const nodes = suwaku.nodes.getConnected();
return nodes.reduce((best, node) =>
node.ping < best.ping ? node : best
);
}
Node Statistics
const info = node.getInfo();
suwaku.on('nodeStats', (node, stats) => {
console.log(`${node.identifier} Stats:`, {
players: stats.players?.playing,
cpu: (stats.cpu?.systemLoad * 100).toFixed(1) + '%',
memory: (stats.memory?.used / 1024 / 1024).toFixed(0) + 'MB',
ping: node.ping + 'ms'
});
});
🔧 Configuration Examples
Basic Setup
const suwaku = new SuwakuClient(client, {
nodes: [{
host: 'localhost',
port: 2333,
password: 'youshallnotpass'
}],
searchEngine: 'youtube'
});
Advanced Setup
const suwaku = new SuwakuClient(client, {
nodes: [
{
host: 'node1.example.com',
port: 2333,
password: 'password1',
secure: true,
identifier: 'node-1',
region: 'us'
},
{
host: 'node2.example.com',
port: 2333,
password: 'password2',
secure: true,
identifier: 'node-2',
region: 'eu'
}
],
searchEngine: 'youtube',
defaultVolume: 80,
autoLeave: true,
autoLeaveDelay: 60000,
leaveOnEmpty: true,
leaveOnEmptyDelay: 30000,
historySize: 100,
maxQueueSize: 2000,
maxPlaylistSize: 1000,
enableFilters: true,
enableSourceFallback: true,
loadBalancer: true,
trackPlayerMoved: true,
reconnectDelay: 3000,
reconnectAttempts: 10,
defaultYoutubeThumbnail: 'maxresdefault'
});
📝 Examples
Check the examples/ folder for complete bot examples:
basic-bot.js - Complete bot with slash commands
- Includes all features: play, pause, skip, queue, filters, etc.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
� LTroubleshooting
Music Stuttering/Buffering?
Suwaku has built-in mechanisms to handle stuttering:
- Auto-Retry - Automatically tries to resume stuck tracks
- Ping Monitoring - Detects high latency nodes
- Health Checks - Monitors node CPU and memory
Quick Fixes:
const suwaku = new SuwakuClient(client, {
retryOnStuck: true,
stuckThreshold: 15000,
reconnectAttempts: 10
});
suwaku.on('trackStuck', (player, track) => {
console.warn(`⚠️ Track stuck: ${track.title}`);
});
suwaku.on('warn', (message) => {
if (message.includes('High ping')) {
console.warn(`⚠️ ${message}`);
}
});
Best Solution: Run your own Lavalink server for best performance!
📄 License
ISC License - see LICENSE file for details
📞 Support
Made with ❤️ by ShindoZk from Brazil
⭐ Star this repo if you find it useful!