staytube-music
v1.0.0
Published
A modern Discord music library for discord.js v14, powered by Lavalink (Kazagumo/Shoukaku) — stable playback, multi-source search, queues, filters, and a simple API.
Maintainers
Readme
StayTube_Music
A modern music library for discord.js v14, powered by Lavalink. StayTube_Music gives you a small, clean API for playing music from YouTube, Spotify, SoundCloud, Apple Music and more, with stable playback that stays smooth no matter how many servers you're in.
Under the hood it builds on two excellent open-source libraries — Kazagumo and Shoukaku — which handle the Lavalink connection. StayTube_Music wraps them in a simpler, friendlier interface. Full credit for the audio engine goes to their authors (see the NOTICE file).
Why Lavalink
Lavalink is a standalone audio server. Your bot sends it a search or a URL, and Lavalink streams the audio straight into the voice channel — the audio never passes through your bot process. That's why it stays fast and stable under load, and why it handles sources like YouTube far more reliably than in-process approaches.
Requirements
- Node.js 18+
- A running Lavalink v4 server (self-hosted, or a free public node)
- discord.js v14
Install
npm install staytube-music discord.js kazagumo shoukakukazagumo and shoukaku are peer dependencies — you install them yourself, so there's never a version clash.
Quick start
const { Client, GatewayIntentBits } = require("discord.js");
const { StayTubeMusic } = require("staytube-music");
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.MessageContent,
],
});
const music = new StayTubeMusic(client, {
nodes: [
{ name: "main", url: "localhost:2333", auth: "youshallnotpass", secure: false },
],
defaultSearchEngine: "ytmsearch",
});
// IMPORTANT: forward raw voice packets to the library
client.on("raw", (d) => music.kazagumo.shoukaku.connections.get(d.d?.guild_id)?.setStateUpdate?.(d));
client.on("messageCreate", async (message) => {
if (message.author.bot || !message.inGuild()) return;
if (message.content.startsWith("!play ")) {
const query = message.content.slice(6);
const vc = message.member.voice.channel;
if (!vc) return message.reply("Join a voice channel first.");
const { result } = await music.play(vc, query, {
member: message.member,
textChannel: message.channel,
});
if (!result.tracks.length) return message.reply("No results found.");
message.reply(`Added **${result.tracks[0].title}**`);
}
if (message.content === "!skip") {
music.get(message.guildId)?.skip();
}
if (message.content === "!stop") {
await music.stop(message.guildId);
}
});
music.on("playerStart", (player, track) => {
client.channels.cache.get(player.textId)?.send(`Now playing: **${track.title}**`);
});
client.login("YOUR_BOT_TOKEN");Getting a Lavalink server
You have two options:
Free public node (quickest): grab a live node from one of these lists and drop its host/port/password into nodes:
- https://lavainfo.netlify.app
- https://lavalink.darrennathanael.com
Self-host (most reliable): download Lavalink from https://github.com/lavalink-devs/Lavalink, set a password in application.yml, and run it with Java 17+. Then use localhost:2333 with your password.
API
new StayTubeMusic(client, options)
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| nodes | StayTubeNode[] | — (required) | Lavalink nodes: { name, url, auth, secure? } |
| defaultSearchEngine | string | "ytmsearch" | Engine for plain-text searches |
| plugins | KazagumoPlugin[] | [] | Extra source plugins (e.g. kazagumo-spotify) |
| nodeOptions | object | {} | Advanced Shoukaku node options |
Methods
join(voiceChannel, textChannel, deaf?)→ creates/returns a player for the guild.search(query, requester?, engine?)→ searches a URL or text; returns tracks.play(voiceChannel, query, options)→ join + search + queue + play in one call.get(guildId)→ the player for a guild, orundefined.stop(guildId)→ destroy the player and leave.
play options: { textChannel, member?, engine?, deaf? }.
Working with the player
The player returned is a Kazagumo player, so you get its full API:
const player = music.get(guildId);
player.pause(true); // pause
player.pause(false); // resume
player.skip();
player.setVolume(80);
player.seek(60000); // ms
player.setLoop("track"); // "none" | "track" | "queue"
player.queue.add(track);
player.queue.shuffle();
console.log(player.queue.current, player.queue.length);Events
Forwarded from the underlying engine:
music.on("playerStart", (player, track) => {});
music.on("playerEnd", (player, track) => {});
music.on("playerEmpty", (player) => {});
music.on("playerException",(player, data) => {});
music.on("nodeReady", (name) => {});
music.on("nodeError", (name, error) => {});
music.on("nodeDisconnect",(name, count) => {});Search engines
Depending on the plugins installed on your Lavalink node: ytsearch (YouTube), ytmsearch (YouTube Music), scsearch (SoundCloud), spsearch (Spotify), amsearch (Apple Music), dzsearch (Deezer), jssearch (JioSaavn).
Credits
The audio engine is provided by Kazagumo, Shoukaku, and Lavalink, all MIT-licensed and authored by their respective teams. StayTube_Music is a wrapper and does not claim ownership of them. See NOTICE.
License
MIT © StayTube_Music
