distube 4.0.3 → 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,7 +59,7 @@ var require_package = __commonJS({
56
59
  "package.json"(exports, module2) {
57
60
  module2.exports = {
58
61
  name: "distube",
59
- version: "4.0.3",
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
65
  types: "./dist/index.d.ts",
@@ -70,7 +73,7 @@ var require_package = __commonJS({
70
73
  ],
71
74
  scripts: {
72
75
  test: "jest",
73
- 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",
74
77
  lint: "prettier --check . && eslint .",
75
78
  "lint:fix": "eslint . --fix",
76
79
  prettier: 'prettier --write "**/*.{ts,json,yml,yaml,md}"',
@@ -133,11 +136,11 @@ var require_package = __commonJS({
133
136
  "@commitlint/cli": "^17.0.3",
134
137
  "@commitlint/config-conventional": "^17.0.3",
135
138
  "@discordjs/voice": "^0.11.0",
136
- "@distube/docgen": "distubejs/docgen",
139
+ "@distubejs/docgen": "distubejs/docgen",
137
140
  "@types/jest": "^28.1.6",
138
- "@types/node": "^18.6.0",
139
- "@typescript-eslint/eslint-plugin": "^5.30.7",
140
- "@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",
141
144
  "babel-jest": "^28.1.3",
142
145
  "discord.js": "^14.0.3",
143
146
  eslint: "^8.20.0",
@@ -152,7 +155,7 @@ var require_package = __commonJS({
152
155
  "npm-check-updates": "^16.0.0",
153
156
  pinst: "^3.0.0",
154
157
  prettier: "^2.7.1",
155
- tsup: "^6.1.3",
158
+ tsup: "^6.2.0",
156
159
  typescript: "^4.7.4"
157
160
  },
158
161
  peerDependencies: {
@@ -239,25 +242,6 @@ __export(src_exports, {
239
242
  module.exports = __toCommonJS(src_exports);
240
243
 
241
244
  // src/type.ts
242
- var Events = /* @__PURE__ */ ((Events2) => {
243
- Events2["ERROR"] = "error";
244
- Events2["ADD_LIST"] = "addList";
245
- Events2["ADD_SONG"] = "addSong";
246
- Events2["PLAY_SONG"] = "playSong";
247
- Events2["FINISH_SONG"] = "finishSong";
248
- Events2["EMPTY"] = "empty";
249
- Events2["FINISH"] = "finish";
250
- Events2["INIT_QUEUE"] = "initQueue";
251
- Events2["NO_RELATED"] = "noRelated";
252
- Events2["DISCONNECT"] = "disconnect";
253
- Events2["DELETE_QUEUE"] = "deleteQueue";
254
- Events2["SEARCH_CANCEL"] = "searchCancel";
255
- Events2["SEARCH_NO_RESULT"] = "searchNoResult";
256
- Events2["SEARCH_DONE"] = "searchDone";
257
- Events2["SEARCH_INVALID_ANSWER"] = "searchInvalidAnswer";
258
- Events2["SEARCH_RESULT"] = "searchResult";
259
- return Events2;
260
- })(Events || {});
261
245
  var RepeatMode = /* @__PURE__ */ ((RepeatMode2) => {
262
246
  RepeatMode2[RepeatMode2["DISABLED"] = 0] = "DISABLED";
263
247
  RepeatMode2[RepeatMode2["SONG"] = 1] = "SONG";
@@ -279,6 +263,25 @@ var StreamType = /* @__PURE__ */ ((StreamType2) => {
279
263
  StreamType2[StreamType2["RAW"] = 1] = "RAW";
280
264
  return StreamType2;
281
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 || {});
282
285
 
283
286
  // src/constant.ts
284
287
  var defaultFilters = {
@@ -920,458 +923,140 @@ join_fn = /* @__PURE__ */ __name(function(channel) {
920
923
  });
921
924
  }, "#join");
922
925
 
923
- // src/core/manager/BaseManager.ts
924
- var import_discord2 = require("discord.js");
925
- var BaseManager = class extends DisTubeBase {
926
- constructor() {
927
- super(...arguments);
928
- __publicField(this, "collection", new import_discord2.Collection());
929
- }
930
- get size() {
931
- return this.collection.size;
932
- }
933
- };
934
- __name(BaseManager, "BaseManager");
935
-
936
- // src/core/manager/GuildIdManager.ts
937
- var GuildIdManager = class extends BaseManager {
938
- add(idOrInstance, data) {
939
- const id = resolveGuildId(idOrInstance);
940
- const existing = this.get(id);
941
- if (existing)
942
- return this;
943
- return this.collection.set(id, data);
944
- }
945
- get(idOrInstance) {
946
- return this.collection.get(resolveGuildId(idOrInstance));
947
- }
948
- remove(idOrInstance) {
949
- return this.collection.delete(resolveGuildId(idOrInstance));
950
- }
951
- has(idOrInstance) {
952
- return this.collection.has(resolveGuildId(idOrInstance));
953
- }
954
- };
955
- __name(GuildIdManager, "GuildIdManager");
956
-
957
- // src/core/manager/DisTubeVoiceManager.ts
958
- var import_voice2 = require("@discordjs/voice");
959
- var DisTubeVoiceManager = class extends GuildIdManager {
960
- create(channel) {
961
- const existing = this.get(channel.guildId);
962
- if (existing) {
963
- existing.channel = channel;
964
- return existing;
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
+ });
965
963
  }
966
- return new DisTubeVoice(this, channel);
967
- }
968
- join(channel) {
969
- const existing = this.get(channel.guildId);
970
- if (existing)
971
- return existing.join(channel);
972
- return this.create(channel).join();
973
964
  }
974
- leave(guild) {
975
- const voice = this.get(guild);
976
- if (voice) {
977
- voice.leave();
978
- } else {
979
- const connection = (0, import_voice2.getVoiceConnection)(resolveGuildId(guild), this.client.user?.id) ?? (0, import_voice2.getVoiceConnection)(resolveGuildId(guild));
980
- if (connection && connection.state.status !== import_voice2.VoiceConnectionStatus.Destroyed) {
981
- connection.destroy();
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;
982
975
  }
983
976
  }
977
+ return options;
984
978
  }
985
- };
986
- __name(DisTubeVoiceManager, "DisTubeVoiceManager");
987
-
988
- // src/core/manager/FilterManager.ts
989
- var _validate, validate_fn, _resolveName, resolveName_fn, _resolveValue, resolveValue_fn, _apply, apply_fn;
990
- var FilterManager = class extends BaseManager {
991
- constructor(queue) {
992
- super(queue.distube);
993
- __privateAdd(this, _validate);
994
- __privateAdd(this, _resolveName);
995
- __privateAdd(this, _resolveValue);
996
- __privateAdd(this, _apply);
997
- __publicField(this, "queue");
998
- this.queue = queue;
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);
999
983
  }
1000
- add(filterOrFilters, override = false) {
1001
- if (Array.isArray(filterOrFilters)) {
1002
- const resolvedFilters = filterOrFilters.map((f) => __privateMethod(this, _validate, validate_fn).call(this, f));
1003
- const newFilters = resolvedFilters.reduceRight((unique, o) => {
1004
- if (!unique.some((obj) => obj === o && obj.name === o) && !unique.some((obj) => obj !== o.name && obj.name !== o.name)) {
1005
- if (!this.has(o))
1006
- unique.push(o);
1007
- if (this.has(o) && override) {
1008
- this.remove(o);
1009
- unique.push(o);
1010
- }
1011
- }
1012
- return unique;
1013
- }, []).reverse();
1014
- return this.set([...this.collection.values(), ...newFilters]);
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;
1015
991
  }
1016
- return this.set([...this.collection.values(), filterOrFilters]);
1017
- }
1018
- clear() {
1019
- return this.set([]);
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);
1020
1013
  }
1021
- set(filters) {
1022
- this.collection.clear();
1023
- for (const filter of filters) {
1024
- const resolved = __privateMethod(this, _validate, validate_fn).call(this, filter);
1025
- this.collection.set(__privateMethod(this, _resolveName, resolveName_fn).call(this, resolved), resolved);
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;
1026
1022
  }
1027
- __privateMethod(this, _apply, apply_fn).call(this);
1028
- return this;
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 });
1029
1039
  }
1030
- remove(filterOrFilters) {
1031
- const remove = /* @__PURE__ */ __name((f) => this.collection.delete(__privateMethod(this, _resolveName, resolveName_fn).call(this, __privateMethod(this, _validate, validate_fn).call(this, f))), "remove");
1032
- if (Array.isArray(filterOrFilters))
1033
- filterOrFilters.map(remove);
1034
- else
1035
- remove(filterOrFilters);
1036
- __privateMethod(this, _apply, apply_fn).call(this);
1037
- return this;
1038
- }
1039
- has(filter) {
1040
- return this.collection.has(__privateMethod(this, _resolveName, resolveName_fn).call(this, filter));
1041
- }
1042
- get names() {
1043
- return this.collection.map((f) => __privateMethod(this, _resolveName, resolveName_fn).call(this, f));
1044
- }
1045
- get values() {
1046
- return this.collection.map((f) => __privateMethod(this, _resolveValue, resolveValue_fn).call(this, f));
1047
- }
1048
- toString() {
1049
- return this.names.toString();
1050
- }
1051
- };
1052
- __name(FilterManager, "FilterManager");
1053
- _validate = new WeakSet();
1054
- validate_fn = /* @__PURE__ */ __name(function(filter) {
1055
- if (typeof filter === "string" && Object.prototype.hasOwnProperty.call(this.distube.filters, filter) || typeof filter === "object" && typeof filter.name === "string" && typeof filter.value === "string") {
1056
- return filter;
1057
- }
1058
- throw new DisTubeError("INVALID_TYPE", "FilterResolvable", filter, "filter");
1059
- }, "#validate");
1060
- _resolveName = new WeakSet();
1061
- resolveName_fn = /* @__PURE__ */ __name(function(filter) {
1062
- return typeof filter === "string" ? filter : filter.name;
1063
- }, "#resolveName");
1064
- _resolveValue = new WeakSet();
1065
- resolveValue_fn = /* @__PURE__ */ __name(function(filter) {
1066
- return typeof filter === "string" ? this.distube.filters[filter] : filter.value;
1067
- }, "#resolveValue");
1068
- _apply = new WeakSet();
1069
- apply_fn = /* @__PURE__ */ __name(function() {
1070
- this.queue.beginTime = this.queue.currentTime;
1071
- this.queues.playSong(this.queue);
1072
- }, "#apply");
1073
-
1074
- // src/core/manager/QueueManager.ts
1075
- var _voiceEventHandler, voiceEventHandler_fn, _handleSongFinish, handleSongFinish_fn, _handlePlayingError, handlePlayingError_fn, _emitPlaySong, emitPlaySong_fn;
1076
- var QueueManager = class extends GuildIdManager {
1077
- constructor() {
1078
- super(...arguments);
1079
- __privateAdd(this, _voiceEventHandler);
1080
- __privateAdd(this, _handleSongFinish);
1081
- __privateAdd(this, _handlePlayingError);
1082
- __privateAdd(this, _emitPlaySong);
1083
- }
1084
- async create(channel, song, textChannel) {
1085
- if (this.has(channel.guildId))
1086
- throw new DisTubeError("QUEUE_EXIST");
1087
- const voice = this.voices.create(channel);
1088
- const queue = new Queue(this.distube, voice, song, textChannel);
1089
- await queue._taskQueue.queuing();
1090
- try {
1091
- await voice.join();
1092
- __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1093
- this.add(queue.id, queue);
1094
- this.emit("initQueue", queue);
1095
- const err = await this.playSong(queue);
1096
- return err || queue;
1097
- } finally {
1098
- queue._taskQueue.resolve();
1099
- }
1100
- }
1101
- createStream(queue) {
1102
- const { duration, formats, isLive, source, streamURL } = queue.songs[0];
1103
- const ffmpegArgs = queue.filters.size ? ["-af", queue.filters.values.join(",")] : void 0;
1104
- const seek = duration ? queue.beginTime : void 0;
1105
- const streamOptions = { ffmpegArgs, seek, isLive, type: this.options.streamType };
1106
- if (source === "youtube")
1107
- return DisTubeStream.YouTube(formats, streamOptions);
1108
- return DisTubeStream.DirectLink(streamURL, streamOptions);
1109
- }
1110
- async playSong(queue) {
1111
- if (!queue)
1112
- return true;
1113
- if (!queue.songs.length) {
1114
- queue.stop();
1115
- return true;
1116
- }
1117
- if (queue.stopped)
1118
- return false;
1119
- try {
1120
- const song = queue.songs[0];
1121
- const { url, source, formats, streamURL } = song;
1122
- if (source === "youtube" && !formats)
1123
- song._patchYouTube(await this.handler.getYouTubeInfo(url));
1124
- if (source !== "youtube" && !streamURL) {
1125
- for (const plugin of [...this.distube.extractorPlugins, ...this.distube.customPlugins]) {
1126
- if (await plugin.validate(url)) {
1127
- const info = [plugin.getStreamURL(url), plugin.getRelatedSongs(url)];
1128
- const result = await Promise.all(info);
1129
- song.streamURL = result[0];
1130
- song.related = result[1];
1131
- break;
1132
- }
1133
- }
1134
- }
1135
- const stream = this.createStream(queue);
1136
- queue.voice.play(stream);
1137
- song.streamURL = stream.url;
1138
- if (queue.stopped)
1139
- queue.stop();
1140
- else if (queue.paused)
1141
- queue.voice.pause();
1142
- return false;
1143
- } catch (e) {
1144
- __privateMethod(this, _handlePlayingError, handlePlayingError_fn).call(this, queue, e);
1145
- return true;
1146
- }
1147
- }
1148
- };
1149
- __name(QueueManager, "QueueManager");
1150
- _voiceEventHandler = new WeakSet();
1151
- voiceEventHandler_fn = /* @__PURE__ */ __name(function(queue) {
1152
- queue._listeners = {
1153
- disconnect: (error) => {
1154
- queue.remove();
1155
- this.emit("disconnect", queue);
1156
- if (error)
1157
- this.emitError(error, queue.textChannel);
1158
- },
1159
- error: (error) => __privateMethod(this, _handlePlayingError, handlePlayingError_fn).call(this, queue, error),
1160
- finish: () => __privateMethod(this, _handleSongFinish, handleSongFinish_fn).call(this, queue)
1161
- };
1162
- for (const event of objectKeys(queue._listeners)) {
1163
- queue.voice.on(event, queue._listeners[event]);
1164
- }
1165
- }, "#voiceEventHandler");
1166
- _handleSongFinish = new WeakSet();
1167
- handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
1168
- this.emit("finishSong", queue, queue.songs[0]);
1169
- await queue._taskQueue.queuing();
1170
- try {
1171
- if (queue.stopped)
1172
- return;
1173
- if (queue.repeatMode === 2 /* QUEUE */ && !queue._prev)
1174
- queue.songs.push(queue.songs[0]);
1175
- if (queue._prev) {
1176
- if (queue.repeatMode === 2 /* QUEUE */)
1177
- queue.songs.unshift(queue.songs.pop());
1178
- else
1179
- queue.songs.unshift(queue.previousSongs.pop());
1180
- }
1181
- if (queue.songs.length <= 1 && (queue._next || queue.repeatMode === 0 /* DISABLED */)) {
1182
- if (queue.autoplay) {
1183
- try {
1184
- await queue.addRelatedSong();
1185
- } catch {
1186
- this.emit("noRelated", queue);
1187
- }
1188
- }
1189
- if (queue.songs.length <= 1) {
1190
- if (this.options.leaveOnFinish)
1191
- queue.voice.leave();
1192
- if (!queue.autoplay)
1193
- this.emit("finish", queue);
1194
- queue.remove();
1195
- return;
1196
- }
1197
- }
1198
- const emitPlaySong = __privateMethod(this, _emitPlaySong, emitPlaySong_fn).call(this, queue);
1199
- if (!queue._prev && (queue.repeatMode !== 1 /* SONG */ || queue._next)) {
1200
- const prev = queue.songs.shift();
1201
- delete prev.formats;
1202
- delete prev.streamURL;
1203
- if (this.options.savePreviousSongs)
1204
- queue.previousSongs.push(prev);
1205
- else
1206
- queue.previousSongs.push({ id: prev.id });
1207
- }
1208
- queue._next = queue._prev = false;
1209
- queue.beginTime = 0;
1210
- const err = await this.playSong(queue);
1211
- if (!err && emitPlaySong)
1212
- this.emit("playSong", queue, queue.songs[0]);
1213
- } finally {
1214
- queue._taskQueue.resolve();
1215
- }
1216
- }, "#handleSongFinish");
1217
- _handlePlayingError = new WeakSet();
1218
- handlePlayingError_fn = /* @__PURE__ */ __name(function(queue, error) {
1219
- const song = queue.songs.shift();
1220
- try {
1221
- error.name = "PlayingError";
1222
- error.message = `${error.message}
1223
- Id: ${song.id}
1224
- Name: ${song.name}`;
1225
- } catch {
1226
- }
1227
- this.emitError(error, queue.textChannel);
1228
- if (queue.songs.length > 0) {
1229
- queue._next = queue._prev = false;
1230
- queue.beginTime = 0;
1231
- this.playSong(queue).then((e) => {
1232
- if (!e)
1233
- this.emit("playSong", queue, queue.songs[0]);
1234
- });
1235
- } else {
1236
- queue.stop();
1237
- }
1238
- }, "#handlePlayingError");
1239
- _emitPlaySong = new WeakSet();
1240
- emitPlaySong_fn = /* @__PURE__ */ __name(function(queue) {
1241
- return !this.options.emitNewSongOnly || queue.repeatMode === 1 /* SONG */ && queue._next || queue.repeatMode !== 1 /* SONG */ && queue.songs[0]?.id !== queue.songs[1]?.id;
1242
- }, "#emitPlaySong");
1243
-
1244
- // src/core/DisTubeHandler.ts
1245
- var import_ytpl = __toESM(require("@distube/ytpl"));
1246
- var import_ytdl_core = __toESM(require("@distube/ytdl-core"));
1247
- var DisTubeHandler = class extends DisTubeBase {
1248
- constructor(distube) {
1249
- super(distube);
1250
- const client = this.client;
1251
- if (this.options.leaveOnEmpty) {
1252
- client.on("voiceStateUpdate", (oldState) => {
1253
- if (!oldState?.channel)
1254
- return;
1255
- const queue = this.queues.get(oldState);
1256
- if (!queue) {
1257
- if (isVoiceChannelEmpty(oldState)) {
1258
- setTimeout(() => {
1259
- if (!this.queues.get(oldState) && isVoiceChannelEmpty(oldState))
1260
- this.voices.leave(oldState);
1261
- }, this.options.emptyCooldown * 1e3).unref();
1262
- }
1263
- return;
1264
- }
1265
- if (queue._emptyTimeout) {
1266
- clearTimeout(queue._emptyTimeout);
1267
- delete queue._emptyTimeout;
1268
- }
1269
- if (isVoiceChannelEmpty(oldState)) {
1270
- queue._emptyTimeout = setTimeout(() => {
1271
- delete queue._emptyTimeout;
1272
- if (isVoiceChannelEmpty(oldState)) {
1273
- queue.voice.leave();
1274
- this.emit("empty", queue);
1275
- if (queue.stopped)
1276
- queue.remove();
1277
- }
1278
- }, this.options.emptyCooldown * 1e3).unref();
1279
- }
1280
- });
1281
- }
1282
- }
1283
- get ytdlOptions() {
1284
- const options = this.options.ytdlOptions;
1285
- if (this.options.youtubeCookie) {
1286
- if (!options.requestOptions)
1287
- options.requestOptions = {};
1288
- if (!options.requestOptions.headers)
1289
- options.requestOptions.headers = {};
1290
- options.requestOptions.headers.cookie = this.options.youtubeCookie;
1291
- if (this.options.youtubeIdentityToken) {
1292
- options.requestOptions.headers["x-youtube-identity-token"] = this.options.youtubeIdentityToken;
1293
- }
1294
- }
1295
- return options;
1296
- }
1297
- getYouTubeInfo(url, basic = false) {
1298
- if (basic)
1299
- return import_ytdl_core.default.getBasicInfo(url, this.ytdlOptions);
1300
- return import_ytdl_core.default.getInfo(url, this.ytdlOptions);
1301
- }
1302
- async resolve(song, options = {}) {
1303
- if (song instanceof Song || song instanceof Playlist) {
1304
- if ("metadata" in options)
1305
- song.metadata = options.metadata;
1306
- if ("member" in options)
1307
- song.member = options.member;
1308
- return song;
1309
- }
1310
- if (song instanceof SearchResultVideo)
1311
- return new Song(song, options);
1312
- if (song instanceof SearchResultPlaylist)
1313
- return this.resolvePlaylist(song.url, options);
1314
- if (isObject(song)) {
1315
- if (!("url" in song) && !("id" in song))
1316
- throw new DisTubeError("CANNOT_RESOLVE_SONG", song);
1317
- return new Song(song, options);
1318
- }
1319
- if (import_ytpl.default.validateID(song))
1320
- return this.resolvePlaylist(song, options);
1321
- if (import_ytdl_core.default.validateURL(song))
1322
- return new Song(await this.getYouTubeInfo(song), options);
1323
- if (isURL(song)) {
1324
- for (const plugin of this.distube.extractorPlugins) {
1325
- if (await plugin.validate(song))
1326
- return plugin.resolve(song, options);
1327
- }
1328
- throw new DisTubeError("NOT_SUPPORTED_URL");
1329
- }
1330
- throw new DisTubeError("CANNOT_RESOLVE_SONG", song);
1331
- }
1332
- async resolvePlaylist(playlist, options = {}) {
1333
- const { member, source, metadata } = { source: "youtube", ...options };
1334
- if (playlist instanceof Playlist) {
1335
- if ("metadata" in options)
1336
- playlist.metadata = metadata;
1337
- if ("member" in options)
1338
- playlist.member = member;
1339
- return playlist;
1340
- }
1341
- if (typeof playlist === "string") {
1342
- const info = await (0, import_ytpl.default)(playlist, { limit: Infinity });
1343
- const songs = info.items.filter((v) => !v.thumbnail.includes("no_thumbnail")).map((v) => new Song(v, { member, metadata }));
1344
- return new Playlist({
1345
- source,
1346
- songs,
1347
- member,
1348
- name: info.title,
1349
- url: info.url,
1350
- thumbnail: songs[0].thumbnail
1351
- }, { metadata });
1352
- }
1353
- return new Playlist(playlist, { member, properties: { source }, metadata });
1354
- }
1355
- async searchSong(message, query) {
1356
- if (!isMessageInstance(message))
1357
- throw new DisTubeError("INVALID_TYPE", "Discord.Message", message, "message");
1358
- if (typeof query !== "string")
1359
- throw new DisTubeError("INVALID_TYPE", "string", query, "query");
1360
- if (query.length === 0)
1361
- throw new DisTubeError("EMPTY_STRING", "query");
1362
- const limit = this.options.searchSongs > 1 ? this.options.searchSongs : 1;
1363
- const results = await this.distube.search(query, {
1364
- limit,
1365
- safeSearch: this.options.nsfw ? false : !message.channel?.nsfw
1366
- }).catch(() => {
1367
- if (!this.emit("searchNoResult", message, query)) {
1368
- console.warn("searchNoResult event does not have any listeners! Emits `error` event instead.");
1369
- throw new DisTubeError("NO_RESULT");
1370
- }
1371
- });
1372
- if (!results)
1373
- return null;
1374
- return this.createSearchMessageCollector(message, results, query);
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);
1375
1060
  }
1376
1061
  async createSearchMessageCollector(message, results, query) {
1377
1062
  if (!isMessageInstance(message))
@@ -1390,8 +1075,10 @@ var DisTubeHandler = class extends DisTubeBase {
1390
1075
  for (const evn of searchEvents) {
1391
1076
  if (this.distube.listenerCount(evn) === 0) {
1392
1077
  console.warn(`"searchSongs" option is disabled due to missing "${evn}" listener.`);
1393
- console.warn(`If you don't want to use "${evn}" event, simply add an empty listener (not recommended):
1394
- <DisTube>.on("${evn}", () => {})`);
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
+ );
1395
1082
  this.options.searchSongs = 0;
1396
1083
  }
1397
1084
  }
@@ -1554,13 +1241,23 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1554
1241
  throw new DisTubeError("INVALID_TYPE", "boolean", options.savePreviousSongs, "DisTubeOptions.savePreviousSongs");
1555
1242
  }
1556
1243
  if (typeof options.joinNewVoiceChannel !== "boolean") {
1557
- 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
+ );
1558
1250
  }
1559
1251
  if (typeof options.youtubeCookie !== "undefined" && typeof options.youtubeCookie !== "string") {
1560
1252
  throw new DisTubeError("INVALID_TYPE", "string", options.youtubeCookie, "DisTubeOptions.youtubeCookie");
1561
1253
  }
1562
1254
  if (typeof options.youtubeIdentityToken !== "undefined" && typeof options.youtubeIdentityToken !== "string") {
1563
- 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
+ );
1564
1261
  }
1565
1262
  if (typeof options.customFilters !== "undefined" && typeof options.customFilters !== "object" || Array.isArray(options.customFilters)) {
1566
1263
  throw new DisTubeError("INVALID_TYPE", "object", options.customFilters, "DisTubeOptions.customFilters");
@@ -1583,91 +1280,425 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1583
1280
  if (typeof options.nsfw !== "boolean") {
1584
1281
  throw new DisTubeError("INVALID_TYPE", "boolean", options.nsfw, "DisTubeOptions.nsfw");
1585
1282
  }
1586
- if (typeof options.emitAddSongWhenCreatingQueue !== "boolean") {
1587
- throw new DisTubeError("INVALID_TYPE", "boolean", options.emitAddSongWhenCreatingQueue, "DisTubeOptions.emitAddSongWhenCreatingQueue");
1283
+ if (typeof options.emitAddSongWhenCreatingQueue !== "boolean") {
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));
1588
1501
  }
1589
- if (typeof options.emitAddListWhenCreatingQueue !== "boolean") {
1590
- 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));
1591
1504
  }
1592
- if (typeof options.streamType !== "number" || isNaN(options.streamType) || !StreamType[options.streamType]) {
1593
- throw new DisTubeError("INVALID_TYPE", "StreamType", options.streamType, "DisTubeOptions.streamType");
1505
+ toString() {
1506
+ return this.names.toString();
1594
1507
  }
1595
- if (typeof options.directLink !== "boolean") {
1596
- 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;
1597
1514
  }
1598
- }, "#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");
1599
1530
 
1600
- // src/core/DisTubeStream.ts
1601
- var import_prism_media = require("prism-media");
1602
- var import_voice3 = require("@discordjs/voice");
1603
- var chooseBestVideoFormat = /* @__PURE__ */ __name((formats, isLive = false) => {
1604
- let filter = /* @__PURE__ */ __name((format) => format.hasAudio, "filter");
1605
- if (isLive)
1606
- filter = /* @__PURE__ */ __name((format) => format.hasAudio && format.isHLS, "filter");
1607
- formats = formats.filter(filter).sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate) || Number(a.bitrate) - Number(b.bitrate));
1608
- return formats.find((format) => !format.hasVideo) || formats.sort((a, b) => Number(a.bitrate) - Number(b.bitrate))[0];
1609
- }, "chooseBestVideoFormat");
1610
- var DisTubeStream = class {
1611
- constructor(url, options) {
1612
- __publicField(this, "type");
1613
- __publicField(this, "stream");
1614
- __publicField(this, "url");
1615
- this.url = url;
1616
- this.type = !options.type ? import_voice3.StreamType.OggOpus : import_voice3.StreamType.Raw;
1617
- const args = [
1618
- "-reconnect",
1619
- "1",
1620
- "-reconnect_streamed",
1621
- "1",
1622
- "-reconnect_delay_max",
1623
- "5",
1624
- "-i",
1625
- url,
1626
- "-analyzeduration",
1627
- "0",
1628
- "-loglevel",
1629
- "0",
1630
- "-ar",
1631
- "48000",
1632
- "-ac",
1633
- "2",
1634
- "-f"
1635
- ];
1636
- if (!options.type) {
1637
- args.push("opus", "-acodec", "libopus");
1638
- } else {
1639
- 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();
1640
1556
  }
1641
- if (typeof options.seek === "number" && options.seek > 0) {
1642
- 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;
1643
1573
  }
1644
- if (Array.isArray(options.ffmpegArgs)) {
1645
- 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;
1646
1605
  }
1647
- this.stream = new import_prism_media.FFmpeg({ args, shell: false });
1648
1606
  }
1649
- static YouTube(formats, options = {}) {
1650
- if (!formats || !formats.length)
1651
- throw new DisTubeError("UNAVAILABLE_VIDEO");
1652
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1653
- throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1654
- }
1655
- const bestFormat = chooseBestVideoFormat(formats, options.isLive);
1656
- if (!bestFormat)
1657
- throw new DisTubeError("UNPLAYABLE_FORMATS");
1658
- 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]);
1659
1623
  }
