distube 4.0.2 → 4.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,7 +22,10 @@ var __copyProps = (to, from, except, desc) => {
22
22
  }
23
23
  return to;
24
24
  };
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
26
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
30
  var __publicField = (obj, key, value) => {
28
31
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -56,15 +59,11 @@ var require_package = __commonJS({
56
59
  "package.json"(exports, module2) {
57
60
  module2.exports = {
58
61
  name: "distube",
59
- version: "4.0.2",
62
+ version: "4.0.4",
60
63
  description: "A Discord.js module to simplify your music commands and play songs with audio filters on Discord without any API key.",
61
64
  main: "./dist/index.js",
62
- module: "./dist/index.mjs",
63
65
  types: "./dist/index.d.ts",
64
- exports: {
65
- import: "./dist/index.mjs",
66
- require: "./dist/index.js"
67
- },
66
+ exports: "./dist/index.js",
68
67
  directories: {
69
68
  lib: "src",
70
69
  test: "tests"
@@ -74,7 +73,7 @@ var require_package = __commonJS({
74
73
  ],
75
74
  scripts: {
76
75
  test: "jest",
77
- docs: "docgen -s src/*.ts src/**/*.ts -o docs.json -c pages/index.yml -g -j jsdoc.config.json",
76
+ docs: "docgen -s src/**/*.ts -o docs.json -c pages/index.yml -g -j jsdoc.config.json",
78
77
  lint: "prettier --check . && eslint .",
79
78
  "lint:fix": "eslint . --fix",
80
79
  prettier: 'prettier --write "**/*.{ts,json,yml,yaml,md}"',
@@ -137,13 +136,13 @@ var require_package = __commonJS({
137
136
  "@commitlint/cli": "^17.0.3",
138
137
  "@commitlint/config-conventional": "^17.0.3",
139
138
  "@discordjs/voice": "^0.11.0",
140
- "@distube/docgen": "distubejs/docgen",
139
+ "@distubejs/docgen": "distubejs/docgen",
141
140
  "@types/jest": "^28.1.6",
142
- "@types/node": "^18.0.6",
143
- "@typescript-eslint/eslint-plugin": "^5.30.7",
144
- "@typescript-eslint/parser": "^5.30.7",
141
+ "@types/node": "^18.6.1",
142
+ "@typescript-eslint/eslint-plugin": "^5.31.0",
143
+ "@typescript-eslint/parser": "^5.31.0",
145
144
  "babel-jest": "^28.1.3",
146
- "discord.js": "^14.0.2",
145
+ "discord.js": "^14.0.3",
147
146
  eslint: "^8.20.0",
148
147
  "eslint-config-distube": "^1.6.4",
149
148
  "eslint-config-prettier": "^8.5.0",
@@ -153,10 +152,10 @@ var require_package = __commonJS({
153
152
  jest: "^28.1.3",
154
153
  "jsdoc-babel": "^0.5.0",
155
154
  "nano-staged": "^0.8.0",
156
- "npm-check-updates": "^15.3.4",
155
+ "npm-check-updates": "^16.0.0",
157
156
  pinst: "^3.0.0",
158
157
  prettier: "^2.7.1",
159
- tsup: "^6.1.3",
158
+ tsup: "^6.2.0",
160
159
  typescript: "^4.7.4"
161
160
  },
162
161
  peerDependencies: {
@@ -199,6 +198,7 @@ __export(src_exports, {
199
198
  DisTubeStream: () => DisTubeStream,
200
199
  DisTubeVoice: () => DisTubeVoice,
201
200
  DisTubeVoiceManager: () => DisTubeVoiceManager,
201
+ Events: () => Events,
202
202
  ExtractorPlugin: () => ExtractorPlugin,
203
203
  FilterManager: () => FilterManager,
204
204
  GuildIdManager: () => GuildIdManager,
@@ -233,6 +233,7 @@ __export(src_exports, {
233
233
  isTextChannelInstance: () => isTextChannelInstance,
234
234
  isURL: () => isURL,
235
235
  isVoiceChannelEmpty: () => isVoiceChannelEmpty,
236
+ objectKeys: () => objectKeys,
236
237
  parseNumber: () => parseNumber,
237
238
  resolveGuildId: () => resolveGuildId,
238
239
  toSecond: () => toSecond,
@@ -262,6 +263,25 @@ var StreamType = /* @__PURE__ */ ((StreamType2) => {
262
263
  StreamType2[StreamType2["RAW"] = 1] = "RAW";
263
264
  return StreamType2;
264
265
  })(StreamType || {});
266
+ var Events = /* @__PURE__ */ ((Events2) => {
267
+ Events2["ERROR"] = "error";
268
+ Events2["ADD_LIST"] = "addList";
269
+ Events2["ADD_SONG"] = "addSong";
270
+ Events2["PLAY_SONG"] = "playSong";
271
+ Events2["FINISH_SONG"] = "finishSong";
272
+ Events2["EMPTY"] = "empty";
273
+ Events2["FINISH"] = "finish";
274
+ Events2["INIT_QUEUE"] = "initQueue";
275
+ Events2["NO_RELATED"] = "noRelated";
276
+ Events2["DISCONNECT"] = "disconnect";
277
+ Events2["DELETE_QUEUE"] = "deleteQueue";
278
+ Events2["SEARCH_CANCEL"] = "searchCancel";
279
+ Events2["SEARCH_NO_RESULT"] = "searchNoResult";
280
+ Events2["SEARCH_DONE"] = "searchDone";
281
+ Events2["SEARCH_INVALID_ANSWER"] = "searchInvalidAnswer";
282
+ Events2["SEARCH_RESULT"] = "searchResult";
283
+ return Events2;
284
+ })(Events || {});
265
285
 
266
286
  // src/constant.ts
267
287
  var defaultFilters = {
@@ -698,6 +718,7 @@ var DisTubeBase = class {
698
718
  __name(DisTubeBase, "DisTubeBase");
699
719
 
700
720
  // src/core/DisTubeVoice.ts
721
+ var import_discord = require("discord.js");
701
722
  var import_tiny_typed_emitter = require("tiny-typed-emitter");
702
723
  var import_voice = require("@discordjs/voice");
703
724
  var _channel, _volume, _br, br_fn, _join, join_fn;
@@ -751,7 +772,23 @@ var DisTubeVoice = class extends import_tiny_typed_emitter.TypedEmitter {
751
772
  }).on("error", () => void 0);
752
773
  this.connection.subscribe(this.audioPlayer);
753
774
  }
775
+ get channelId() {
776
+ return this.connection?.joinConfig?.channelId ?? void 0;
777
+ }
754
778
  get channel() {
779
+ if (!this.channelId)
780
+ return __privateGet(this, _channel);
781
+ if (__privateGet(this, _channel)?.id === this.channelId)
782
+ return __privateGet(this, _channel);
783
+ const channel = this.voices.client.channels.cache.get(this.channelId);
784
+ if (!channel)
785
+ return __privateGet(this, _channel);
786
+ for (const type of import_discord.Constants.VoiceBasedChannelTypes) {
787
+ if (channel.type === type) {
788
+ __privateSet(this, _channel, channel);
789
+ return channel;
790
+ }
791
+ }
755
792
  return __privateGet(this, _channel);
756
793
  }
757
794
  set channel(channel) {
@@ -762,7 +799,7 @@ var DisTubeVoice = class extends import_tiny_typed_emitter.TypedEmitter {
762
799
  throw new DisTubeError("VOICE_DIFFERENT_GUILD");
763
800
  if (channel.client.user?.id !== this.voices.client.user?.id)
764
801
  throw new DisTubeError("VOICE_DIFFERENT_CLIENT");
765
- if (channel.id === __privateGet(this, _channel)?.id)
802
+ if (channel.id === this.channelId)
766
803
  return;
767
804
  if (!channel.joinable) {
768
805
  if (channel.full)
@@ -886,538 +923,222 @@ join_fn = /* @__PURE__ */ __name(function(channel) {
886
923
  });
887
924
  }, "#join");
888
925
 
889
- // src/core/manager/BaseManager.ts
890
- var import_discord = require("discord.js");
891
- var BaseManager = class extends DisTubeBase {
892
- constructor() {
893
- super(...arguments);
894
- __publicField(this, "collection", new import_discord.Collection());
926
+ // src/core/DisTubeHandler.ts
927
+ var import_ytpl = __toESM(require("@distube/ytpl"));
928
+ var import_ytdl_core = __toESM(require("@distube/ytdl-core"));
929
+ var DisTubeHandler = class extends DisTubeBase {
930
+ constructor(distube) {
931
+ super(distube);
932
+ const client = this.client;
933
+ if (this.options.leaveOnEmpty) {
934
+ client.on("voiceStateUpdate", (oldState) => {
935
+ if (!oldState?.channel)
936
+ return;
937
+ const queue = this.queues.get(oldState);
938
+ if (!queue) {
939
+ if (isVoiceChannelEmpty(oldState)) {
940
+ setTimeout(() => {
941
+ if (!this.queues.get(oldState) && isVoiceChannelEmpty(oldState))
942
+ this.voices.leave(oldState);
943
+ }, this.options.emptyCooldown * 1e3).unref();
944
+ }
945
+ return;
946
+ }
947
+ if (queue._emptyTimeout) {
948
+ clearTimeout(queue._emptyTimeout);
949
+ delete queue._emptyTimeout;
950
+ }
951
+ if (isVoiceChannelEmpty(oldState)) {
952
+ queue._emptyTimeout = setTimeout(() => {
953
+ delete queue._emptyTimeout;
954
+ if (isVoiceChannelEmpty(oldState)) {
955
+ queue.voice.leave();
956
+ this.emit("empty", queue);
957
+ if (queue.stopped)
958
+ queue.remove();
959
+ }
960
+ }, this.options.emptyCooldown * 1e3).unref();
961
+ }
962
+ });
963
+ }
895
964
  }
896
- get size() {
897
- return this.collection.size;
965
+ get ytdlOptions() {
966
+ const options = this.options.ytdlOptions;
967
+ if (this.options.youtubeCookie) {
968
+ if (!options.requestOptions)
969
+ options.requestOptions = {};
970
+ if (!options.requestOptions.headers)
971
+ options.requestOptions.headers = {};
972
+ options.requestOptions.headers.cookie = this.options.youtubeCookie;
973
+ if (this.options.youtubeIdentityToken) {
974
+ options.requestOptions.headers["x-youtube-identity-token"] = this.options.youtubeIdentityToken;
975
+ }
976
+ }
977
+ return options;
898
978
  }
899
- };
900
- __name(BaseManager, "BaseManager");
901
-
902
- // src/core/manager/GuildIdManager.ts
903
- var GuildIdManager = class extends BaseManager {
904
- add(idOrInstance, data) {
905
- const id = resolveGuildId(idOrInstance);
906
- const existing = this.get(id);
907
- if (existing)
908
- return this;
909
- return this.collection.set(id, data);
979
+ getYouTubeInfo(url, basic = false) {
980
+ if (basic)
981
+ return import_ytdl_core.default.getBasicInfo(url, this.ytdlOptions);
982
+ return import_ytdl_core.default.getInfo(url, this.ytdlOptions);
910
983
  }
911
- get(idOrInstance) {
912
- return this.collection.get(resolveGuildId(idOrInstance));
984
+ async resolve(song, options = {}) {
985
+ if (song instanceof Song || song instanceof Playlist) {
986
+ if ("metadata" in options)
987
+ song.metadata = options.metadata;
988
+ if ("member" in options)
989
+ song.member = options.member;
990
+ return song;
991
+ }
992
+ if (song instanceof SearchResultVideo)
993
+ return new Song(song, options);
994
+ if (song instanceof SearchResultPlaylist)
995
+ return this.resolvePlaylist(song.url, options);
996
+ if (isObject(song)) {
997
+ if (!("url" in song) && !("id" in song))
998
+ throw new DisTubeError("CANNOT_RESOLVE_SONG", song);
999
+ return new Song(song, options);
1000
+ }
1001
+ if (import_ytpl.default.validateID(song))
1002
+ return this.resolvePlaylist(song, options);
1003
+ if (import_ytdl_core.default.validateURL(song))
1004
+ return new Song(await this.getYouTubeInfo(song, true), options);
1005
+ if (isURL(song)) {
1006
+ for (const plugin of this.distube.extractorPlugins) {
1007
+ if (await plugin.validate(song))
1008
+ return plugin.resolve(song, options);
1009
+ }
1010
+ throw new DisTubeError("NOT_SUPPORTED_URL");
1011
+ }
1012
+ throw new DisTubeError("CANNOT_RESOLVE_SONG", song);
913
1013
  }
914
- remove(idOrInstance) {
915
- return this.collection.delete(resolveGuildId(idOrInstance));
1014
+ async resolvePlaylist(playlist, options = {}) {
1015
+ const { member, source, metadata } = { source: "youtube", ...options };
1016
+ if (playlist instanceof Playlist) {
1017
+ if ("metadata" in options)
1018
+ playlist.metadata = metadata;
1019
+ if ("member" in options)
1020
+ playlist.member = member;
1021
+ return playlist;
1022
+ }
1023
+ if (typeof playlist === "string") {
1024
+ const info = await (0, import_ytpl.default)(playlist, { limit: Infinity });
1025
+ const songs = info.items.filter((v) => !v.thumbnail.includes("no_thumbnail")).map((v) => new Song(v, { member, metadata }));
1026
+ return new Playlist(
1027
+ {
1028
+ source,
1029
+ songs,
1030
+ member,
1031
+ name: info.title,
1032
+ url: info.url,
1033
+ thumbnail: songs[0].thumbnail
1034
+ },
1035
+ { metadata }
1036
+ );
1037
+ }
1038
+ return new Playlist(playlist, { member, properties: { source }, metadata });
916
1039
  }
917
- has(idOrInstance) {
918
- return this.collection.has(resolveGuildId(idOrInstance));
1040
+ async searchSong(message, query) {
1041
+ if (!isMessageInstance(message))
1042
+ throw new DisTubeError("INVALID_TYPE", "Discord.Message", message, "message");
1043
+ if (typeof query !== "string")
1044
+ throw new DisTubeError("INVALID_TYPE", "string", query, "query");
1045
+ if (query.length === 0)
1046
+ throw new DisTubeError("EMPTY_STRING", "query");
1047
+ const limit = this.options.searchSongs > 1 ? this.options.searchSongs : 1;
1048
+ const results = await this.distube.search(query, {
1049
+ limit,
1050
+ safeSearch: this.options.nsfw ? false : !message.channel?.nsfw
1051
+ }).catch(() => {
1052
+ if (!this.emit("searchNoResult", message, query)) {
1053
+ console.warn("searchNoResult event does not have any listeners! Emits `error` event instead.");
1054
+ throw new DisTubeError("NO_RESULT");
1055
+ }
1056
+ });
1057
+ if (!results)
1058
+ return null;
1059
+ return this.createSearchMessageCollector(message, results, query);
919
1060
  }
920
- };
921
- __name(GuildIdManager, "GuildIdManager");
922
-
923
- // src/core/manager/DisTubeVoiceManager.ts
924
- var import_voice2 = require("@discordjs/voice");
925
- var DisTubeVoiceManager = class extends GuildIdManager {
926
- create(channel) {
927
- const existing = this.get(channel.guildId);
928
- if (existing) {
929
- existing.channel = channel;
930
- return existing;
1061
+ async createSearchMessageCollector(message, results, query) {
1062
+ if (!isMessageInstance(message))
1063
+ throw new DisTubeError("INVALID_TYPE", "Discord.Message", message, "message");
1064
+ if (!Array.isArray(results) || results.length == 0) {
1065
+ throw new DisTubeError("INVALID_TYPE", "Array<SearchResult|Song|Playlist>", results, "results");
931
1066
  }
932
- return new DisTubeVoice(this, channel);
933
- }
934
- join(channel) {
935
- const existing = this.get(channel.guildId);
936
- if (existing)
937
- return existing.join(channel);
938
- return this.create(channel).join();
1067
+ if (this.options.searchSongs > 1) {
1068
+ const searchEvents = [
1069
+ "searchNoResult",
1070
+ "searchResult",
1071
+ "searchCancel",
1072
+ "searchInvalidAnswer",
1073
+ "searchDone"
1074
+ ];
1075
+ for (const evn of searchEvents) {
1076
+ if (this.distube.listenerCount(evn) === 0) {
1077
+ console.warn(`"searchSongs" option is disabled due to missing "${evn}" listener.`);
1078
+ console.warn(
1079
+ `If you don't want to use "${evn}" event, simply add an empty listener (not recommended):
1080
+ <DisTube>.on("${evn}", () => {})`
1081
+ );
1082
+ this.options.searchSongs = 0;
1083
+ }
1084
+ }
1085
+ }
1086
+ const limit = this.options.searchSongs > 1 ? this.options.searchSongs : 1;
1087
+ let result = results[0];
1088
+ if (limit > 1) {
1089
+ results.splice(limit);
1090
+ this.emit("searchResult", message, results, query);
1091
+ const c = message.channel;
1092
+ const answers = await c.awaitMessages({
1093
+ filter: (m) => m.author.id === message.author.id,
1094
+ max: 1,
1095
+ time: this.options.searchCooldown * 1e3,
1096
+ errors: ["time"]
1097
+ }).catch(() => void 0);
1098
+ const ans = answers?.first();
1099
+ if (!ans) {
1100
+ this.emit("searchCancel", message, query);
1101
+ return null;
1102
+ }
1103
+ const index = parseInt(ans.content, 10);
1104
+ if (isNaN(index) || index > results.length || index < 1) {
1105
+ this.emit("searchInvalidAnswer", message, ans, query);
1106
+ return null;
1107
+ }
1108
+ this.emit("searchDone", message, ans, query);
1109
+ result = results[index - 1];
1110
+ }
1111
+ return result;
939
1112
  }
940
- leave(guild) {
941
- const voice = this.get(guild);
942
- if (voice) {
943
- voice.leave();
1113
+ async playPlaylist(voiceChannel, playlist, options = {}) {
1114
+ const { textChannel, skip } = { skip: false, ...options };
1115
+ const position = Number(options.position) || (skip ? 1 : 0);
1116
+ if (!(playlist instanceof Playlist))
1117
+ throw new DisTubeError("INVALID_TYPE", "Playlist", playlist, "playlist");
1118
+ const queue = this.queues.get(voiceChannel);
1119
+ if (!this.options.nsfw && !(queue?.textChannel || textChannel)?.nsfw) {
1120
+ playlist.songs = playlist.songs.filter((s) => !s.age_restricted);
1121
+ }
1122
+ if (!playlist.songs.length) {
1123
+ if (!this.options.nsfw && !textChannel?.nsfw) {
1124
+ throw new DisTubeError("EMPTY_FILTERED_PLAYLIST");
1125
+ }
1126
+ throw new DisTubeError("EMPTY_PLAYLIST");
1127
+ }
1128
+ if (queue) {
1129
+ if (this.options.joinNewVoiceChannel)
1130
+ queue.voice.channel = voiceChannel;
1131
+ queue.addToQueue(playlist.songs, position);
1132
+ if (skip)
1133
+ queue.skip();
1134
+ else
1135
+ this.emit("addList", queue, playlist);
944
1136
  } else {
945
- const connection = (0, import_voice2.getVoiceConnection)(resolveGuildId(guild), this.client.user?.id) ?? (0, import_voice2.getVoiceConnection)(resolveGuildId(guild));
946
- if (connection && connection.state.status !== import_voice2.VoiceConnectionStatus.Destroyed) {
947
- connection.destroy();
948
- }
949
- }
950
- }
951
- };
952
- __name(DisTubeVoiceManager, "DisTubeVoiceManager");
953
-
954
- // src/core/manager/FilterManager.ts
955
- var _validate, validate_fn, _resolveName, resolveName_fn, _resolveValue, resolveValue_fn, _apply, apply_fn;
956
- var FilterManager = class extends BaseManager {
957
- constructor(queue) {
958
- super(queue.distube);
959
- __privateAdd(this, _validate);
960
- __privateAdd(this, _resolveName);
961
- __privateAdd(this, _resolveValue);
962
- __privateAdd(this, _apply);
963
- __publicField(this, "queue");
964
- this.queue = queue;
965
- }
966
- add(filterOrFilters, override = false) {
967
- if (Array.isArray(filterOrFilters)) {
968
- const resolvedFilters = filterOrFilters.map((f) => __privateMethod(this, _validate, validate_fn).call(this, f));
969
- const newFilters = resolvedFilters.reduceRight((unique, o) => {
970
- if (!unique.some((obj) => obj === o && obj.name === o) && !unique.some((obj) => obj !== o.name && obj.name !== o.name)) {
971
- if (!this.has(o))
972
- unique.push(o);
973
- if (this.has(o) && override) {
974
- this.remove(o);
975
- unique.push(o);
976
- }
977
- }
978
- return unique;
979
- }, []).reverse();
980
- return this.set([...this.collection.values(), ...newFilters]);
981
- }
982
- return this.set([...this.collection.values(), filterOrFilters]);
983
- }
984
- clear() {
985
- return this.set([]);
986
- }
987
- set(filters) {
988
- this.collection.clear();
989
- for (const filter of filters) {
990
- const resolved = __privateMethod(this, _validate, validate_fn).call(this, filter);
991
- this.collection.set(__privateMethod(this, _resolveName, resolveName_fn).call(this, resolved), resolved);
992
- }
993
- __privateMethod(this, _apply, apply_fn).call(this);
994
- return this;
995
- }
996
- remove(filterOrFilters) {
997
- const remove = /* @__PURE__ */ __name((f) => this.collection.delete(__privateMethod(this, _resolveName, resolveName_fn).call(this, __privateMethod(this, _validate, validate_fn).call(this, f))), "remove");
998
- if (Array.isArray(filterOrFilters))
999
- filterOrFilters.map(remove);
1000
- else
1001
- remove(filterOrFilters);
1002
- __privateMethod(this, _apply, apply_fn).call(this);
1003
- return this;
1004
- }
1005
- has(filter) {
1006
- return this.collection.has(__privateMethod(this, _resolveName, resolveName_fn).call(this, filter));
1007
- }
1008
- get names() {
1009
- return this.collection.map((f) => __privateMethod(this, _resolveName, resolveName_fn).call(this, f));
1010
- }
1011
- get values() {
1012
- return this.collection.map((f) => __privateMethod(this, _resolveValue, resolveValue_fn).call(this, f));
1013
- }
1014
- toString() {
1015
- return this.names.toString();
1016
- }
1017
- };
1018
- __name(FilterManager, "FilterManager");
1019
- _validate = new WeakSet();
1020
- validate_fn = /* @__PURE__ */ __name(function(filter) {
1021
- if (typeof filter === "string" && Object.prototype.hasOwnProperty.call(this.distube.filters, filter) || typeof filter === "object" && typeof filter.name === "string" && typeof filter.value === "string") {
1022
- return filter;
1023
- }
1024
- throw new DisTubeError("INVALID_TYPE", "FilterResolvable", filter, "filter");
1025
- }, "#validate");
1026
- _resolveName = new WeakSet();
1027
- resolveName_fn = /* @__PURE__ */ __name(function(filter) {
1028
- return typeof filter === "string" ? filter : filter.name;
1029
- }, "#resolveName");
1030
- _resolveValue = new WeakSet();
1031
- resolveValue_fn = /* @__PURE__ */ __name(function(filter) {
1032
- return typeof filter === "string" ? this.distube.filters[filter] : filter.value;
1033
- }, "#resolveValue");
1034
- _apply = new WeakSet();
1035
- apply_fn = /* @__PURE__ */ __name(function() {
1036
- this.queue.beginTime = this.queue.currentTime;
1037
- this.queues.playSong(this.queue);
1038
- }, "#apply");
1039
-
1040
- // src/core/manager/QueueManager.ts
1041
- var _voiceEventHandler, voiceEventHandler_fn, _handleSongFinish, handleSongFinish_fn, _handlePlayingError, handlePlayingError_fn, _emitPlaySong, emitPlaySong_fn;
1042
- var QueueManager = class extends GuildIdManager {
1043
- constructor() {
1044
- super(...arguments);
1045
- __privateAdd(this, _voiceEventHandler);
1046
- __privateAdd(this, _handleSongFinish);
1047
- __privateAdd(this, _handlePlayingError);
1048
- __privateAdd(this, _emitPlaySong);
1049
- }
1050
- async create(channel, song, textChannel) {
1051
- if (this.has(channel.guildId))
1052
- throw new DisTubeError("QUEUE_EXIST");
1053
- const voice = this.voices.create(channel);
1054
- const queue = new Queue(this.distube, voice, song, textChannel);
1055
- await queue._taskQueue.queuing();
1056
- try {
1057
- await voice.join();
1058
- __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1059
- this.add(queue.id, queue);
1060
- this.emit("initQueue", queue);
1061
- const err = await this.playSong(queue);
1062
- return err || queue;
1063
- } finally {
1064
- queue._taskQueue.resolve();
1065
- }
1066
- }
1067
- createStream(queue) {
1068
- const { duration, formats, isLive, source, streamURL } = queue.songs[0];
1069
- const ffmpegArgs = queue.filters.size ? ["-af", queue.filters.values.join(",")] : void 0;
1070
- const seek = duration ? queue.beginTime : void 0;
1071
- const streamOptions = { ffmpegArgs, seek, isLive, type: this.options.streamType };
1072
- if (source === "youtube")
1073
- return DisTubeStream.YouTube(formats, streamOptions);
1074
- return DisTubeStream.DirectLink(streamURL, streamOptions);
1075
- }
1076
- async playSong(queue) {
1077
- if (!queue)
1078
- return true;
1079
- if (!queue.songs.length) {
1080
- queue.stop();
1081
- return true;
1082
- }
1083
- if (queue.stopped)
1084
- return false;
1085
- try {
1086
- const song = queue.songs[0];
1087
- const { url, source, formats, streamURL } = song;
1088
- if (source === "youtube" && !formats)
1089
- song._patchYouTube(await this.handler.getYouTubeInfo(url));
1090
- if (source !== "youtube" && !streamURL) {
1091
- for (const plugin of [...this.distube.extractorPlugins, ...this.distube.customPlugins]) {
1092
- if (await plugin.validate(url)) {
1093
- const info = [plugin.getStreamURL(url), plugin.getRelatedSongs(url)];
1094
- const result = await Promise.all(info);
1095
- song.streamURL = result[0];
1096
- song.related = result[1];
1097
- break;
1098
- }
1099
- }
1100
- }
1101
- const stream = this.createStream(queue);
1102
- queue.voice.play(stream);
1103
- song.streamURL = stream.url;
1104
- if (queue.stopped)
1105
- queue.stop();
1106
- else if (queue.paused)
1107
- queue.voice.pause();
1108
- return false;
1109
- } catch (e) {
1110
- __privateMethod(this, _handlePlayingError, handlePlayingError_fn).call(this, queue, e);
1111
- return true;
1112
- }
1113
- }
1114
- };
1115
- __name(QueueManager, "QueueManager");
1116
- _voiceEventHandler = new WeakSet();
1117
- voiceEventHandler_fn = /* @__PURE__ */ __name(function(queue) {
1118
- queue._listeners = {
1119
- disconnect: (error) => {
1120
- queue.remove();
1121
- this.emit("disconnect", queue);
1122
- if (error)
1123
- this.emitError(error, queue.textChannel);
1124
- },
1125
- error: (error) => __privateMethod(this, _handlePlayingError, handlePlayingError_fn).call(this, queue, error),
1126
- finish: () => __privateMethod(this, _handleSongFinish, handleSongFinish_fn).call(this, queue)
1127
- };
1128
- for (const event of Object.keys(queue._listeners)) {
1129
- queue.voice.on(event, queue._listeners[event]);
1130
- }
1131
- }, "#voiceEventHandler");
1132
- _handleSongFinish = new WeakSet();
1133
- handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
1134
- this.emit("finishSong", queue, queue.songs[0]);
1135
- await queue._taskQueue.queuing();
1136
- try {
1137
- if (queue.stopped)
1138
- return;
1139
- if (queue.repeatMode === 2 /* QUEUE */ && !queue._prev)
1140
- queue.songs.push(queue.songs[0]);
1141
- if (queue._prev) {
1142
- if (queue.repeatMode === 2 /* QUEUE */)
1143
- queue.songs.unshift(queue.songs.pop());
1144
- else
1145
- queue.songs.unshift(queue.previousSongs.pop());
1146
- }
1147
- if (queue.songs.length <= 1 && (queue._next || queue.repeatMode === 0 /* DISABLED */)) {
1148
- if (queue.autoplay) {
1149
- try {
1150
- await queue.addRelatedSong();
1151
- } catch {
1152
- this.emit("noRelated", queue);
1153
- }
1154
- }
1155
- if (queue.songs.length <= 1) {
1156
- if (this.options.leaveOnFinish)
1157
- queue.voice.leave();
1158
- if (!queue.autoplay)
1159
- this.emit("finish", queue);
1160
- queue.remove();
1161
- return;
1162
- }
1163
- }
1164
- const emitPlaySong = __privateMethod(this, _emitPlaySong, emitPlaySong_fn).call(this, queue);
1165
- if (!queue._prev && (queue.repeatMode !== 1 /* SONG */ || queue._next)) {
1166
- const prev = queue.songs.shift();
1167
- delete prev.formats;
1168
- delete prev.streamURL;
1169
- if (this.options.savePreviousSongs)
1170
- queue.previousSongs.push(prev);
1171
- else
1172
- queue.previousSongs.push({ id: prev.id });
1173
- }
1174
- queue._next = queue._prev = false;
1175
- queue.beginTime = 0;
1176
- const err = await this.playSong(queue);
1177
- if (!err && emitPlaySong)
1178
- this.emit("playSong", queue, queue.songs[0]);
1179
- } finally {
1180
- queue._taskQueue.resolve();
1181
- }
1182
- }, "#handleSongFinish");
1183
- _handlePlayingError = new WeakSet();
1184
- handlePlayingError_fn = /* @__PURE__ */ __name(function(queue, error) {
1185
- const song = queue.songs.shift();
1186
- try {
1187
- error.name = "PlayingError";
1188
- error.message = `${error.message}
1189
- Id: ${song.id}
1190
- Name: ${song.name}`;
1191
- } catch {
1192
- }
1193
- this.emitError(error, queue.textChannel);
1194
- if (queue.songs.length > 0) {
1195
- queue._next = queue._prev = false;
1196
- queue.beginTime = 0;
1197
- this.playSong(queue).then((e) => {
1198
- if (!e)
1199
- this.emit("playSong", queue, queue.songs[0]);
1200
- });
1201
- } else {
1202
- queue.stop();
1203
- }
1204
- }, "#handlePlayingError");
1205
- _emitPlaySong = new WeakSet();
1206
- emitPlaySong_fn = /* @__PURE__ */ __name(function(queue) {
1207
- return !this.options.emitNewSongOnly || queue.repeatMode === 1 /* SONG */ && queue._next || queue.repeatMode !== 1 /* SONG */ && queue.songs[0]?.id !== queue.songs[1]?.id;
1208
- }, "#emitPlaySong");
1209
-
1210
- // src/core/DisTubeHandler.ts
1211
- var import_ytpl = __toESM(require("@distube/ytpl"));
1212
- var import_ytdl_core = __toESM(require("@distube/ytdl-core"));
1213
- var DisTubeHandler = class extends DisTubeBase {
1214
- constructor(distube) {
1215
- super(distube);
1216
- const client = this.client;
1217
- if (this.options.leaveOnEmpty) {
1218
- client.on("voiceStateUpdate", (oldState) => {
1219
- if (!oldState?.channel)
1220
- return;
1221
- const queue = this.queues.get(oldState);
1222
- if (!queue) {
1223
- if (isVoiceChannelEmpty(oldState)) {
1224
- setTimeout(() => {
1225
- if (!this.queues.get(oldState) && isVoiceChannelEmpty(oldState))
1226
- this.voices.leave(oldState);
1227
- }, this.options.emptyCooldown * 1e3).unref();
1228
- }
1229
- return;
1230
- }
1231
- if (queue._emptyTimeout) {
1232
- clearTimeout(queue._emptyTimeout);
1233
- delete queue._emptyTimeout;
1234
- }
1235
- if (isVoiceChannelEmpty(oldState)) {
1236
- queue._emptyTimeout = setTimeout(() => {
1237
- delete queue._emptyTimeout;
1238
- if (isVoiceChannelEmpty(oldState)) {
1239
- queue.voice.leave();
1240
- this.emit("empty", queue);
1241
- if (queue.stopped)
1242
- queue.remove();
1243
- }
1244
- }, this.options.emptyCooldown * 1e3).unref();
1245
- }
1246
- });
1247
- }
1248
- }
1249
- get ytdlOptions() {
1250
- const options = this.options.ytdlOptions;
1251
- if (this.options.youtubeCookie) {
1252
- if (!options.requestOptions)
1253
- options.requestOptions = {};
1254
- if (!options.requestOptions.headers)
1255
- options.requestOptions.headers = {};
1256
- options.requestOptions.headers.cookie = this.options.youtubeCookie;
1257
- if (this.options.youtubeIdentityToken) {
1258
- options.requestOptions.headers["x-youtube-identity-token"] = this.options.youtubeIdentityToken;
1259
- }
1260
- }
1261
- return options;
1262
- }
1263
- getYouTubeInfo(url, basic = false) {
1264
- if (basic)
1265
- return import_ytdl_core.default.getBasicInfo(url, this.ytdlOptions);
1266
- return import_ytdl_core.default.getInfo(url, this.ytdlOptions);
1267
- }
1268
- async resolve(song, options = {}) {
1269
- if (song instanceof Song || song instanceof Playlist) {
1270
- if ("metadata" in options)
1271
- song.metadata = options.metadata;
1272
- if ("member" in options)
1273
- song.member = options.member;
1274
- return song;
1275
- }
1276
- if (song instanceof SearchResultVideo)
1277
- return new Song(song, options);
1278
- if (song instanceof SearchResultPlaylist)
1279
- return this.resolvePlaylist(song.url, options);
1280
- if (isObject(song)) {
1281
- if (!("url" in song) && !("id" in song))
1282
- throw new DisTubeError("CANNOT_RESOLVE_SONG", song);
1283
- return new Song(song, options);
1284
- }
1285
- if (import_ytpl.default.validateID(song))
1286
- return this.resolvePlaylist(song, options);
1287
- if (import_ytdl_core.default.validateURL(song))
1288
- return new Song(await this.getYouTubeInfo(song), options);
1289
- if (isURL(song)) {
1290
- for (const plugin of this.distube.extractorPlugins) {
1291
- if (await plugin.validate(song))
1292
- return plugin.resolve(song, options);
1293
- }
1294
- throw new DisTubeError("NOT_SUPPORTED_URL");
1295
- }
1296
- throw new DisTubeError("CANNOT_RESOLVE_SONG", song);
1297
- }
1298
- async resolvePlaylist(playlist, options = {}) {
1299
- const { member, source, metadata } = { source: "youtube", ...options };
1300
- if (playlist instanceof Playlist) {
1301
- if ("metadata" in options)
1302
- playlist.metadata = metadata;
1303
- if ("member" in options)
1304
- playlist.member = member;
1305
- return playlist;
1306
- }
1307
- if (typeof playlist === "string") {
1308
- const info = await (0, import_ytpl.default)(playlist, { limit: Infinity });
1309
- const songs = info.items.filter((v) => !v.thumbnail.includes("no_thumbnail")).map((v) => new Song(v, { member, metadata }));
1310
- return new Playlist({
1311
- source,
1312
- songs,
1313
- member,
1314
- name: info.title,
1315
- url: info.url,
1316
- thumbnail: songs[0].thumbnail
1317
- }, { metadata });
1318
- }
1319
- return new Playlist(playlist, { member, properties: { source }, metadata });
1320
- }
1321
- async searchSong(message, query) {
1322
- if (!isMessageInstance(message))
1323
- throw new DisTubeError("INVALID_TYPE", "Discord.Message", message, "message");
1324
- if (typeof query !== "string")
1325
- throw new DisTubeError("INVALID_TYPE", "string", query, "query");
1326
- if (query.length === 0)
1327
- throw new DisTubeError("EMPTY_STRING", "query");
1328
- const limit = this.options.searchSongs > 1 ? this.options.searchSongs : 1;
1329
- const results = await this.distube.search(query, {
1330
- limit,
1331
- safeSearch: this.options.nsfw ? false : !message.channel?.nsfw
1332
- }).catch(() => {
1333
- if (!this.emit("searchNoResult", message, query)) {
1334
- console.warn("searchNoResult event does not have any listeners! Emits `error` event instead.");
1335
- throw new DisTubeError("NO_RESULT");
1336
- }
1337
- });
1338
- if (!results)
1339
- return null;
1340
- return this.createSearchMessageCollector(message, results, query);
1341
- }
1342
- async createSearchMessageCollector(message, results, query) {
1343
- if (!isMessageInstance(message))
1344
- throw new DisTubeError("INVALID_TYPE", "Discord.Message", message, "message");
1345
- if (!Array.isArray(results) || results.length == 0) {
1346
- throw new DisTubeError("INVALID_TYPE", "Array<SearchResult|Song|Playlist>", results, "results");
1347
- }
1348
- if (this.options.searchSongs > 1) {
1349
- const searchEvents = [
1350
- "searchNoResult",
1351
- "searchResult",
1352
- "searchCancel",
1353
- "searchInvalidAnswer",
1354
- "searchDone"
1355
- ];
1356
- for (const evn of searchEvents) {
1357
- if (this.distube.listenerCount(evn) === 0) {
1358
- console.warn(`"searchSongs" option is disabled due to missing "${evn}" listener.`);
1359
- console.warn(`If you don't want to use "${evn}" event, simply add an empty listener (not recommended):
1360
- <DisTube>.on("${evn}", () => {})`);
1361
- this.options.searchSongs = 0;
1362
- }
1363
- }
1364
- }
1365
- const limit = this.options.searchSongs > 1 ? this.options.searchSongs : 1;
1366
- let result = results[0];
1367
- if (limit > 1) {
1368
- results.splice(limit);
1369
- this.emit("searchResult", message, results, query);
1370
- const c = message.channel;
1371
- const answers = await c.awaitMessages({
1372
- filter: (m) => m.author.id === message.author.id,
1373
- max: 1,
1374
- time: this.options.searchCooldown * 1e3,
1375
- errors: ["time"]
1376
- }).catch(() => void 0);
1377
- const ans = answers?.first();
1378
- if (!ans) {
1379
- this.emit("searchCancel", message, query);
1380
- return null;
1381
- }
1382
- const index = parseInt(ans.content, 10);
1383
- if (isNaN(index) || index > results.length || index < 1) {
1384
- this.emit("searchInvalidAnswer", message, ans, query);
1385
- return null;
1386
- }
1387
- this.emit("searchDone", message, ans, query);
1388
- result = results[index - 1];
1389
- }
1390
- return result;
1391
- }
1392
- async playPlaylist(voiceChannel, playlist, options = {}) {
1393
- const { textChannel, skip } = { skip: false, ...options };
1394
- const position = Number(options.position) || (skip ? 1 : 0);
1395
- if (!(playlist instanceof Playlist))
1396
- throw new DisTubeError("INVALID_TYPE", "Playlist", playlist, "playlist");
1397
- const queue = this.queues.get(voiceChannel);
1398
- if (!this.options.nsfw && !(queue?.textChannel || textChannel)?.nsfw) {
1399
- playlist.songs = playlist.songs.filter((s) => !s.age_restricted);
1400
- }
1401
- if (!playlist.songs.length) {
1402
- if (!this.options.nsfw && !textChannel?.nsfw) {
1403
- throw new DisTubeError("EMPTY_FILTERED_PLAYLIST");
1404
- }
1405
- throw new DisTubeError("EMPTY_PLAYLIST");
1406
- }
1407
- if (queue) {
1408
- if (this.options.joinNewVoiceChannel)
1409
- queue.voice.channel = voiceChannel;
1410
- queue.addToQueue(playlist.songs, position);
1411
- if (skip)
1412
- queue.skip();
1413
- else
1414
- this.emit("addList", queue, playlist);
1415
- } else {
1416
- const newQueue = await this.queues.create(voiceChannel, playlist.songs, textChannel);
1417
- if (newQueue instanceof Queue) {
1418
- if (this.options.emitAddListWhenCreatingQueue)
1419
- this.emit("addList", newQueue, playlist);
1420
- this.emit("playSong", newQueue, newQueue.songs[0]);
1137
+ const newQueue = await this.queues.create(voiceChannel, playlist.songs, textChannel);
1138
+ if (newQueue instanceof Queue) {
1139
+ if (this.options.emitAddListWhenCreatingQueue)
1140
+ this.emit("addList", newQueue, playlist);
1141
+ this.emit("playSong", newQueue, newQueue.songs[0]);
1421
1142
  }
1422
1143
  }
1423
1144
  }
@@ -1520,13 +1241,23 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1520
1241
  throw new DisTubeError("INVALID_TYPE", "boolean", options.savePreviousSongs, "DisTubeOptions.savePreviousSongs");
1521
1242
  }
1522
1243
  if (typeof options.joinNewVoiceChannel !== "boolean") {
1523
- throw new DisTubeError("INVALID_TYPE", "boolean", options.joinNewVoiceChannel, "DisTubeOptions.joinNewVoiceChannel");
1244
+ throw new DisTubeError(
1245
+ "INVALID_TYPE",
1246
+ "boolean",
1247
+ options.joinNewVoiceChannel,
1248
+ "DisTubeOptions.joinNewVoiceChannel"
1249
+ );
1524
1250
  }
1525
1251
  if (typeof options.youtubeCookie !== "undefined" && typeof options.youtubeCookie !== "string") {
1526
1252
  throw new DisTubeError("INVALID_TYPE", "string", options.youtubeCookie, "DisTubeOptions.youtubeCookie");
1527
1253
  }
1528
1254
  if (typeof options.youtubeIdentityToken !== "undefined" && typeof options.youtubeIdentityToken !== "string") {
1529
- throw new DisTubeError("INVALID_TYPE", "string", options.youtubeIdentityToken, "DisTubeOptions.youtubeIdentityToken");
1255
+ throw new DisTubeError(
1256
+ "INVALID_TYPE",
1257
+ "string",
1258
+ options.youtubeIdentityToken,
1259
+ "DisTubeOptions.youtubeIdentityToken"
1260
+ );
1530
1261
  }
1531
1262
  if (typeof options.customFilters !== "undefined" && typeof options.customFilters !== "object" || Array.isArray(options.customFilters)) {
1532
1263
  throw new DisTubeError("INVALID_TYPE", "object", options.customFilters, "DisTubeOptions.customFilters");
@@ -1550,90 +1281,424 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1550
1281
  throw new DisTubeError("INVALID_TYPE", "boolean", options.nsfw, "DisTubeOptions.nsfw");
1551
1282
  }
1552
1283
  if (typeof options.emitAddSongWhenCreatingQueue !== "boolean") {
1553
- throw new DisTubeError("INVALID_TYPE", "boolean", options.emitAddSongWhenCreatingQueue, "DisTubeOptions.emitAddSongWhenCreatingQueue");
1284
+ throw new DisTubeError(
1285
+ "INVALID_TYPE",
1286
+ "boolean",
1287
+ options.emitAddSongWhenCreatingQueue,
1288
+ "DisTubeOptions.emitAddSongWhenCreatingQueue"
1289
+ );
1290
+ }
1291
+ if (typeof options.emitAddListWhenCreatingQueue !== "boolean") {
1292
+ throw new DisTubeError(
1293
+ "INVALID_TYPE",
1294
+ "boolean",
1295
+ options.emitAddListWhenCreatingQueue,
1296
+ "DisTubeOptions.emitAddListWhenCreatingQueue"
1297
+ );
1298
+ }
1299
+ if (typeof options.streamType !== "number" || isNaN(options.streamType) || !StreamType[options.streamType]) {
1300
+ throw new DisTubeError("INVALID_TYPE", "StreamType", options.streamType, "DisTubeOptions.streamType");
1301
+ }
1302
+ if (typeof options.directLink !== "boolean") {
1303
+ throw new DisTubeError("INVALID_TYPE", "boolean", options.directLink, "DisTubeOptions.directLink");
1304
+ }
1305
+ }, "#validateOptions");
1306
+
1307
+ // src/core/DisTubeStream.ts
1308
+ var import_prism_media = require("prism-media");
1309
+ var import_voice2 = require("@discordjs/voice");
1310
+ var chooseBestVideoFormat = /* @__PURE__ */ __name((formats, isLive = false) => {
1311
+ let filter = /* @__PURE__ */ __name((format) => format.hasAudio, "filter");
1312
+ if (isLive)
1313
+ filter = /* @__PURE__ */ __name((format) => format.hasAudio && format.isHLS, "filter");
1314
+ formats = formats.filter(filter).sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate) || Number(a.bitrate) - Number(b.bitrate));
1315
+ return formats.find((format) => !format.hasVideo) || formats.sort((a, b) => Number(a.bitrate) - Number(b.bitrate))[0];
1316
+ }, "chooseBestVideoFormat");
1317
+ var DisTubeStream = class {
1318
+ constructor(url, options) {
1319
+ __publicField(this, "type");
1320
+ __publicField(this, "stream");
1321
+ __publicField(this, "url");
1322
+ this.url = url;
1323
+ this.type = !options.type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
1324
+ const args = [
1325
+ "-reconnect",
1326
+ "1",
1327
+ "-reconnect_streamed",
1328
+ "1",
1329
+ "-reconnect_delay_max",
1330
+ "5",
1331
+ "-i",
1332
+ url,
1333
+ "-analyzeduration",
1334
+ "0",
1335
+ "-loglevel",
1336
+ "0",
1337
+ "-ar",
1338
+ "48000",
1339
+ "-ac",
1340
+ "2",
1341
+ "-f"
1342
+ ];
1343
+ if (!options.type) {
1344
+ args.push("opus", "-acodec", "libopus");
1345
+ } else {
1346
+ args.push("s16le");
1347
+ }
1348
+ if (typeof options.seek === "number" && options.seek > 0) {
1349
+ args.unshift("-ss", options.seek.toString());
1350
+ }
1351
+ if (Array.isArray(options.ffmpegArgs)) {
1352
+ args.push(...options.ffmpegArgs);
1353
+ }
1354
+ this.stream = new import_prism_media.FFmpeg({ args, shell: false });
1355
+ this.stream._readableState && (this.stream._readableState.highWaterMark = 1 << 25);
1356
+ }
1357
+ static YouTube(formats, options = {}) {
1358
+ if (!formats || !formats.length)
1359
+ throw new DisTubeError("UNAVAILABLE_VIDEO");
1360
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
1361
+ throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1362
+ }
1363
+ const bestFormat = chooseBestVideoFormat(formats, options.isLive);
1364
+ if (!bestFormat)
1365
+ throw new DisTubeError("UNPLAYABLE_FORMATS");
1366
+ return new DisTubeStream(bestFormat.url, options);
1367
+ }
1368
+ static DirectLink(url, options = {}) {
1369
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
1370
+ throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1371
+ }
1372
+ if (typeof url !== "string" || !isURL(url)) {
1373
+ throw new DisTubeError("INVALID_TYPE", "an URL", url);
1374
+ }
1375
+ return new DisTubeStream(url, options);
1376
+ }
1377
+ };
1378
+ __name(DisTubeStream, "DisTubeStream");
1379
+
1380
+ // src/core/manager/BaseManager.ts
1381
+ var import_discord2 = require("discord.js");
1382
+ var BaseManager = class extends DisTubeBase {
1383
+ constructor() {
1384
+ super(...arguments);
1385
+ __publicField(this, "collection", new import_discord2.Collection());
1386
+ }
1387
+ get size() {
1388
+ return this.collection.size;
1389
+ }
1390
+ };
1391
+ __name(BaseManager, "BaseManager");
1392
+
1393
+ // src/core/manager/GuildIdManager.ts
1394
+ var GuildIdManager = class extends BaseManager {
1395
+ add(idOrInstance, data) {
1396
+ const id = resolveGuildId(idOrInstance);
1397
+ const existing = this.get(id);
1398
+ if (existing)
1399
+ return this;
1400
+ return this.collection.set(id, data);
1401
+ }
1402
+ get(idOrInstance) {
1403
+ return this.collection.get(resolveGuildId(idOrInstance));
1404
+ }
1405
+ remove(idOrInstance) {
1406
+ return this.collection.delete(resolveGuildId(idOrInstance));
1407
+ }
1408
+ has(idOrInstance) {
1409
+ return this.collection.has(resolveGuildId(idOrInstance));
1410
+ }
1411
+ };
1412
+ __name(GuildIdManager, "GuildIdManager");
1413
+
1414
+ // src/core/manager/DisTubeVoiceManager.ts
1415
+ var import_voice3 = require("@discordjs/voice");
1416
+ var DisTubeVoiceManager = class extends GuildIdManager {
1417
+ create(channel) {
1418
+ const existing = this.get(channel.guildId);
1419
+ if (existing) {
1420
+ existing.channel = channel;
1421
+ return existing;
1422
+ }
1423
+ return new DisTubeVoice(this, channel);
1424
+ }
1425
+ join(channel) {
1426
+ const existing = this.get(channel.guildId);
1427
+ if (existing)
1428
+ return existing.join(channel);
1429
+ return this.create(channel).join();
1430
+ }
1431
+ leave(guild) {
1432
+ const voice = this.get(guild);
1433
+ if (voice) {
1434
+ voice.leave();
1435
+ } else {
1436
+ const connection = (0, import_voice3.getVoiceConnection)(resolveGuildId(guild), this.client.user?.id) ?? (0, import_voice3.getVoiceConnection)(resolveGuildId(guild));
1437
+ if (connection && connection.state.status !== import_voice3.VoiceConnectionStatus.Destroyed) {
1438
+ connection.destroy();
1439
+ }
1440
+ }
1441
+ }
1442
+ };
1443
+ __name(DisTubeVoiceManager, "DisTubeVoiceManager");
1444
+
1445
+ // src/core/manager/FilterManager.ts
1446
+ var _validate, validate_fn, _resolveName, resolveName_fn, _resolveValue, resolveValue_fn, _apply, apply_fn;
1447
+ var FilterManager = class extends BaseManager {
1448
+ constructor(queue) {
1449
+ super(queue.distube);
1450
+ __privateAdd(this, _validate);
1451
+ __privateAdd(this, _resolveName);
1452
+ __privateAdd(this, _resolveValue);
1453
+ __privateAdd(this, _apply);
1454
+ __publicField(this, "queue");
1455
+ this.queue = queue;
1456
+ }
1457
+ add(filterOrFilters, override = false) {
1458
+ if (Array.isArray(filterOrFilters)) {
1459
+ const resolvedFilters = filterOrFilters.map((f) => __privateMethod(this, _validate, validate_fn).call(this, f));
1460
+ const newFilters = resolvedFilters.reduceRight((unique, o) => {
1461
+ if (!unique.some((obj) => obj === o && obj.name === o) && !unique.some((obj) => obj !== o.name && obj.name !== o.name)) {
1462
+ if (!this.has(o))
1463
+ unique.push(o);
1464
+ if (this.has(o) && override) {
1465
+ this.remove(o);
1466
+ unique.push(o);
1467
+ }
1468
+ }
1469
+ return unique;
1470
+ }, []).reverse();
1471
+ return this.set([...this.collection.values(), ...newFilters]);
1472
+ }
1473
+ return this.set([...this.collection.values(), filterOrFilters]);
1474
+ }
1475
+ clear() {
1476
+ return this.set([]);
1477
+ }
1478
+ set(filters) {
1479
+ this.collection.clear();
1480
+ for (const filter of filters) {
1481
+ const resolved = __privateMethod(this, _validate, validate_fn).call(this, filter);
1482
+ this.collection.set(__privateMethod(this, _resolveName, resolveName_fn).call(this, resolved), resolved);
1483
+ }
1484
+ __privateMethod(this, _apply, apply_fn).call(this);
1485
+ return this;
1486
+ }
1487
+ remove(filterOrFilters) {
1488
+ const remove = /* @__PURE__ */ __name((f) => this.collection.delete(__privateMethod(this, _resolveName, resolveName_fn).call(this, __privateMethod(this, _validate, validate_fn).call(this, f))), "remove");
1489
+ if (Array.isArray(filterOrFilters))
1490
+ filterOrFilters.map(remove);
1491
+ else
1492
+ remove(filterOrFilters);
1493
+ __privateMethod(this, _apply, apply_fn).call(this);
1494
+ return this;
1495
+ }
1496
+ has(filter) {
1497
+ return this.collection.has(__privateMethod(this, _resolveName, resolveName_fn).call(this, filter));
1498
+ }
1499
+ get names() {
1500
+ return this.collection.map((f) => __privateMethod(this, _resolveName, resolveName_fn).call(this, f));
1554
1501
  }
1555
- if (typeof options.emitAddListWhenCreatingQueue !== "boolean") {
1556
- throw new DisTubeError("INVALID_TYPE", "boolean", options.emitAddListWhenCreatingQueue, "DisTubeOptions.emitAddListWhenCreatingQueue");
1502
+ get values() {
1503
+ return this.collection.map((f) => __privateMethod(this, _resolveValue, resolveValue_fn).call(this, f));
1557
1504
  }
1558
- if (typeof options.streamType !== "number" || isNaN(options.streamType) || !StreamType[options.streamType]) {
1559
- throw new DisTubeError("INVALID_TYPE", "StreamType", options.streamType, "DisTubeOptions.streamType");
1505
+ toString() {
1506
+ return this.names.toString();
1560
1507
  }
1561
- if (typeof options.directLink !== "boolean") {
1562
- throw new DisTubeError("INVALID_TYPE", "boolean", options.directLink, "DisTubeOptions.directLink");
1508
+ };
1509
+ __name(FilterManager, "FilterManager");
1510
+ _validate = new WeakSet();
1511
+ validate_fn = /* @__PURE__ */ __name(function(filter) {
1512
+ if (typeof filter === "string" && Object.prototype.hasOwnProperty.call(this.distube.filters, filter) || typeof filter === "object" && typeof filter.name === "string" && typeof filter.value === "string") {
1513
+ return filter;
1563
1514
  }
1564
- }, "#validateOptions");
1515
+ throw new DisTubeError("INVALID_TYPE", "FilterResolvable", filter, "filter");
1516
+ }, "#validate");
1517
+ _resolveName = new WeakSet();
1518
+ resolveName_fn = /* @__PURE__ */ __name(function(filter) {
1519
+ return typeof filter === "string" ? filter : filter.name;
1520
+ }, "#resolveName");
1521
+ _resolveValue = new WeakSet();
1522
+ resolveValue_fn = /* @__PURE__ */ __name(function(filter) {
1523
+ return typeof filter === "string" ? this.distube.filters[filter] : filter.value;
1524
+ }, "#resolveValue");
1525
+ _apply = new WeakSet();
1526
+ apply_fn = /* @__PURE__ */ __name(function() {
1527
+ this.queue.beginTime = this.queue.currentTime;
1528
+ this.queues.playSong(this.queue);
1529
+ }, "#apply");
1565
1530
 
1566
- // src/core/DisTubeStream.ts
1567
- var import_prism_media = require("prism-media");
1568
- var import_voice3 = require("@discordjs/voice");
1569
- var chooseBestVideoFormat = /* @__PURE__ */ __name((formats, isLive = false) => {
1570
- let filter = /* @__PURE__ */ __name((format) => format.hasAudio, "filter");
1571
- if (isLive)
1572
- filter = /* @__PURE__ */ __name((format) => format.hasAudio && format.isHLS, "filter");
1573
- formats = formats.filter(filter).sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate) || Number(a.bitrate) - Number(b.bitrate));
1574
- return formats.find((format) => !format.hasVideo) || formats.sort((a, b) => Number(a.bitrate) - Number(b.bitrate))[0];
1575
- }, "chooseBestVideoFormat");
1576
- var DisTubeStream = class {
1577
- constructor(url, options) {
1578
- __publicField(this, "type");
1579
- __publicField(this, "stream");
1580
- __publicField(this, "url");
1581
- this.url = url;
1582
- this.type = !options.type ? import_voice3.StreamType.OggOpus : import_voice3.StreamType.Raw;
1583
- const args = [
1584
- "-reconnect",
1585
- "1",
1586
- "-reconnect_streamed",
1587
- "1",
1588
- "-reconnect_delay_max",
1589
- "5",
1590
- "-i",
1591
- url,
1592
- "-analyzeduration",
1593
- "0",
1594
- "-loglevel",
1595
- "0",
1596
- "-ar",
1597
- "48000",
1598
- "-ac",
1599
- "2",
1600
- "-f"
1601
- ];
1602
- if (!options.type) {
1603
- args.push("opus", "-acodec", "libopus");
1604
- } else {
1605
- args.push("s16le");
1531
+ // src/core/manager/QueueManager.ts
1532
+ var _voiceEventHandler, voiceEventHandler_fn, _handleSongFinish, handleSongFinish_fn, _handlePlayingError, handlePlayingError_fn, _emitPlaySong, emitPlaySong_fn;
1533
+ var QueueManager = class extends GuildIdManager {
1534
+ constructor() {
1535
+ super(...arguments);
1536
+ __privateAdd(this, _voiceEventHandler);
1537
+ __privateAdd(this, _handleSongFinish);
1538
+ __privateAdd(this, _handlePlayingError);
1539
+ __privateAdd(this, _emitPlaySong);
1540
+ }
1541
+ async create(channel, song, textChannel) {
1542
+ if (this.has(channel.guildId))
1543
+ throw new DisTubeError("QUEUE_EXIST");
1544
+ const voice = this.voices.create(channel);
1545
+ const queue = new Queue(this.distube, voice, song, textChannel);
1546
+ await queue._taskQueue.queuing();
1547
+ try {
1548
+ await voice.join();
1549
+ __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1550
+ this.add(queue.id, queue);
1551
+ this.emit("initQueue", queue);
1552
+ const err = await this.playSong(queue);
1553
+ return err || queue;
1554
+ } finally {
1555
+ queue._taskQueue.resolve();
1606
1556
  }
1607
- if (typeof options.seek === "number" && options.seek > 0) {
1608
- args.unshift("-ss", options.seek.toString());
1557
+ }
1558
+ createStream(queue) {
1559
+ const { duration, formats, isLive, source, streamURL } = queue.songs[0];
1560
+ const ffmpegArgs = queue.filters.size ? ["-af", queue.filters.values.join(",")] : void 0;
1561
+ const seek = duration ? queue.beginTime : void 0;
1562
+ const streamOptions = { ffmpegArgs, seek, isLive, type: this.options.streamType };
1563
+ if (source === "youtube")
1564
+ return DisTubeStream.YouTube(formats, streamOptions);
1565
+ return DisTubeStream.DirectLink(streamURL, streamOptions);
1566
+ }
1567
+ async playSong(queue) {
1568
+ if (!queue)
1569
+ return true;
1570
+ if (!queue.songs.length) {
1571
+ queue.stop();
1572
+ return true;
1609
1573
  }
1610
- if (Array.isArray(options.ffmpegArgs)) {
1611
- args.push(...options.ffmpegArgs);
1574
+ if (queue.stopped)
1575
+ return false;
1576
+ try {
1577
+ const song = queue.songs[0];
1578
+ const { url, source, formats, streamURL, isLive } = song;
1579
+ if (source === "youtube") {
1580
+ if (!formats || !chooseBestVideoFormat(formats, isLive)) {
1581
+ song._patchYouTube(await this.handler.getYouTubeInfo(url));
1582
+ }
1583
+ } else if (!streamURL) {
1584
+ for (const plugin of [...this.distube.extractorPlugins, ...this.distube.customPlugins]) {
1585
+ if (await plugin.validate(url)) {
1586
+ const info = [plugin.getStreamURL(url), plugin.getRelatedSongs(url)];
1587
+ const result = await Promise.all(info);
1588
+ song.streamURL = result[0];
1589
+ song.related = result[1];
1590
+ break;
1591
+ }
1592
+ }
1593
+ }
1594
+ const stream = this.createStream(queue);
1595
+ queue.voice.play(stream);
1596
+ song.streamURL = stream.url;
1597
+ if (queue.stopped)
1598
+ queue.stop();
1599
+ else if (queue.paused)
1600
+ queue.voice.pause();
1601
+ return false;
1602
+ } catch (e) {
1603
+ __privateMethod(this, _handlePlayingError, handlePlayingError_fn).call(this, queue, e);
1604
+ return true;
1612
1605
  }
1613
- this.stream = new import_prism_media.FFmpeg({ args, shell: false });
1614
1606
  }
1615
- static YouTube(formats, options = {}) {
1616
- if (!formats || !formats.length)
1617
- throw new DisTubeError("UNAVAILABLE_VIDEO");
1618
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1619
- throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1620
- }
1621
- const bestFormat = chooseBestVideoFormat(formats, options.isLive);
1622
- if (!bestFormat)
1623
- throw new DisTubeError("UNPLAYABLE_FORMATS");
1624
- return new DisTubeStream(bestFormat.url, options);
1607
+ };
1608
+ __name(QueueManager, "QueueManager");
1609
+ _voiceEventHandler = new WeakSet();
1610
+ voiceEventHandler_fn = /* @__PURE__ */ __name(function(queue) {
1611
+ queue._listeners = {
1612
+ disconnect: (error) => {
1613
+ queue.remove();
1614
+ this.emit("disconnect", queue);
1615
+ if (error)
1616
+ this.emitError(error, queue.textChannel);
1617
+ },
1618
+ error: (error) => __privateMethod(this, _handlePlayingError, handlePlayingError_fn).call(this, queue, error),
1619
+ finish: () => __privateMethod(this, _handleSongFinish, handleSongFinish_fn).call(this, queue)
1620
+ };
1621
+ for (const event of objectKeys(queue._listeners)) {
1622
+ queue.voice.on(event, queue._listeners[event]);
1625
1623
  }
1626
- static DirectLink(url, options = {}) {
1627
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1628
- throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1624
+ }, "#voiceEventHandler");
1625
+ _handleSongFinish = new WeakSet();
1626
+ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
1627
+ this.emit("finishSong", queue, queue.songs[0]);
1628
+ await queue._taskQueue.queuing();
1629
+ try {
1630
+ if (queue.stopped)
1631
+ return;
1632
+ if (queue.repeatMode === 2 /* QUEUE */ && !queue._prev)
1633
+ queue.songs.push(queue.songs[0]);
1634
+ if (queue._prev) {
1635
+ if (queue.repeatMode === 2 /* QUEUE */)
1636
+ queue.songs.unshift(queue.songs.pop());
1637
+ else
1638
+ queue.songs.unshift(queue.previousSongs.pop());
1629
1639
  }
1630
- if (typeof url !== "string" || !isURL(url)) {
1631
- throw new DisTubeError("INVALID_TYPE", "an URL", url);
1640
+ if (queue.songs.length <= 1 && (queue._next || queue.repeatMode === 0 /* DISABLED */)) {
1641
+ if (queue.autoplay) {
1642
+ try {
1643
+ await queue.addRelatedSong();
1644
+ } catch {
1645
+ this.emit("noRelated", queue);
1646
+ }
1647
+ }
1648
+ if (queue.songs.length <= 1) {
1649
+ if (this.options.leaveOnFinish)
1650
+ queue.voice.leave();
1651
+ if (!queue.autoplay)
1652
+ this.emit("finish", queue);
1653
+ queue.remove();
1654
+ return;
1655
+ }
1632
1656
  }
1633
- return new DisTubeStream(url, options);
1657
+ const emitPlaySong = __privateMethod(this, _emitPlaySong, emitPlaySong_fn).call(this, queue);
1658
+ if (!queue._prev && (queue.repeatMode !== 1 /* SONG */ || queue._next)) {
1659
+ const prev = queue.songs.shift();
1660
+ delete prev.formats;
1661
+ delete prev.streamURL;
1662
+ if (this.options.savePreviousSongs)
1663
+ queue.previousSongs.push(prev);
1664
+ else
1665
+ queue.previousSongs.push({ id: prev.id });
1666
+ }
1667
+ queue._next = queue._prev = false;
1668
+ queue.beginTime = 0;
1669
+ const err = await this.playSong(queue);
1670
+ if (!err && emitPlaySong)
1671
+ this.emit("playSong", queue, queue.songs[0]);
1672
+ } finally {
1673
+ queue._taskQueue.resolve();
1634
1674
  }
1635
- };
1636
- __name(DisTubeStream, "DisTubeStream");
1675
+ }, "#handleSongFinish");
1676
+ _handlePlayingError = new WeakSet();
1677
+ handlePlayingError_fn = /* @__PURE__ */ __name(function(queue, error) {
1678
+ const song = queue.songs.shift();
1679
+ try {
1680
+ error.name = "PlayingError";
1681
+ error.message = `${error.message}
1682
+ Id: ${song.id}
1683
+ Name: ${song.name}`;
1684
+ } catch {
1685
+ }
1686
+ this.emitError(error, queue.textChannel);
1687
+ if (queue.songs.length > 0) {
1688
+ queue._next = queue._prev = false;
1689
+ queue.beginTime = 0;
1690
+ this.playSong(queue).then((e) => {
1691
+ if (!e)
1692
+ this.emit("playSong", queue, queue.songs[0]);
1693
+ });
1694
+ } else {
1695
+ queue.stop();
1696
+ }
1697
+ }, "#handlePlayingError");
1698
+ _emitPlaySong = new WeakSet();
1699
+ emitPlaySong_fn = /* @__PURE__ */ __name(function(queue) {
1700
+ return !this.options.emitNewSongOnly || queue.repeatMode === 1 /* SONG */ && queue._next || queue.repeatMode !== 1 /* SONG */ && queue.songs[0]?.id !== queue.songs[1]?.id;
1701
+ }, "#emitPlaySong");
1637
1702
 
1638
1703
  // src/struct/Queue.ts
1639
1704
  var _filters;
@@ -1881,7 +1946,7 @@ var Queue = class extends DisTubeBase {
1881
1946
  this.songs = [];
1882
1947
  this.previousSongs = [];
1883
1948
  if (this._listeners) {
1884
- for (const event of Object.keys(this._listeners)) {
1949
+ for (const event of objectKeys(this._listeners)) {
1885
1950
  this.voice.removeListener(event, this._listeners[event]);
1886
1951
  }
1887
1952
  }
@@ -1957,7 +2022,7 @@ __name(ExtractorPlugin, "ExtractorPlugin");
1957
2022
 
1958
2023
  // src/util.ts
1959
2024
  var import_url = require("url");
1960
- var import_discord2 = require("discord.js");
2025
+ var import_discord3 = require("discord.js");
1961
2026
  var formatInt = /* @__PURE__ */ __name((int) => int < 10 ? `0${int}` : int, "formatInt");
1962
2027
  function formatDuration(sec) {
1963
2028
  if (!sec || !Number(sec))
@@ -2011,8 +2076,8 @@ function isURL(input) {
2011
2076
  }
2012
2077
  __name(isURL, "isURL");
2013
2078
  function checkIntents(options) {
2014
- const intents = new import_discord2.IntentsBitField(options.intents);
2015
- if (!intents.has(import_discord2.GatewayIntentBits.GuildVoiceStates))
2079
+ const intents = new import_discord3.IntentsBitField(options.intents);
2080
+ if (!intents.has(import_discord3.GatewayIntentBits.GuildVoiceStates))
2016
2081
  throw new DisTubeError("MISSING_INTENTS", "GuildVoiceStates");
2017
2082
  }
2018
2083
  __name(checkIntents, "checkIntents");
@@ -2030,7 +2095,7 @@ function isVoiceChannelEmpty(voiceState) {
2030
2095
  __name(isVoiceChannelEmpty, "isVoiceChannelEmpty");
2031
2096
  function isSnowflake(id) {
2032
2097
  try {
2033
- return import_discord2.SnowflakeUtil.deconstruct(id).timestamp > import_discord2.SnowflakeUtil.epoch;
2098
+ return import_discord3.SnowflakeUtil.deconstruct(id).timestamp > import_discord3.SnowflakeUtil.epoch;
2034
2099
  } catch {
2035
2100
  return false;
2036
2101
  }
@@ -2041,15 +2106,15 @@ function isMemberInstance(member) {
2041
2106
  }
2042
2107
  __name(isMemberInstance, "isMemberInstance");
2043
2108
  function isTextChannelInstance(channel) {
2044
- return !!channel && isSnowflake(channel.id) && isSnowflake(channel.guildId) && typeof channel.name === "string" && import_discord2.Constants.TextBasedChannelTypes.includes(channel.type) && typeof channel.nsfw === "boolean" && "messages" in channel && typeof channel.send === "function";
2109
+ return !!channel && isSnowflake(channel.id) && isSnowflake(channel.guildId) && typeof channel.name === "string" && import_discord3.Constants.TextBasedChannelTypes.includes(channel.type) && typeof channel.nsfw === "boolean" && "messages" in channel && typeof channel.send === "function";
2045
2110
  }
2046
2111
  __name(isTextChannelInstance, "isTextChannelInstance");
2047
2112
  function isMessageInstance(message) {
2048
- return !!message && isSnowflake(message.id) && isSnowflake(message.guildId) && isMemberInstance(message.member) && isTextChannelInstance(message.channel) && import_discord2.Constants.NonSystemMessageTypes.includes(message.type) && message.member.id === message.author?.id;
2113
+ return !!message && isSnowflake(message.id) && isSnowflake(message.guildId) && isMemberInstance(message.member) && isTextChannelInstance(message.channel) && import_discord3.Constants.NonSystemMessageTypes.includes(message.type) && message.member.id === message.author?.id;
2049
2114
  }
2050
2115
  __name(isMessageInstance, "isMessageInstance");
2051
2116
  function isSupportedVoiceChannel(channel) {
2052
- return !!channel && isSnowflake(channel.id) && isSnowflake(channel.guildId) && import_discord2.Constants.VoiceBasedChannelTypes.includes(channel.type);
2117
+ return !!channel && isSnowflake(channel.id) && isSnowflake(channel.guildId) && import_discord3.Constants.VoiceBasedChannelTypes.includes(channel.type);
2053
2118
  }
2054
2119
  __name(isSupportedVoiceChannel, "isSupportedVoiceChannel");
2055
2120
  function isGuildInstance(guild) {
@@ -2081,8 +2146,8 @@ __name(isClientInstance, "isClientInstance");
2081
2146
  function checkInvalidKey(target, source, sourceName) {
2082
2147
  if (!isObject(target))
2083
2148
  throw new DisTubeError("INVALID_TYPE", "object", target, sourceName);
2084
- const sourceKeys = Array.isArray(source) ? source : Object.keys(source);
2085
- const invalidKey = Object.keys(target).find((key) => !sourceKeys.includes(key));
2149
+ const sourceKeys = Array.isArray(source) ? source : objectKeys(source);
2150
+ const invalidKey = objectKeys(target).find((key) => !sourceKeys.includes(key));
2086
2151
  if (invalidKey)
2087
2152
  throw new DisTubeError("INVALID_KEY", sourceName, invalidKey);
2088
2153
  }
@@ -2095,6 +2160,12 @@ function isRecord(obj) {
2095
2160
  return isObject(obj);
2096
2161
  }
2097
2162
  __name(isRecord, "isRecord");
2163
+ function objectKeys(obj) {
2164
+ if (!isObject(obj))
2165
+ return [];
2166
+ return Object.keys(obj);
2167
+ }
2168
+ __name(objectKeys, "objectKeys");
2098
2169
 
2099
2170
  // src/plugin/DirectLink.ts
2100
2171
  var import_undici = require("undici");
@@ -2111,10 +2182,14 @@ var DirectLinkPlugin = class extends ExtractorPlugin {
2111
2182
  }
2112
2183
  async resolve(url, options = {}) {
2113
2184
  url = url.replace(/\/+$/, "");
2114
- return new Song({
2115
- name: url.substring(url.lastIndexOf("/") + 1).replace(/((\?|#).*)?$/, "") || url,
2116
- url
2117
- }, options);
2185
+ return new Song(
2186
+ {
2187
+ name: url.substring(url.lastIndexOf("/") + 1).replace(/((\?|#).*)?$/, "") || url,
2188
+ url,
2189
+ src: "direct_link"
2190
+ },
2191
+ options
2192
+ );
2118
2193
  }
2119
2194
  };
2120
2195
  __name(DirectLinkPlugin, "DirectLinkPlugin");
@@ -2228,7 +2303,9 @@ ${e.message}`;
2228
2303
  throw new DisTubeError("INVALID_TYPE", "Array", songs, "songs");
2229
2304
  if (!songs.length)
2230
2305
  throw new DisTubeError("EMPTY_ARRAY", "songs");
2231
- const filteredSongs = songs.filter((song) => song instanceof Song || isURL(song) || typeof song !== "string" && song.type === "video" /* VIDEO */);
2306
+ const filteredSongs = songs.filter(
2307
+ (song) => song instanceof Song || isURL(song) || typeof song !== "string" && song.type === "video" /* VIDEO */
2308
+ );
2232
2309
  if (!filteredSongs.length)
2233
2310
  throw new DisTubeError("NO_VALID_SONG");
2234
2311
  if (member && !isMemberInstance(member)) {
@@ -2238,7 +2315,9 @@ ${e.message}`;
2238
2315
  throw new DisTubeError("NO_VALID_SONG");
2239
2316
  let resolvedSongs;
2240
2317
  if (parallel) {
2241
- const promises = filteredSongs.map((song) => this.handler.resolve(song, { member, metadata }).catch(() => void 0));
2318
+ const promises = filteredSongs.map(
2319
+ (song) => this.handler.resolve(song, { member, metadata }).catch(() => void 0)
2320
+ );
2242
2321
  resolvedSongs = (await Promise.all(promises)).filter((s) => !!s);
2243
2322
  } else {
2244
2323
  const resolved = [];
@@ -2360,7 +2439,9 @@ ${e.message}`;
2360
2439
  } else {
2361
2440
  console.error(error);
2362
2441
  console.warn("Unhandled 'error' event.");
2363
- console.warn("See: https://distube.js.org/#/docs/DisTube/stable/class/DisTube?scrollTo=e-error and https://nodejs.org/api/events.html#events_error_events");
2442
+ console.warn(
2443
+ "See: https://distube.js.org/#/docs/DisTube/stable/class/DisTube?scrollTo=e-error and https://nodejs.org/api/events.html#events_error_events"
2444
+ );
2364
2445
  }
2365
2446
  }
2366
2447
  };
@@ -2377,6 +2458,7 @@ __name(DisTube, "DisTube");
2377
2458
  DisTubeStream,
2378
2459
  DisTubeVoice,
2379
2460
  DisTubeVoiceManager,
2461
+ Events,
2380
2462
  ExtractorPlugin,
2381
2463
  FilterManager,
2382
2464
  GuildIdManager,
@@ -2410,6 +2492,7 @@ __name(DisTube, "DisTube");
2410
2492
  isTextChannelInstance,
2411
2493
  isURL,
2412
2494
  isVoiceChannelEmpty,
2495
+ objectKeys,
2413
2496
  parseNumber,
2414
2497
  resolveGuildId,
2415
2498
  toSecond,