UNPKG

lavalink-client

Version:

Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.

1,072 lines (1,063 loc) 231 kB
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\?.+(?:&|&#38;);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&#38;)index=((?:\d){1,3}))?(?:(?:\?|&|&#38;)?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\?.+(?:&|&#38;);v=))([a-zA-Z0-9\-_]{11})?(?:(?:\?|&|&#38;)index=((?:\d){1,3}))?(?:(?:\?|&|&#38;)?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`, (