1660
- static DirectLink(url, options = {}) {
1661
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1662
- 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());
1663
1639
  }
1664
- if (typeof url !== "string" || !isURL(url)) {
1665
- 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
+ }
1666
1656
  }
1667
- 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();
1668
1674
  }
1669
- };
1670
- __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");
1671
1702
 
1672
1703
  // src/struct/Queue.ts
1673
1704
  var _filters;
@@ -2151,10 +2182,14 @@ var DirectLinkPlugin = class extends ExtractorPlugin {
2151
2182
  }
2152
2183
  async resolve(url, options = {}) {
2153
2184
  url = url.replace(/\/+$/, "");
2154
- return new Song({
2155
- name: url.substring(url.lastIndexOf("/") + 1).replace(/((\?|#).*)?$/, "") || url,
2156
- url
2157
- }, 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
+ );
2158
2193
  }
2159
2194
  };
2160
2195
  __name(DirectLinkPlugin, "DirectLinkPlugin");
@@ -2268,7 +2303,9 @@ ${e.message}`;
2268
2303
  throw new DisTubeError("INVALID_TYPE", "Array", songs, "songs");
2269
2304
  if (!songs.length)
2270
2305
  throw new DisTubeError("EMPTY_ARRAY", "songs");
2271
- 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
+ );
2272
2309
  if (!filteredSongs.length)
2273
2310
  throw new DisTubeError("NO_VALID_SONG");
2274
2311
  if (member && !isMemberInstance(member)) {
@@ -2278,7 +2315,9 @@ ${e.message}`;
2278
2315
  throw new DisTubeError("NO_VALID_SONG");
2279
2316
  let resolvedSongs;
2280
2317
  if (parallel) {
2281
- 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
+ );
2282
2321
  resolvedSongs = (await Promise.all(promises)).filter((s) => !!s);
2283
2322
  } else {
2284
2323
  const resolved = [];
@@ -2400,7 +2439,9 @@ ${e.message}`;
2400
2439
  } else {
2401
2440
  console.error(error);
2402
2441
  console.warn("Unhandled 'error' event.");
2403
- 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
+ );
2404
2445
  }
2405
2446
  }
2406
2447
  };