lavalink-client
Version:
Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.
1,072 lines (1,063 loc) • 231 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
DebugEvents: () => DebugEvents,
DefaultQueueStore: () => DefaultQueueStore,
DefaultSources: () => DefaultSources,
DestroyReasons: () => DestroyReasons,
DisconnectReasons: () => DisconnectReasons,
EQList: () => EQList,
FilterManager: () => FilterManager,
LavalinkManager: () => LavalinkManager,
LavalinkNode: () => LavalinkNode,
LavalinkPlugins: () => LavalinkPlugins,
ManagerUtils: () => ManagerUtils,
MiniMap: () => MiniMap,
NodeManager: () => NodeManager,
NodeSymbol: () => NodeSymbol,
Player: () => Player,
Queue: () => Queue,
QueueSaver: () => QueueSaver,
QueueSymbol: () => QueueSymbol,
SourceLinksRegexes: () => SourceLinksRegexes,
TrackSymbol: () => TrackSymbol,
UnresolvedTrackSymbol: () => UnresolvedTrackSymbol,
audioOutputsData: () => audioOutputsData,
parseLavalinkConnUrl: () => parseLavalinkConnUrl,
queueTrackEnd: () => queueTrackEnd,
safeStringify: () => safeStringify,
validSponsorBlocks: () => validSponsorBlocks
});
module.exports = __toCommonJS(index_exports);
// src/structures/LavalinkManager.ts
var import_events2 = require("events");
// src/structures/Constants.ts
var DebugEvents = /* @__PURE__ */ ((DebugEvents2) => {
DebugEvents2["SetSponsorBlock"] = "SetSponsorBlock";
DebugEvents2["DeleteSponsorBlock"] = "DeleteSponsorBlock";
DebugEvents2["TrackEndReplaced"] = "TrackEndReplaced";
DebugEvents2["AutoplayExecution"] = "AutoplayExecution";
DebugEvents2["AutoplayNoSongsAdded"] = "AutoplayNoSongsAdded";
DebugEvents2["AutoplayThresholdSpamLimiter"] = "AutoplayThresholdSpamLimiter";
DebugEvents2["TriggerQueueEmptyInterval"] = "TriggerQueueEmptyInterval";
DebugEvents2["QueueEnded"] = "QueueEnded";
DebugEvents2["TrackStartNewSongsOnly"] = "TrackStartNewSongsOnly";
DebugEvents2["TrackStartNoTrack"] = "TrackStartNoTrack";
DebugEvents2["ResumingFetchingError"] = "ResumingFetchingError";
DebugEvents2["PlayerUpdateNoPlayer"] = "PlayerUpdateNoPlayer";
DebugEvents2["PlayerUpdateFilterFixApply"] = "PlayerUpdateFilterFixApply";
DebugEvents2["PlayerUpdateSuccess"] = "PlayerUpdateSuccess";
DebugEvents2["HeartBeatTriggered"] = "HeartBeatTriggered";
DebugEvents2["NoSocketOnDestroy"] = "NoSocketOnDestroy";
DebugEvents2["SocketTerminateHeartBeatTimeout"] = "SocketTerminateHeartBeatTimeout";
DebugEvents2["TryingConnectWhileConnected"] = "TryingConnectWhileConnected";
DebugEvents2["LavaSearchNothingFound"] = "LavaSearchNothingFound";
DebugEvents2["SearchNothingFound"] = "SearchNothingFound";
DebugEvents2["ValidatingBlacklistLinks"] = "ValidatingBlacklistLinks";
DebugEvents2["ValidatingWhitelistLinks"] = "ValidatingWhitelistLinks";
DebugEvents2["TrackErrorMaxTracksErroredPerTime"] = "TrackErrorMaxTracksErroredPerTime";
DebugEvents2["TrackStuckMaxTracksErroredPerTime"] = "TrackStuckMaxTracksErroredPerTime";
DebugEvents2["PlayerDestroyingSomewhereElse"] = "PlayerDestroyingSomewhereElse";
DebugEvents2["PlayerCreateNodeNotFound"] = "PlayerCreateNodeNotFound";
DebugEvents2["PlayerPlayQueueEmptyTimeoutClear"] = "PlayerPlayQueueEmptyTimeoutClear";
DebugEvents2["PlayerPlayWithTrackReplace"] = "PlayerPlayWithTrackReplace";
DebugEvents2["PlayerPlayUnresolvedTrack"] = "PlayerPlayUnresolvedTrack";
DebugEvents2["PlayerPlayUnresolvedTrackFailed"] = "PlayerPlayUnresolvedTrackFailed";
DebugEvents2["PlayerVolumeAsFilter"] = "PlayerVolumeAsFilter";
DebugEvents2["BandcampSearchLokalEngine"] = "BandcampSearchLokalEngine";
DebugEvents2["PlayerChangeNode"] = "PlayerChangeNode";
DebugEvents2["BuildTrackError"] = "BuildTrackError";
DebugEvents2["TransformRequesterFunctionFailed"] = "TransformRequesterFunctionFailed";
DebugEvents2["GetClosestTrackFailed"] = "GetClosestTrackFailed";
DebugEvents2["PlayerDeleteInsteadOfDestroy"] = "PlayerDeleteInsteadOfDestroy";
DebugEvents2["FailedToConnectToNodes"] = "FailedToConnectToNodes";
DebugEvents2["NoAudioDebug"] = "NoAudioDebug";
DebugEvents2["PlayerAutoReconnect"] = "PlayerAutoReconnect";
return DebugEvents2;
})(DebugEvents || {});
var DestroyReasons = /* @__PURE__ */ ((DestroyReasons2) => {
DestroyReasons2["QueueEmpty"] = "QueueEmpty";
DestroyReasons2["NodeDestroy"] = "NodeDestroy";
DestroyReasons2["NodeDeleted"] = "NodeDeleted";
DestroyReasons2["LavalinkNoVoice"] = "LavalinkNoVoice";
DestroyReasons2["NodeReconnectFail"] = "NodeReconnectFail";
DestroyReasons2["Disconnected"] = "Disconnected";
DestroyReasons2["PlayerReconnectFail"] = "PlayerReconnectFail";
DestroyReasons2["PlayerChangeNodeFail"] = "PlayerChangeNodeFail";
DestroyReasons2["PlayerChangeNodeFailNoEligibleNode"] = "PlayerChangeNodeFailNoEligibleNode";
DestroyReasons2["ChannelDeleted"] = "ChannelDeleted";
DestroyReasons2["DisconnectAllNodes"] = "DisconnectAllNodes";
DestroyReasons2["ReconnectAllNodes"] = "ReconnectAllNodes";
DestroyReasons2["TrackErrorMaxTracksErroredPerTime"] = "TrackErrorMaxTracksErroredPerTime";
DestroyReasons2["TrackStuckMaxTracksErroredPerTime"] = "TrackStuckMaxTracksErroredPerTime";
return DestroyReasons2;
})(DestroyReasons || {});
var DisconnectReasons = /* @__PURE__ */ ((DisconnectReasons2) => {
DisconnectReasons2["Disconnected"] = "Disconnected";
DisconnectReasons2["DisconnectAllNodes"] = "DisconnectAllNodes";
return DisconnectReasons2;
})(DisconnectReasons || {});
var validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
var audioOutputsData = {
mono: {
// totalLeft: 1, totalRight: 1
leftToLeft: 0.5,
//each channel should in total 0 | 1, 0 === off, 1 === on, 0.5+0.5 === 1
leftToRight: 0.5,
rightToLeft: 0.5,
rightToRight: 0.5
},
stereo: {
// totalLeft: 1, totalRight: 1
leftToLeft: 1,
leftToRight: 0,
rightToLeft: 0,
rightToRight: 1
},
left: {
// totalLeft: 1, totalRight: 0
leftToLeft: 1,
leftToRight: 0,
rightToLeft: 1,
rightToRight: 0
},
right: {
// totalLeft: 0, totalRight: 1
leftToLeft: 0,
leftToRight: 1,
rightToLeft: 0,
rightToRight: 1
}
};
var EQList = {
/** A Bassboost Equalizer, so high it distorts the audio */
BassboostEarrape: [
{ band: 0, gain: 0.6 * 0.375 },
{ band: 1, gain: 0.67 * 0.375 },
{ band: 2, gain: 0.67 * 0.375 },
{ band: 3, gain: 0.4 * 0.375 },
{ band: 4, gain: -0.5 * 0.375 },
{ band: 5, gain: 0.15 * 0.375 },
{ band: 6, gain: -0.45 * 0.375 },
{ band: 7, gain: 0.23 * 0.375 },
{ band: 8, gain: 0.35 * 0.375 },
{ band: 9, gain: 0.45 * 0.375 },
{ band: 10, gain: 0.55 * 0.375 },
{ band: 11, gain: -0.6 * 0.375 },
{ band: 12, gain: 0.55 * 0.375 },
{ band: 13, gain: -0.5 * 0.375 },
{ band: 14, gain: -0.75 * 0.375 }
],
/** A High and decent Bassboost Equalizer */
BassboostHigh: [
{ band: 0, gain: 0.6 * 0.25 },
{ band: 1, gain: 0.67 * 0.25 },
{ band: 2, gain: 0.67 * 0.25 },
{ band: 3, gain: 0.4 * 0.25 },
{ band: 4, gain: -0.5 * 0.25 },
{ band: 5, gain: 0.15 * 0.25 },
{ band: 6, gain: -0.45 * 0.25 },
{ band: 7, gain: 0.23 * 0.25 },
{ band: 8, gain: 0.35 * 0.25 },
{ band: 9, gain: 0.45 * 0.25 },
{ band: 10, gain: 0.55 * 0.25 },
{ band: 11, gain: -0.6 * 0.25 },
{ band: 12, gain: 0.55 * 0.25 },
{ band: 13, gain: -0.5 * 0.25 },
{ band: 14, gain: -0.75 * 0.25 }
],
/** A decent Bassboost Equalizer */
BassboostMedium: [
{ band: 0, gain: 0.6 * 0.1875 },
{ band: 1, gain: 0.67 * 0.1875 },
{ band: 2, gain: 0.67 * 0.1875 },
{ band: 3, gain: 0.4 * 0.1875 },
{ band: 4, gain: -0.5 * 0.1875 },
{ band: 5, gain: 0.15 * 0.1875 },
{ band: 6, gain: -0.45 * 0.1875 },
{ band: 7, gain: 0.23 * 0.1875 },
{ band: 8, gain: 0.35 * 0.1875 },
{ band: 9, gain: 0.45 * 0.1875 },
{ band: 10, gain: 0.55 * 0.1875 },
{ band: 11, gain: -0.6 * 0.1875 },
{ band: 12, gain: 0.55 * 0.1875 },
{ band: 13, gain: -0.5 * 0.1875 },
{ band: 14, gain: -0.75 * 0.1875 }
],
/** A slight Bassboost Equalizer */
BassboostLow: [
{ band: 0, gain: 0.6 * 0.125 },
{ band: 1, gain: 0.67 * 0.125 },
{ band: 2, gain: 0.67 * 0.125 },
{ band: 3, gain: 0.4 * 0.125 },
{ band: 4, gain: -0.5 * 0.125 },
{ band: 5, gain: 0.15 * 0.125 },
{ band: 6, gain: -0.45 * 0.125 },
{ band: 7, gain: 0.23 * 0.125 },
{ band: 8, gain: 0.35 * 0.125 },
{ band: 9, gain: 0.45 * 0.125 },
{ band: 10, gain: 0.55 * 0.125 },
{ band: 11, gain: -0.6 * 0.125 },
{ band: 12, gain: 0.55 * 0.125 },
{ band: 13, gain: -0.5 * 0.125 },
{ band: 14, gain: -0.75 * 0.125 }
],
/** Makes the Music slightly "better" */
BetterMusic: [
{ band: 0, gain: 0.25 },
{ band: 1, gain: 0.025 },
{ band: 2, gain: 0.0125 },
{ band: 3, gain: 0 },
{ band: 4, gain: 0 },
{ band: 5, gain: -0.0125 },
{ band: 6, gain: -0.025 },
{ band: 7, gain: -0.0175 },
{ band: 8, gain: 0 },
{ band: 9, gain: 0 },
{ band: 10, gain: 0.0125 },
{ band: 11, gain: 0.025 },
{ band: 12, gain: 0.25 },
{ band: 13, gain: 0.125 },
{ band: 14, gain: 0.125 }
],
/** Makes the Music sound like rock music / sound rock music better */
Rock: [
{ band: 0, gain: 0.3 },
{ band: 1, gain: 0.25 },
{ band: 2, gain: 0.2 },
{ band: 3, gain: 0.1 },
{ band: 4, gain: 0.05 },
{ band: 5, gain: -0.05 },
{ band: 6, gain: -0.15 },
{ band: 7, gain: -0.2 },
{ band: 8, gain: -0.1 },
{ band: 9, gain: -0.05 },
{ band: 10, gain: 0.05 },
{ band: 11, gain: 0.1 },
{ band: 12, gain: 0.2 },
{ band: 13, gain: 0.25 },
{ band: 14, gain: 0.3 }
],
/** Makes the Music sound like Classic music / sound Classic music better */
Classic: [
{ band: 0, gain: 0.375 },
{ band: 1, gain: 0.35 },
{ band: 2, gain: 0.125 },
{ band: 3, gain: 0 },
{ band: 4, gain: 0 },
{ band: 5, gain: 0.125 },
{ band: 6, gain: 0.55 },
{ band: 7, gain: 0.05 },
{ band: 8, gain: 0.125 },
{ band: 9, gain: 0.25 },
{ band: 10, gain: 0.2 },
{ band: 11, gain: 0.25 },
{ band: 12, gain: 0.3 },
{ band: 13, gain: 0.25 },
{ band: 14, gain: 0.3 }
],
/** Makes the Music sound like Pop music / sound Pop music better */
Pop: [
{ band: 0, gain: 0.2635 },
{ band: 1, gain: 0.22141 },
{ band: 2, gain: -0.21141 },
{ band: 3, gain: -0.1851 },
{ band: 4, gain: -0.155 },
{ band: 5, gain: 0.21141 },
{ band: 6, gain: 0.22456 },
{ band: 7, gain: 0.237 },
{ band: 8, gain: 0.237 },
{ band: 9, gain: 0.237 },
{ band: 10, gain: -0.05 },
{ band: 11, gain: -0.116 },
{ band: 12, gain: 0.192 },
{ band: 13, gain: 0 }
],
/** Makes the Music sound like Electronic music / sound Electronic music better */
Electronic: [
{ band: 0, gain: 0.375 },
{ band: 1, gain: 0.35 },
{ band: 2, gain: 0.125 },
{ band: 3, gain: 0 },
{ band: 4, gain: 0 },
{ band: 5, gain: -0.125 },
{ band: 6, gain: -0.125 },
{ band: 7, gain: 0 },
{ band: 8, gain: 0.25 },
{ band: 9, gain: 0.125 },
{ band: 10, gain: 0.15 },
{ band: 11, gain: 0.2 },
{ band: 12, gain: 0.25 },
{ band: 13, gain: 0.35 },
{ band: 14, gain: 0.4 }
],
/** Boosts all Bands slightly for louder and fuller sound */
FullSound: [
{ band: 0, gain: 0.25 + 0.375 },
{ band: 1, gain: 0.25 + 0.025 },
{ band: 2, gain: 0.25 + 0.0125 },
{ band: 3, gain: 0.25 + 0 },
{ band: 4, gain: 0.25 + 0 },
{ band: 5, gain: 0.25 + -0.0125 },
{ band: 6, gain: 0.25 + -0.025 },
{ band: 7, gain: 0.25 + -0.0175 },
{ band: 8, gain: 0.25 + 0 },
{ band: 9, gain: 0.25 + 0 },
{ band: 10, gain: 0.25 + 0.0125 },
{ band: 11, gain: 0.25 + 0.025 },
{ band: 12, gain: 0.25 + 0.375 },
{ band: 13, gain: 0.25 + 0.125 },
{ band: 14, gain: 0.25 + 0.125 }
],
/** Boosts basses + lower highs for a pro gaming sound */
Gaming: [
{ band: 0, gain: 0.35 },
{ band: 1, gain: 0.3 },
{ band: 2, gain: 0.25 },
{ band: 3, gain: 0.2 },
{ band: 4, gain: 0.15 },
{ band: 5, gain: 0.1 },
{ band: 6, gain: 0.05 },
{ band: 7, gain: -0 },
{ band: 8, gain: -0.05 },
{ band: 9, gain: -0.1 },
{ band: 10, gain: -0.15 },
{ band: 11, gain: -0.2 },
{ band: 12, gain: -0.25 },
{ band: 13, gain: -0.3 },
{ band: 14, gain: -0.35 }
]
};
// src/structures/NodeManager.ts
var import_events = require("events");
// src/structures/Node.ts
var import_path = require("path");
var import_ws = __toESM(require("ws"));
// src/structures/Utils.ts
var import_node_url = require("url");
var import_types = require("util/types");
// src/structures/LavalinkManagerStatics.ts
var DefaultSources = {
// youtubemusic
"youtube music": "ytmsearch",
"youtubemusic": "ytmsearch",
"ytmsearch": "ytmsearch",
"ytm": "ytmsearch",
"musicyoutube": "ytmsearch",
"music youtube": "ytmsearch",
// youtube
"youtube": "ytsearch",
"yt": "ytsearch",
"ytsearch": "ytsearch",
// soundcloud
"soundcloud": "scsearch",
"scsearch": "scsearch",
"sc": "scsearch",
// apple music
"apple music": "amsearch",
"apple": "amsearch",
"applemusic": "amsearch",
"amsearch": "amsearch",
"am": "amsearch",
"musicapple": "amsearch",
"music apple": "amsearch",
// spotify
"spotify": "spsearch",
"spsearch": "spsearch",
"sp": "spsearch",
"spotify.com": "spsearch",
"spotifycom": "spsearch",
"sprec": "sprec",
"spsuggestion": "sprec",
// deezer
"deezer": "dzsearch",
"dz": "dzsearch",
"dzsearch": "dzsearch",
"dzisrc": "dzisrc",
"dzrec": "dzrec",
// yandexmusic
"yandex music": "ymsearch",
"yandexmusic": "ymsearch",
"yandex": "ymsearch",
"ymsearch": "ymsearch",
"ymrec": "ymrec",
// VK Music (lavasrc)
"vksearch": "vksearch",
"vkmusic": "vksearch",
"vk music": "vksearch",
"vkrec": "vkrec",
"vk": "vksearch",
// Qobuz (lavasrc)
"qbsearch": "qbsearch",
"qobuz": "qbsearch",
"qbisrc": "qbisrc",
"qbrec": "qbrec",
// speak PLUGIN
"speak": "speak",
"tts": "tts",
"ftts": "ftts",
"flowery": "ftts",
"flowery.tts": "ftts",
"flowerytts": "ftts",
// Client sided search platforms (after lavalinkv4.0.6 it will search via bcsearch on the node itself)
"bandcamp": "bcsearch",
"bc": "bcsearch",
"bcsearch": "bcsearch",
// other searches:
"phsearch": "phsearch",
"pornhub": "phsearch",
"porn": "phsearch",
// local files
"local": "local",
// http requests
"http": "http",
"https": "https",
"link": "link",
"uri": "uri",
// tidal
"tidal": "tdsearch",
"td": "tdsearch",
"tidal music": "tdsearch",
"tdsearch": "tdsearch",
"tdrec": "tdrec",
// jiosaavn
"jiosaavn": "jssearch",
"js": "jssearch",
"jssearch": "jssearch",
"jsrec": "jsrec"
};
var LavalinkPlugins = {
DuncteBot_Plugin: "DuncteBot-plugin",
LavaSrc: "lavasrc-plugin",
GoogleCloudTTS: "tts-plugin",
LavaSearch: "lavasearch-plugin",
Jiosaavn_Plugin: "jiosaavn-plugin",
LavalinkFilterPlugin: "lavalink-filter-plugin",
JavaTimedLyricsPlugin: "java-lyrics-plugin"
};
var SourceLinksRegexes = {
/** DEFAULT SUPPORTED BY LAVALINK */
YoutubeRegex: /https?:\/\/?(?:www\.)?(?:(m|www)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|shorts|playlist\?|watch\?v=|watch\?.+(?:&|&);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&)index=((?:\d){1,3}))?(?:(?:\?|&|&)?list=([a-zA-Z\-_0-9]{34}))?(?:\S+)?/,
YoutubeMusicRegex: /https?:\/\/?(?:www\.)?(?:(music|m|www)\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|shorts|playlist\?|watch\?v=|watch\?.+(?:&|&);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&)index=((?:\d){1,3}))?(?:(?:\?|&|&)?list=([a-zA-Z\-_0-9]{34}))?(?:\S+)?/,
SoundCloudRegex: /https:\/\/(?:on\.)?soundcloud\.com\//,
SoundCloudMobileRegex: /https?:\/\/(soundcloud\.app\.goo\.gl)\/(\S+)/,
bandcamp: /https?:\/\/?(?:www\.)?([\d|\w]+)\.bandcamp\.com\/(\S+)/,
TwitchTv: /https?:\/\/?(?:www\.)?twitch\.tv\/\w+/,
vimeo: /https?:\/\/(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|)(\d+)(?:|\/\?)/,
mp3Url: /(https?|ftp|file):\/\/(www.)?(.*?)\.(mp3)$/,
m3uUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(m3u)$/,
m3u8Url: /(https?|ftp|file):\/\/(www.)?(.*?)\.(m3u8)$/,
mp4Url: /(https?|ftp|file):\/\/(www.)?(.*?)\.(mp4)$/,
m4aUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(m4a)$/,
wavUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(wav)$/,
aacpUrl: /(https?|ftp|file):\/\/(www.)?(.*?)\.(aacp)$/,
/** FROM LAVA SOURCE */
DeezerTrackRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?track\/(\d+)/,
DeezerPageLinkRegex: /(https?:\/\/|)?(?:www\.)?deezer\.page\.link\/(\S+)/,
DeezerPlaylistRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?playlist\/(\d+)/,
DeezerAlbumRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?album\/(\d+)/,
DeezerArtistRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?artist\/(\d+)/,
DeezerMixesRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?mixes\/genre\/(\d+)/,
DeezerEpisodeRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?episode\/(\d+)/,
// DeezerPodcastRegex: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?podcast\/(\d+)/,
AllDeezerRegexWithoutPageLink: /(https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|playlist|album|artist|mixes\/genre|episode)\/(\d+)/,
AllDeezerRegex: /((https?:\/\/|)?(?:www\.)?deezer\.com\/(?:\w{2}\/)?(track|playlist|album|artist|mixes\/genre|episode)\/(\d+)|(https?:\/\/|)?(?:www\.)?deezer\.page\.link\/(\S+))/,
SpotifySongRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?track\/(?<identifier>[a-zA-Z0-9-_]+)/,
SpotifyPlaylistRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?playlist\/(?<identifier>[a-zA-Z0-9-_]+)/,
SpotifyArtistRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?artist\/(?<identifier>[a-zA-Z0-9-_]+)/,
SpotifyEpisodeRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?episode\/(?<identifier>[a-zA-Z0-9-_]+)/,
SpotifyShowRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?show\/(?<identifier>[a-zA-Z0-9-_]+)/,
SpotifyAlbumRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?album\/(?<identifier>[a-zA-Z0-9-_]+)/,
AllSpotifyRegex: /(https?:\/\/)(www\.)?open\.spotify\.com\/((?<region>[a-zA-Z-]+)\/)?(user\/(?<user>[a-zA-Z0-9-_]+)\/)?(?<type>track|album|playlist|artist|episode|show)\/(?<identifier>[a-zA-Z0-9-_]+)/,
appleMusic: /https?:\/\/?(?:www\.)?music\.apple\.com\/(\S+)/,
/** From tidal */
tidal: /https?:\/\/?(?:www\.)?(?:tidal|listen)\.tidal\.com\/(?<type>track|album|playlist|artist)\/(?<identifier>[a-zA-Z0-9-_]+)/,
/** From jiosaavn-plugin */
jiosaavn: /(https?:\/\/)(www\.)?jiosaavn\.com\/(?<type>song|album|featured|artist)\/([a-zA-Z0-9-_/,]+)/,
/** FROM DUNCTE BOT PLUGIN */
tiktok: /https:\/\/www\.tiktok\.com\//,
mixcloud: /https:\/\/www\.mixcloud\.com\//,
musicYandex: /https:\/\/music\.yandex\.ru\//,
radiohost: /https?:\/\/[^.\s]+\.radiohost\.de\/(\S+)/
};
// src/structures/Utils.ts
var TrackSymbol = Symbol("LC-Track");
var UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
var QueueSymbol = Symbol("LC-Queue");
var NodeSymbol = Symbol("LC-Node");
var escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
function parseLavalinkConnUrl(connectionUrl) {
if (!connectionUrl.startsWith("lavalink://")) throw new Error(`ConnectionUrl (${connectionUrl}) must start with 'lavalink://'`);
const parsed = new import_node_url.URL(connectionUrl);
return {
authorization: parsed.password,
id: parsed.username,
host: parsed.hostname,
port: Number(parsed.port)
};
}
var ManagerUtils = class {
LavalinkManager = void 0;
constructor(LavalinkManager2) {
this.LavalinkManager = LavalinkManager2;
}
buildPluginInfo(data, clientData = {}) {
return {
clientData,
...data.pluginInfo || data.plugin
};
}
buildTrack(data, requester) {
if (!data?.encoded || typeof data.encoded !== "string") throw new RangeError("Argument 'data.encoded' must be present.");
if (!data.info) throw new RangeError("Argument 'data.info' must be present.");
try {
let transformedRequester = typeof requester === "object" ? this.getTransformedRequester(requester) : void 0;
if (!transformedRequester && typeof data?.userData?.requester === "object" && data.userData.requester !== null) {
transformedRequester = this.getTransformedRequester(data.userData.requester);
}
const r = {
encoded: data.encoded,
info: {
identifier: data.info.identifier,
title: data.info.title,
author: data.info.author,
duration: data.info?.duration || data.info?.length,
artworkUrl: data.info.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
uri: data.info.uri,
sourceName: data.info.sourceName,
isSeekable: data.info.isSeekable,
isStream: data.info.isStream,
isrc: data.info.isrc
},
userData: {
...data.userData,
requester: transformedRequester
},
pluginInfo: this.buildPluginInfo(data, "clientData" in data ? data.clientData : {}),
requester: transformedRequester || this.getTransformedRequester(this.LavalinkManager?.options?.client)
};
Object.defineProperty(r, TrackSymbol, { configurable: true, value: true });
return r;
} catch (error) {
if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
this.LavalinkManager?.emit("debug", "BuildTrackError" /* BuildTrackError */, {
error,
functionLayer: "ManagerUtils > buildTrack()",
message: "Error while building track",
state: "error"
});
}
throw new RangeError(`Argument "data" is not a valid track: ${error.message}`);
}
}
/**
* Builds a UnresolvedTrack to be resolved before being played .
* @param query
* @param requester
*/
buildUnresolvedTrack(query, requester) {
if (typeof query === "undefined")
throw new RangeError('Argument "query" must be present.');
const unresolvedTrack = {
encoded: query.encoded || void 0,
info: query.info ? query.info : query.title ? query : void 0,
pluginInfo: this.buildPluginInfo(query),
requester: this.getTransformedRequester(requester),
async resolve(player) {
const closest = await getClosestTrack(this, player);
if (!closest) throw new SyntaxError("No closest Track found");
for (const prop of Object.getOwnPropertyNames(this)) delete this[prop];
delete this[UnresolvedTrackSymbol];
Object.defineProperty(this, TrackSymbol, { configurable: true, value: true });
return Object.assign(this, closest);
}
};
if (!this.isUnresolvedTrack(unresolvedTrack)) throw SyntaxError("Could not build Unresolved Track");
Object.defineProperty(unresolvedTrack, UnresolvedTrackSymbol, { configurable: true, value: true });
return unresolvedTrack;
}
/**
* Validate if a data is equal to a node
* @param data
*/
isNode(data) {
if (!data) return false;
const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(data));
if (!keys.includes("constructor")) return false;
if (!keys.length) return false;
if (!["connect", "destroy", "destroyPlayer", "fetchAllPlayers", "fetchInfo", "fetchPlayer", "fetchStats", "fetchVersion", "request", "updatePlayer", "updateSession"].every((v) => keys.includes(v))) return false;
return true;
}
getTransformedRequester(requester) {
try {
return typeof this.LavalinkManager?.options?.playerOptions?.requesterTransformer === "function" ? this.LavalinkManager?.options?.playerOptions?.requesterTransformer(requester) : requester;
} catch (e) {
if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
this.LavalinkManager?.emit("debug", "TransformRequesterFunctionFailed" /* TransformRequesterFunctionFailed */, {
error: e,
functionLayer: "ManagerUtils > getTransformedRequester()",
message: "Your custom transformRequesterFunction failed to execute, please check your function for errors.",
state: "error"
});
}
return requester;
}
}
/**
* Validate if a data is equal to node options
* @param data
*/
isNodeOptions(data) {
if (!data || typeof data !== "object" || Array.isArray(data)) return false;
if (typeof data.host !== "string" || !data.host.length) return false;
if (typeof data.port !== "number" || isNaN(data.port) || data.port < 0 || data.port > 65535) return false;
if (typeof data.authorization !== "string" || !data.authorization.length) return false;
if ("secure" in data && typeof data.secure !== "boolean" && data.secure !== void 0) return false;
if ("sessionId" in data && typeof data.sessionId !== "string" && data.sessionId !== void 0) return false;
if ("id" in data && typeof data.id !== "string" && data.id !== void 0) return false;
if ("regions" in data && (!Array.isArray(data.regions) || !data.regions.every((v) => typeof v === "string") && data.regions !== void 0)) return false;
if ("poolOptions" in data && typeof data.poolOptions !== "object" && data.poolOptions !== void 0) return false;
if ("retryAmount" in data && (typeof data.retryAmount !== "number" || isNaN(data.retryAmount) || data.retryAmount <= 0 && data.retryAmount !== void 0)) return false;
if ("retryDelay" in data && (typeof data.retryDelay !== "number" || isNaN(data.retryDelay) || data.retryDelay <= 0 && data.retryDelay !== void 0)) return false;
if ("requestTimeout" in data && (typeof data.requestTimeout !== "number" || isNaN(data.requestTimeout) || data.requestTimeout <= 0 && data.requestTimeout !== void 0)) return false;
return true;
}
/**
* Validate if a data is equal to a track
* @param data the Track to validate
* @returns
*/
isTrack(data) {
if (!data) return false;
if (data[TrackSymbol] === true) return true;
return typeof data?.encoded === "string" && typeof data?.info === "object" && !("resolve" in data);
}
/**
* Checks if the provided argument is a valid UnresolvedTrack.
* @param track
*/
isUnresolvedTrack(data) {
if (!data) return false;
if (data[UnresolvedTrackSymbol] === true) return true;
return typeof data === "object" && ("info" in data && typeof data.info.title === "string" || typeof data.encoded === "string") && "resolve" in data && typeof data.resolve === "function";
}
/**
* Checks if the provided argument is a valid UnresolvedTrack.
* @param track
*/
isUnresolvedTrackQuery(data) {
return typeof data === "object" && !("info" in data) && typeof data.title === "string";
}
async getClosestTrack(data, player) {
try {
return getClosestTrack(data, player);
} catch (e) {
if (this.LavalinkManager?.options?.advancedOptions?.enableDebugEvents) {
this.LavalinkManager?.emit("debug", "GetClosestTrackFailed" /* GetClosestTrackFailed */, {
error: e,
functionLayer: "ManagerUtils > getClosestTrack()",
message: "Failed to resolve track because the getClosestTrack function failed.",
state: "error"
});
}
throw e;
}
}
validateQueryString(node, queryString, sourceString) {
if (!node.info) throw new Error("No Lavalink Node was provided");
if (!node.info.sourceManagers?.length) throw new Error("Lavalink Node, has no sourceManagers enabled");
if (!queryString.trim().length) throw new Error(`Query string is empty, please provide a valid query string.`);
if (sourceString === "speak" && queryString.length > 100) throw new Error(`Query is speak, which is limited to 100 characters.`);
if (this.LavalinkManager.options?.linksBlacklist?.length > 0) {
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
this.LavalinkManager.emit("debug", "ValidatingBlacklistLinks" /* ValidatingBlacklistLinks */, {
state: "log",
message: `Validating Query against LavalinkManager.options.linksBlacklist, query: "${queryString}"`,
functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()"
});
}
if (this.LavalinkManager.options?.linksBlacklist.some((v) => typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase())) || (0, import_types.isRegExp)(v) && v.test(queryString))) {
throw new Error(`Query string contains a link / word which is blacklisted.`);
}
}
if (!/^https?:\/\//.test(queryString)) return;
else if (this.LavalinkManager.options?.linksAllowed === false) throw new Error("Using links to make a request is not allowed.");
if (this.LavalinkManager.options?.linksWhitelist?.length > 0) {
if (this.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
this.LavalinkManager.emit("debug", "ValidatingWhitelistLinks" /* ValidatingWhitelistLinks */, {
state: "log",
message: `Link was provided to the Query, validating against LavalinkManager.options.linksWhitelist, query: "${queryString}"`,
functionLayer: "(LavalinkNode > node | player) > search() > validateQueryString()"
});
}
if (!this.LavalinkManager.options?.linksWhitelist.some((v) => typeof v === "string" && (queryString.toLowerCase().includes(v.toLowerCase()) || v.toLowerCase().includes(queryString.toLowerCase())) || (0, import_types.isRegExp)(v) && v.test(queryString))) {
throw new Error(`Query string contains a link / word which isn't whitelisted.`);
}
}
if ((SourceLinksRegexes.YoutubeMusicRegex.test(queryString) || SourceLinksRegexes.YoutubeRegex.test(queryString)) && !node.info?.sourceManagers?.includes("youtube")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'youtube' enabled");
}
if ((SourceLinksRegexes.SoundCloudMobileRegex.test(queryString) || SourceLinksRegexes.SoundCloudRegex.test(queryString)) && !node.info?.sourceManagers?.includes("soundcloud")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'soundcloud' enabled");
}
if (SourceLinksRegexes.bandcamp.test(queryString) && !node.info?.sourceManagers?.includes("bandcamp")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'bandcamp' enabled (introduced with lavaplayer 2.2.0 or lavalink 4.0.6)");
}
if (SourceLinksRegexes.TwitchTv.test(queryString) && !node.info?.sourceManagers?.includes("twitch")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'twitch' enabled");
}
if (SourceLinksRegexes.vimeo.test(queryString) && !node.info?.sourceManagers?.includes("vimeo")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'vimeo' enabled");
}
if (SourceLinksRegexes.tiktok.test(queryString) && !node.info?.sourceManagers?.includes("tiktok")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tiktok' enabled");
}
if (SourceLinksRegexes.mixcloud.test(queryString) && !node.info?.sourceManagers?.includes("mixcloud")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'mixcloud' enabled");
}
if (SourceLinksRegexes.AllSpotifyRegex.test(queryString) && !node.info?.sourceManagers?.includes("spotify")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'spotify' enabled");
}
if (SourceLinksRegexes.appleMusic.test(queryString) && !node.info?.sourceManagers?.includes("applemusic")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'applemusic' enabled");
}
if (SourceLinksRegexes.AllDeezerRegex.test(queryString) && !node.info?.sourceManagers?.includes("deezer")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'deezer' enabled");
}
if (SourceLinksRegexes.musicYandex.test(queryString) && !node.info?.sourceManagers?.includes("yandexmusic")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'yandexmusic' enabled");
}
if (SourceLinksRegexes.jiosaavn.test(queryString) && !node.info?.sourceManagers?.includes("jiosaavn")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled");
}
if (SourceLinksRegexes.tidal.test(queryString) && !node.info?.sourceManagers?.includes("tidal")) {
throw new Error("Query / Link Provided for this Source but Lavalink Node has not 'tidal' enabled");
}
return;
}
transformQuery(query) {
const sourceOfQuery = typeof query === "string" ? void 0 : DefaultSources[query.source?.trim?.()?.toLowerCase?.() ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? query.source?.trim?.()?.toLowerCase?.();
const Query = {
query: typeof query === "string" ? query : query.query,
extraQueryUrlParams: typeof query !== "string" ? query.extraQueryUrlParams : void 0,
source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
};
const foundSource = Object.keys(DefaultSources).find((source) => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
if (foundSource && !["https", "http"].includes(foundSource) && DefaultSources[foundSource]) {
Query.source = DefaultSources[foundSource];
Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
}
return Query;
}
transformLavaSearchQuery(query) {
const sourceOfQuery = typeof query === "string" ? void 0 : DefaultSources[query.source?.trim?.()?.toLowerCase?.() ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()] ?? query.source?.trim?.()?.toLowerCase?.();
const Query = {
query: typeof query === "string" ? query : query.query,
types: query.types ? ["track", "playlist", "artist", "album", "text"].filter((v) => query.types?.find((x) => x.toLowerCase().startsWith(v))) : [
"track",
"playlist",
"artist",
"album"
/*"text"*/
],
source: sourceOfQuery ?? this.LavalinkManager?.options?.playerOptions?.defaultSearchPlatform?.toLowerCase?.()
};
const foundSource = Object.keys(DefaultSources).find((source) => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
if (foundSource && DefaultSources[foundSource]) {
Query.source = DefaultSources[foundSource];
Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length);
}
return Query;
}
validateSourceString(node, sourceString) {
if (!sourceString) throw new Error(`No SourceString was provided`);
const source = DefaultSources[sourceString.toLowerCase().trim()];
if (!source) throw new Error(`Lavalink Node SearchQuerySource: '${sourceString}' is not available`);
if (!node.info) throw new Error("Lavalink Node does not have any info cached yet, not ready yet!");
if (source === "amsearch" && !node.info?.sourceManagers?.includes("applemusic")) {
throw new Error("Lavalink Node has not 'applemusic' enabled, which is required to have 'amsearch' work");
}
if (source === "dzisrc" && !node.info?.sourceManagers?.includes("deezer")) {
throw new Error("Lavalink Node has not 'deezer' enabled, which is required to have 'dzisrc' work");
}
if (source === "dzsearch" && !node.info?.sourceManagers?.includes("deezer")) {
throw new Error("Lavalink Node has not 'deezer' enabled, which is required to have 'dzsearch' work");
}
if (source === "dzisrc" && node.info?.sourceManagers?.includes("deezer") && !node.info?.sourceManagers?.includes("http")) {
throw new Error("Lavalink Node has not 'http' enabled, which is required to have 'dzisrc' to work");
}
if (source === "jsrec" && !node.info?.sourceManagers?.includes("jiosaavn")) {
throw new Error("Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jsrec' to work");
}
if (source === "jssearch" && !node.info?.sourceManagers?.includes("jiosaavn")) {
throw new Error("Lavalink Node has not 'jiosaavn' (via jiosaavn-plugin) enabled, which is required to have 'jssearch' to work");
}
if (source === "scsearch" && !node.info?.sourceManagers?.includes("soundcloud")) {
throw new Error("Lavalink Node has not 'soundcloud' enabled, which is required to have 'scsearch' work");
}
if (source === "speak" && !node.info?.plugins?.find((c) => c.name.toLowerCase().includes(LavalinkPlugins.DuncteBot_Plugin.toLowerCase()))) {
throw new Error("Lavalink Node has not 'speak' enabled, which is required to have 'speak' work");
}
if (source === "tdsearch" && !node.info?.sourceManagers?.includes("tidal")) {
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdsearch' work");
}
if (source === "tdrec" && !node.info?.sourceManagers?.includes("tidal")) {
throw new Error("Lavalink Node has not 'tidal' enabled, which is required to have 'tdrec' work");
}
if (source === "tts" && !node.info?.plugins?.find((c) => c.name.toLowerCase().includes(LavalinkPlugins.GoogleCloudTTS.toLowerCase()))) {
throw new Error("Lavalink Node has not 'tts' enabled, which is required to have 'tts' work");
}
if (source === "ftts" && !(node.info?.sourceManagers?.includes("ftts") || node.info?.sourceManagers?.includes("flowery-tts") || node.info?.sourceManagers?.includes("flowerytts"))) {
throw new Error("Lavalink Node has not 'flowery-tts' enabled, which is required to have 'ftts' work");
}
if (source === "ymsearch" && !node.info?.sourceManagers?.includes("yandexmusic")) {
throw new Error("Lavalink Node has not 'yandexmusic' enabled, which is required to have 'ymsearch' work");
}
if (source === "ytmsearch" && !node.info.sourceManagers?.includes("youtube")) {
throw new Error("Lavalink Node has not 'youtube' enabled, which is required to have 'ytmsearch' work");
}
if (source === "ytsearch" && !node.info?.sourceManagers?.includes("youtube")) {
throw new Error("Lavalink Node has not 'youtube' enabled, which is required to have 'ytsearch' work");
}
if (source === "vksearch" && !node.info?.sourceManagers?.includes("vkmusic")) {
throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vksearch' work");
}
if (source === "vkrec" && !node.info?.sourceManagers?.includes("vkmusic")) {
throw new Error("Lavalink Node has not 'vkmusic' enabled, which is required to have 'vkrec' work");
}
if (source === "qbsearch" && !node.info?.sourceManagers?.includes("qobuz")) {
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbsearch' work");
}
if (source === "qbisrc" && !node.info?.sourceManagers?.includes("qobuz")) {
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbisrc' work");
}
if (source === "qbrec" && !node.info?.sourceManagers?.includes("qobuz")) {
throw new Error("Lavalink Node has not 'qobuz' enabled, which is required to have 'qbrec' work");
}
return;
}
};
var MiniMap = class extends Map {
constructor(data = []) {
super(data);
}
filter(fn, thisArg) {
if (typeof thisArg !== "undefined") fn = fn.bind(thisArg);
const results = new this.constructor[Symbol.species]();
for (const [key, val] of this) {
if (fn(val, key, this)) results.set(key, val);
}
return results;
}
toJSON() {
return [...this.entries()];
}
map(fn, thisArg) {
if (typeof thisArg !== "undefined") fn = fn.bind(thisArg);
const iter = this.entries();
return Array.from({ length: this.size }, () => {
const [key, value] = iter.next().value;
return fn(value, key, this);
});
}
};
async function queueTrackEnd(player, dontShiftQueue = false) {
if (player.queue.current && !player.queue.current?.pluginInfo?.clientData?.previousTrack) {
player.queue.previous.unshift(player.queue.current);
if (player.queue.previous.length > player.queue.options.maxPreviousTracks) player.queue.previous.splice(player.queue.options.maxPreviousTracks, player.queue.previous.length);
await player.queue.utils.save();
}
if (player.repeatMode === "queue" && player.queue.current) player.queue.tracks.push(player.queue.current);
const nextSong = dontShiftQueue ? null : player.queue.tracks.shift();
try {
if (nextSong && player.LavalinkManager.utils.isUnresolvedTrack(nextSong)) await nextSong.resolve(player);
player.queue.current = nextSong || null;
await player.queue.utils.save();
} catch (error) {
if (player.LavalinkManager.options?.advancedOptions?.enableDebugEvents) {
player.LavalinkManager.emit("debug", "PlayerPlayUnresolvedTrackFailed" /* PlayerPlayUnresolvedTrackFailed */, {
state: "error",
error,
message: `queueTrackEnd Util was called, tried to resolve the next track, but failed to find the closest matching song`,
functionLayer: "Player > play() > resolve currentTrack"
});
}
player.LavalinkManager.emit("trackError", player, player.queue.current, error);
if (!dontShiftQueue && player.LavalinkManager.options?.autoSkipOnResolveError === true && player.queue.tracks[0]) return queueTrackEnd(player);
}
return player.queue.current;
}
async function applyUnresolvedData(resTrack, data, utils) {
if (!resTrack?.info || !data?.info) return;
if (data.info.uri) resTrack.info.uri = data.info.uri;
if (utils?.LavalinkManager?.options?.playerOptions?.useUnresolvedData === true) {
if (data.info.artworkUrl?.length) resTrack.info.artworkUrl = data.info.artworkUrl;
if (data.info.title?.length) resTrack.info.title = data.info.title;
if (data.info.author?.length) resTrack.info.author = data.info.author;
} else {
if ((resTrack.info.title === "Unknown title" || resTrack.info.title === "Unspecified description") && resTrack.info.title != data.info.title) resTrack.info.title = data.info.title;
if (resTrack.info.author !== data.info.author) resTrack.info.author = data.info.author;
if (resTrack.info.artworkUrl !== data.info.artworkUrl) resTrack.info.artworkUrl = data.info.artworkUrl;
}
for (const key of Object.keys(data.info)) if (typeof resTrack.info[key] === "undefined" && key !== "resolve" && data.info[key]) resTrack.info[key] = data.info[key];
return resTrack;
}
async function getClosestTrack(data, player) {
if (!player || !player.node) throw new RangeError("No player with a lavalink node was provided");
if (player.LavalinkManager.utils.isTrack(data)) return player.LavalinkManager.utils.buildTrack(data, data.requester);
if (!player.LavalinkManager.utils.isUnresolvedTrack(data)) throw new RangeError("Track is not an unresolved Track");
if (!data?.info?.title && typeof data.encoded !== "string" && !data.info.uri) throw new SyntaxError("the track uri / title / encoded Base64 string is required for unresolved tracks");
if (!data.requester) throw new SyntaxError("The requester is required");
if (typeof data.encoded === "string") {
const r = await player.node.decode.singleTrack(data.encoded, data.requester);
if (r) return applyUnresolvedData(r, data, player.LavalinkManager.utils);
}
if (typeof data.info.uri === "string") {
const r = await player.search({ query: data?.info?.uri }, data.requester).then((v) => v.tracks?.[0]);
if (r) return applyUnresolvedData(r, data, player.LavalinkManager.utils);
}
const query = [data.info?.title, data.info?.author].filter((str) => !!str).join(" by ");
const sourceName = data.info?.sourceName;
return await player.search({
query,
source: sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform
}, data.requester).then((res) => {
let trackToUse = null;
if (data.info.author && !trackToUse) trackToUse = res.tracks.find((track) => [data.info?.author || "", `${data.info?.author} - Topic`].some((name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info?.author)) || new RegExp(`^${escapeRegExp(data.info?.title)}$`, "i").test(track.info?.title));
if (data.info.duration && !trackToUse) trackToUse = res.tracks.find((track) => track.info?.duration >= data.info?.duration - 1500 && track?.info.duration <= data.info?.duration + 1500);
if (data.info.isrc && !trackToUse) trackToUse = res.tracks.find((track) => track.info?.isrc === data.info?.isrc);
return applyUnresolvedData(trackToUse || res.tracks[0], data, player.LavalinkManager.utils);
});
}
function safeStringify(obj, padding = 0) {
const seen = /* @__PURE__ */ new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "function") return void 0;
if (typeof value === "symbol") return void 0;
if (typeof value === "bigint") return value.toString();
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
return value;
}, padding);
}
// src/structures/Node.ts
var LavalinkNode = class {
heartBeatPingTimestamp = 0;
heartBeatPongTimestamp = 0;
get heartBeatPing() {
return this.heartBeatPongTimestamp - this.heartBeatPingTimestamp;
}
heartBeatInterval;
pingTimeout;
isAlive = false;
/** The provided Options of the Node */
options;
/** The amount of rest calls the node has made. */
calls = 0;
/** Stats from lavalink, will be updated via an interval by lavalink. */
stats = {
players: 0,
playingPlayers: 0,
cpu: {
cores: 0,
lavalinkLoad: 0,
systemLoad: 0
},
memory: {
allocated: 0,
free: 0,
reservable: 0,
used: 0
},
uptime: 0,
frameStats: {
deficit: 0,
nulled: 0,
sent: 0
}
};
/** The current sessionId, only present when connected */
sessionId = null;
/** Wether the node resuming is enabled or not */
resuming = { enabled: true, timeout: null };
/** Actual Lavalink Information of the Node */
info = null;
/** The Node Manager of this Node */
NodeManager = null;
/** The Reconnection Timeout */
reconnectTimeout = void 0;
/** The Reconnection Attempt counter */
reconnectAttempts = 1;
/** The Socket of the Lavalink */
socket = null;
/** Version of what the Lavalink Server should be */
version = "v4";
/**
* Create a new Node
* @param options Lavalink Node Options
* @param manager Node Manager
*
*
* @example
* ```ts
* // don't create a node manually, instead use:
*
* client.lavalink.nodeManager.createNode(options)
* ```
*/
constructor(options, manager) {
this.options = {
secure: false,
retryAmount: 5,
retryDelay: 1e4,
requestSignalTimeoutMS: 1e4,
heartBeatInterval: 3e4,
closeOnError: true,
enablePingOnStatsCheck: true,
...options
};
this.NodeManager = manager;
this.validate();
if (this.options.secure && this.options.port !== 443) throw new SyntaxError("If secure is true, then the port must be 443");
this.options.regions = (this.options.regions || []).map((a) => a.toLowerCase());
Object.defineProperty(this, NodeSymbol, { configurable: true, value: true });
}
/**
* Raw Request util function
* @param endpoint endpoint string
* @param modify modify the request
* @param extraQueryUrlParams UrlSearchParams to use in a encodedURI, useful for example for flowertts
* @returns object containing request and option information
*
* @example
* ```ts
* player.node.rawRequest(`/loadtracks?identifier=Never gonna give you up`, (