Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Lavalink v4 support #521

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,4 @@ SESSION_PASSWORD=
WEATHERSTACK_KEY=

# Required for image commands (https://strangeapi.fun/docs)
STRANGE_API_KEY=

# SPOTFIY [Required for Spotify Support]
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
STRANGE_API_KEY=
30 changes: 20 additions & 10 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,29 @@ module.exports = {
},

MUSIC: {
ENABLED: false,
IDLE_TIME: 60, // Time in seconds before the bot disconnects from an idle voice channel
ENABLED: true,
IDLE_TIME: 120, // Time in seconds before the bot disconnects from an idle voice channel
MAX_SEARCH_RESULTS: 5,
DEFAULT_SOURCE: "SC", // YT = Youtube, YTM = Youtube Music, SC = SoundCloud
// Add any number of lavalink nodes here
// Refer to https://github.com/freyacodes/Lavalink to host your own lavalink server
DEFAULT_VOLUME: 60, // Default volume for the music player (0-100)
DEFAULT_SOURCE: "ytsearch", // ytsearch = Youtube, ytmsearch = Youtube Music, spsearch = Spotify, scsearch = SoundCloud
// Lavalink WebSocket configuration
LAVALINK_WS: {
clientName: "Strange-bot", // The name of the Lavalink client.
resuming: true, // Whether Lavalink should attempt to resume existing sessions when reconnecting.
reconnecting: {
tries: Infinity, // Number of times to attempt reconnecting.
delay: 20000 // Delay in milliseconds between each reconnection attempt.
}
},
// Add any number of Lavalink nodes here
LAVALINK_NODES: [
{
host: "localhost",
port: 2333,
password: "youshallnotpass",
id: "Local Node",
secure: false,
info: {
host: "localhost",
auth: "youshallnotpass",
port: 2333,
},
identifier: "Local Node",
},
],
},
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
"url": "https://github.com/saiteja-madha/discord-js-bot/issues"
},
"dependencies": {
"@lavaclient/queue": "^2.1.1",
"@lavaclient/spotify": "^3.1.0",
"@lavaclient/plugin-queue": "^0.0.1",
"@vitalets/google-translate-api": "^9.2.0",
"ascii-table": "0.0.9",
"btoa": "^1.2.1",
Expand All @@ -42,7 +41,7 @@
"express-session": "^1.18.0",
"fixedsize-map": "^1.0.1",
"iso-639-1": "^3.1.0",
"lavaclient": "^4.1.1",
"lavaclient": "^5.0.0-rc",
"module-alias": "^2.2.3",
"moment": "^2.30.1",
"mongoose": "^8.1.1",
Expand Down
6 changes: 3 additions & 3 deletions src/commands/music/bassboost.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ module.exports = {
* @param {number} level
*/
function setBassBoost({ client, guildId }, level) {
const player = client.musicManager.getPlayer(guildId);
const player = client.musicManager.players.resolve(guildId);
const bands = new Array(3).fill(null).map((_, i) => ({ band: i, gain: levels[level] }));
player.setEqualizer(...bands);
player.setFilters(...bands);
return `> Set the bassboost level to \`${level}\``;
}
}
8 changes: 4 additions & 4 deletions src/commands/music/loop.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { musicValidations } = require("@helpers/BotUtils");
const { LoopType } = require("@lavaclient/queue");
const { LoopType } = require("@lavaclient/plugin-queue");
const { ApplicationCommandOptionType } = require("discord.js");

/**
Expand Down Expand Up @@ -56,7 +56,7 @@ module.exports = {
* @param {"queue"|"track"} type
*/
function toggleLoop({ client, guildId }, type) {
const player = client.musicManager.getPlayer(guildId);
const player = client.musicManager.players.resolve(guildId);

// track
if (type === "track") {
Expand All @@ -66,7 +66,7 @@ function toggleLoop({ client, guildId }, type) {

// queue
else if (type === "queue") {
player.queue.setLoop(1);
player.queue.setLoop(LoopType.Queue);
return "Loop mode is set to `queue`";
}
}
}
35 changes: 22 additions & 13 deletions src/commands/music/np.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,39 +33,48 @@ module.exports = {
/**
* @param {import("discord.js").CommandInteraction|import("discord.js").Message} arg0
*/
function nowPlaying({ client, guildId }) {
const player = client.musicManager.getPlayer(guildId);
function nowPlaying({ client, guildId, member }) {
const player = client.musicManager.players.resolve(guildId);
if (!player || !player.queue.current) return "🚫 No music is being played!";

const track = player.queue.current;
const end = track.length > 6.048e8 ? "🔴 LIVE" : new Date(track.length).toISOString().slice(11, 19);
const trackLength = track.info.isStream ? "🔴 LIVE" : prettyMs(track.info.length, { colonNotation: true });
const trackPosition = track.info.isStream ? "🔴 LIVE" : prettyMs(player.position, { colonNotation: true });

let progressBar = "";
if (!track.info.isStream) {
const totalLength = track.info.length > 6.048e8 ? player.position : track.info.length;
progressBar =
new Date(player.position).toISOString().slice(11, 19) +
" [" +
splitBar(totalLength, player.position, 15)[0] +
"] " +
new Date(track.info.length).toISOString().slice(11, 19);
} else {
progressBar = "🔴 LIVE";
}

const embed = new EmbedBuilder()
.setColor(EMBED_COLORS.BOT_EMBED)
.setAuthor({ name: "Now playing" })
.setDescription(`[${track.title}](${track.uri})`)
.setDescription(`[${track.info.title}](${track.info.uri})`)
.addFields(
{
name: "Song Duration",
value: "`" + prettyMs(track.length, { colonNotation: true }) + "`",
value: "`" + trackLength + "`",
inline: true,
},
{
name: "Requested By",
value: track.requester || "Unknown",
value: track.requesterId || member.user.displayName,
inline: true,
},
{
name: "\u200b",
value:
new Date(player.position).toISOString().slice(11, 19) +
" [" +
splitBar(track.length > 6.048e8 ? player.position : track.length, player.position, 15)[0] +
"] " +
end,
value: progressBar,
inline: false,
}
);

return { embeds: [embed] };
}
}
4 changes: 2 additions & 2 deletions src/commands/music/pause.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ module.exports = {
* @param {import("discord.js").CommandInteraction|import("discord.js").Message} arg0
*/
function pause({ client, guildId }) {
const player = client.musicManager.getPlayer(guildId);
const player = client.musicManager.players.resolve(guildId);
if (player.paused) return "The player is already paused.";

player.pause(true);
return "⏸️ Paused the music player.";
}
}
125 changes: 47 additions & 78 deletions src/commands/music/play.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
const { EmbedBuilder, ApplicationCommandOptionType } = require("discord.js");
const prettyMs = require("pretty-ms");
require("@lavaclient/plugin-queue/register")
const { EMBED_COLORS, MUSIC } = require("@root/config");
const { SpotifyItemType } = require("@lavaclient/spotify");

const search_prefix = {
YT: "ytsearch",
YTM: "ytmsearch",
SC: "scsearch",
};

/**
* @type {import("@structures/Command")}
Expand Down Expand Up @@ -54,10 +48,10 @@ module.exports = {
async function play({ member, guild, channel }, query) {
if (!member.voice.channel) return "🚫 You need to join a voice channel first";

let player = guild.client.musicManager.getPlayer(guild.id);
let player = guild.client.musicManager.players.resolve(guild.id);
if (player && !guild.members.me.voice.channel) {
player.disconnect();
await guild.client.musicManager.destroyPlayer(guild.id);
player.voice.disconnect();
await guild.client.musicManager.players.destroy(guild.id);
}

if (player && member.voice.channel !== guild.members.me.voice.channel) {
Expand All @@ -67,73 +61,45 @@ async function play({ member, guild, channel }, query) {
let embed = new EmbedBuilder().setColor(EMBED_COLORS.BOT_EMBED);
let tracks;
let description = "";
let thumbnail;

try {
if (guild.client.musicManager.spotify.isSpotifyUrl(query)) {
if (!process.env.SPOTIFY_CLIENT_ID || !process.env.SPOTIFY_CLIENT_SECRET) {
return "🚫 Spotify songs cannot be played. Please contact the bot owner";
}

const item = await guild.client.musicManager.spotify.load(query);
switch (item?.type) {
case SpotifyItemType.Track: {
const track = await item.resolveYoutubeTrack();
tracks = [track];
description = `[${track.info.title}](${track.info.uri})`;
break;
}

case SpotifyItemType.Artist:
tracks = await item.resolveYoutubeTracks();
description = `Artist: [**${item.name}**](${query})`;
break;

case SpotifyItemType.Album:
tracks = await item.resolveYoutubeTracks();
description = `Album: [**${item.name}**](${query})`;
break;

case SpotifyItemType.Playlist:
tracks = await item.resolveYoutubeTracks();
description = `Playlist: [**${item.name}**](${query})`;
break;

default:
return "🚫 An error occurred while searching for the song";
}

if (!tracks) guild.client.logger.debug({ query, item });
} else {
const res = await guild.client.musicManager.rest.loadTracks(
/^https?:\/\//.test(query) ? query : `${search_prefix[MUSIC.DEFAULT_SOURCE]}:${query}`
);
switch (res.loadType) {
case "LOAD_FAILED":
guild.client.logger.error("Search Exception", res.exception);
return "🚫 There was an error while searching";

case "NO_MATCHES":
return `No results found matching ${query}`;

case "PLAYLIST_LOADED":
tracks = res.tracks;
description = res.playlistInfo.name;
break;

case "TRACK_LOADED":
case "SEARCH_RESULT": {
const [track] = res.tracks;
tracks = [track];
break;
}

default:
guild.client.logger.debug("Unknown loadType", res);
return "🚫 An error occurred while searching for the song";
}

if (!tracks) guild.client.logger.debug({ query, res });
const res = await guild.client.musicManager.api.loadTracks(
/^https?:\/\//.test(query) ? query : `${MUSIC.DEFAULT_SOURCE}:${query}`
);

let track;

switch (res.loadType) {
case "error":
guild.client.logger.error("Search Exception", res.data);
return "🚫 There was an error while searching";

case "empty":
return `No results found matching ${query}`;

case "playlist":
tracks = res.data.tracks;
description = res.data.info.name;
thumbnail = res.data.pluginInfo.artworkUrl;
break;

case "track":
track = res.data;
tracks = [track];
break;

case "search":
track = res.data[0];
tracks = [track];
break;

default:
guild.client.logger.debug("Unknown loadType", res);
return "🚫 An error occurred while searching for the song";
}

if (!tracks) guild.client.logger.debug({ query, res });
} catch (error) {
guild.client.logger.error("Search Exception", typeof error === "object" ? JSON.stringify(error) : error);
return "🚫 An error occurred while searching for the song";
Expand All @@ -150,6 +116,7 @@ async function play({ member, guild, channel }, query) {
embed
.setAuthor({ name: "Added Track to queue" })
.setDescription(`[${track.info.title}](${track.info.uri})`)
.setThumbnail(track.info.artworkUrl)
.setFooter({ text: `Requested By: ${member.user.username}` });

fields.push({
Expand All @@ -170,6 +137,7 @@ async function play({ member, guild, channel }, query) {
} else {
embed
.setAuthor({ name: "Added Playlist to queue" })
.setThumbnail(thumbnail)
.setDescription(description)
.addFields(
{
Expand All @@ -194,17 +162,18 @@ async function play({ member, guild, channel }, query) {

// create a player and/or join the member's vc
if (!player?.connected) {
player = guild.client.musicManager.createPlayer(guild.id);
player = guild.client.musicManager.players.create(guild.id);
player.queue.data.channel = channel;
player.connect(member.voice.channel.id, { deafened: true });
player.voice.connect(member.voice.channel.id, { deafened: true });
player.setVolume(MUSIC.DEFAULT_VOLUME);
}

// do queue things
const started = player.playing || player.paused;
player.queue.add(tracks, { requester: member.user.username, next: false });
player.queue.add(tracks, { requester: member.user.displayName, next: false });
if (!started) {
await player.queue.start();
}

return { embeds: [embed] };
}
}
Loading