distube 4.1.0 → 4.2.0

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
@@ -60,10 +60,10 @@ var __privateMethod = (obj, member, method) => {
60
60
 
61
61
  // package.json
62
62
  var require_package = __commonJS({
63
- "package.json"(exports, module2) {
63
+ "package.json"(exports2, module2) {
64
64
  module2.exports = {
65
65
  name: "distube",
66
- version: "4.1.0",
66
+ version: "4.2.0",
67
67
  description: "A Discord.js module to simplify your music commands and play songs with audio filters on Discord without any API key.",
68
68
  main: "./dist/index.js",
69
69
  types: "./dist/index.d.ts",
@@ -77,16 +77,16 @@ var require_package = __commonJS({
77
77
  ],
78
78
  scripts: {
79
79
  test: "jest",
80
- docs: "docgen -s src/**/*.ts -o docs.json -c pages/index.yml -g -j jsdoc.config.json",
80
+ docs: "typedoc",
81
81
  lint: "prettier --check . && eslint .",
82
82
  "lint:fix": "eslint . --fix",
83
83
  prettier: 'prettier --write "**/*.{ts,json,yml,yaml,md}"',
84
84
  build: "tsup",
85
85
  "build:check": "tsc --noEmit",
86
- update: "ncu -u && yarn up '**' -R",
87
- postinstall: "husky install",
88
- prepublishOnly: "yarn lint && yarn test",
89
- prepack: "yarn build && pinst --disable",
86
+ update: "pnpm up -L",
87
+ postinstall: "husky",
88
+ prepublishOnly: "pnpm run lint && pnpm run test",
89
+ prepack: "pnpm run build && pinst --disable",
90
90
  postpack: "pinst --enable",
91
91
  "dev:add-docs-to-worktree": "git worktree add --track -b docs docs origin/docs"
92
92
  },
@@ -112,70 +112,50 @@ var require_package = __commonJS({
112
112
  bugs: {
113
113
  url: "https://github.com/skick1234/DisTube/issues"
114
114
  },
115
- funding: [
116
- {
117
- type: "individual",
118
- url: "https://paypal.me/Skickkk"
119
- },
120
- {
121
- type: "patreon",
122
- url: "https://patreon.com/DisTube"
123
- }
124
- ],
115
+ funding: "https://github.com/skick1234/DisTube?sponsor=1",
125
116
  homepage: "https://distube.js.org/",
126
117
  dependencies: {
127
- "@distube/ytdl-core": "^4.12.1",
128
- "@distube/ytpl": "^1.1.4",
129
- "@distube/ytsr": "^1.1.10",
130
- "prism-media": "npm:@distube/prism-media@latest",
118
+ "@distube/ytdl-core": "^4.13.3",
119
+ "@distube/ytpl": "^1.2.1",
120
+ "@distube/ytsr": "^2.0.0",
131
121
  "tiny-typed-emitter": "^2.1.0",
132
122
  "tough-cookie": "^4.1.3",
133
123
  tslib: "^2.6.2",
134
- undici: "^5.23.0"
124
+ undici: "^6.13.0"
135
125
  },
136
126
  devDependencies: {
137
- "@babel/core": "^7.22.11",
138
- "@babel/plugin-proposal-class-properties": "^7.18.6",
139
- "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
140
- "@babel/plugin-transform-private-methods": "^7.22.5",
141
- "@babel/preset-env": "^7.22.10",
142
- "@babel/preset-typescript": "^7.22.11",
143
- "@commitlint/cli": "^17.7.1",
144
- "@commitlint/config-conventional": "^17.7.0",
145
- "@discordjs/voice": "^0.16.0",
146
- "@distubejs/docgen": "distubejs/docgen",
147
- "@types/jest": "^29.5.4",
148
- "@types/node": "^20.5.7",
149
- "@types/tough-cookie": "^4.0.2",
150
- "@typescript-eslint/eslint-plugin": "^6.5.0",
151
- "@typescript-eslint/parser": "^6.5.0",
152
- "babel-jest": "^29.6.4",
153
- "discord.js": "^14.13.0",
154
- eslint: "^8.48.0",
155
- "eslint-config-distube": "^1.6.4",
156
- "eslint-config-prettier": "^9.0.0",
157
- "eslint-plugin-deprecation": "^1.5.0",
158
- "eslint-plugin-jsdoc": "^46.5.1",
159
- husky: "^8.0.3",
160
- jest: "^29.6.4",
161
- "jsdoc-babel": "^0.5.0",
127
+ "@babel/core": "^7.24.4",
128
+ "@babel/plugin-transform-class-properties": "^7.24.1",
129
+ "@babel/plugin-transform-object-rest-spread": "^7.24.1",
130
+ "@babel/plugin-transform-private-methods": "^7.24.1",
131
+ "@babel/preset-env": "^7.24.4",
132
+ "@babel/preset-typescript": "^7.24.1",
133
+ "@commitlint/cli": "^19.2.2",
134
+ "@commitlint/config-conventional": "^19.2.2",
135
+ "@discordjs/voice": "^0.16.1",
136
+ "@types/jest": "^29.5.12",
137
+ "@types/node": "^20.12.7",
138
+ "@types/tough-cookie": "^4.0.5",
139
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
140
+ "@typescript-eslint/parser": "^7.7.0",
141
+ "babel-jest": "^29.7.0",
142
+ "discord.js": "^14.14.1",
143
+ eslint: "^8.57.0",
144
+ "eslint-config-distube": "^1.7.0",
145
+ husky: "^9.0.11",
146
+ jest: "^29.7.0",
162
147
  "nano-staged": "^0.8.0",
163
- "npm-check-updates": "^16.13.2",
164
148
  pinst: "^3.0.0",
165
- prettier: "^3.0.3",
166
- tsup: "^7.2.0",
167
- typescript: "^5.2.2"
149
+ prettier: "^3.2.5",
150
+ tsup: "^8.0.2",
151
+ typedoc: "^0.25.13",
152
+ "typedoc-material-theme": "^1.0.2",
153
+ typescript: "^5.4.5"
168
154
  },
169
155
  peerDependencies: {
170
- "@discordjs/opus": "*",
171
156
  "@discordjs/voice": "*",
172
157
  "discord.js": "14"
173
158
  },
174
- peerDependenciesMeta: {
175
- "@discordjs/opus": {
176
- optional: true
177
- }
178
- },
179
159
  "nano-staged": {
180
160
  "*.ts": [
181
161
  "prettier --write",
@@ -186,9 +166,8 @@ var require_package = __commonJS({
186
166
  ]
187
167
  },
188
168
  engines: {
189
- node: ">=16.9.0"
190
- },
191
- packageManager: "yarn@3.6.1"
169
+ node: ">=18.17"
170
+ }
192
171
  };
193
172
  }
194
173
  });
@@ -290,6 +269,7 @@ var Events = /* @__PURE__ */ ((Events2) => {
290
269
  Events2["SEARCH_DONE"] = "searchDone";
291
270
  Events2["SEARCH_INVALID_ANSWER"] = "searchInvalidAnswer";
292
271
  Events2["SEARCH_RESULT"] = "searchResult";
272
+ Events2["FFMPEG_DEBUG"] = "ffmpegDebug";
293
273
  return Events2;
294
274
  })(Events || {});
295
275
 
@@ -327,7 +307,12 @@ var defaultOptions = {
327
307
  emitAddListWhenCreatingQueue: true,
328
308
  joinNewVoiceChannel: true,
329
309
  streamType: 0 /* OPUS */,
330
- directLink: true
310
+ directLink: true,
311
+ ffmpegPath: "ffmpeg",
312
+ ffmpegDefaultArgs: {
313
+ analyzeduration: 0,
314
+ hide_banner: true
315
+ }
331
316
  };
332
317
 
333
318
  // src/struct/DisTubeError.ts
@@ -411,15 +396,13 @@ var _TaskQueue = class _TaskQueue {
411
396
  constructor() {
412
397
  /**
413
398
  * The task array
414
- * @type {Task[]}
415
- * @private
416
399
  */
417
400
  __privateAdd(this, _tasks, []);
418
401
  }
419
402
  /**
420
403
  * Waits for last task finished and queues a new task
421
- * @param {boolean} [resolveInfo=false] Whether the task is a resolving info task
422
- * @returns {Promise<void>}
404
+ *
405
+ * @param resolveInfo - Whether the task is a resolving info task
423
406
  */
424
407
  queuing(resolveInfo = false) {
425
408
  const next = this.remaining ? __privateGet(this, _tasks)[__privateGet(this, _tasks).length - 1].promise : Promise.resolve();
@@ -434,14 +417,12 @@ var _TaskQueue = class _TaskQueue {
434
417
  }
435
418
  /**
436
419
  * The remaining number of tasks
437
- * @type {number}
438
420
  */
439
421
  get remaining() {
440
422
  return __privateGet(this, _tasks).length;
441
423
  }
442
424
  /**
443
425
  * Whether or not having a resolving info task
444
- * @type {boolean}
445
426
  */
446
427
  get hasResolveTask() {
447
428
  return __privateGet(this, _tasks).some((t) => t.resolveInfo);
@@ -456,11 +437,9 @@ var _metadata, _member;
456
437
  var _Playlist = class _Playlist {
457
438
  /**
458
439
  * Create a playlist
459
- * @param {Song[]|PlaylistInfo} playlist Playlist
460
- * @param {Object} [options] Optional options
461
- * @param {Discord.GuildMember} [options.member] Requested user
462
- * @param {Object} [options.properties] Custom properties
463
- * @param {T} [options.metadata] Playlist metadata
440
+ *
441
+ * @param playlist - Playlist
442
+ * @param options - Optional options
464
443
  */
465
444
  constructor(playlist, options = {}) {
466
445
  __publicField(this, "source");
@@ -504,21 +483,18 @@ var _Playlist = class _Playlist {
504
483
  }
505
484
  /**
506
485
  * Playlist duration in second.
507
- * @type {number}
508
486
  */
509
487
  get duration() {
510
488
  return this.songs.reduce((prev, next) => prev + next.duration, 0);
511
489
  }
512
490
  /**
513
491
  * Formatted duration string `hh:mm:ss`.
514
- * @type {string}
515
492
  */
516
493
  get formattedDuration() {
517
494
  return formatDuration(this.duration);
518
495
  }
519
496
  /**
520
497
  * User requested.
521
- * @type {Discord.GuildMember?}
522
498
  */
523
499
  get member() {
524
500
  return __privateGet(this, _member);
@@ -531,7 +507,6 @@ var _Playlist = class _Playlist {
531
507
  }
532
508
  /**
533
509
  * User requested.
534
- * @type {Discord.User?}
535
510
  */
536
511
  get user() {
537
512
  return this.member?.user;
@@ -553,7 +528,8 @@ var Playlist = _Playlist;
553
528
  var _ISearchResult = class _ISearchResult {
554
529
  /**
555
530
  * Create a search result
556
- * @param {Object} info ytsr result
531
+ *
532
+ * @param info - ytsr result
557
533
  */
558
534
  constructor(info) {
559
535
  __publicField(this, "source");
@@ -621,11 +597,9 @@ var _metadata2, _member2, _playlist;
621
597
  var _Song = class _Song {
622
598
  /**
623
599
  * Create a Song
624
- * @param {ytdl.videoInfo|SearchResult|OtherSongInfo} info Raw info
625
- * @param {Object} [options] Optional options
626
- * @param {Discord.GuildMember} [options.member] Requested user
627
- * @param {string} [options.source="youtube"] Song source
628
- * @param {T} [options.metadata] Song metadata
600
+ *
601
+ * @param info - Raw info
602
+ * @param options - Optional options
629
603
  */
630
604
  constructor(info, options = {}) {
631
605
  __publicField(this, "source");
@@ -702,8 +676,8 @@ var _Song = class _Song {
702
676
  }
703
677
  /**
704
678
  * Patch data from other source
705
- * @param {OtherSongInfo} info Video info
706
- * @private
679
+ *
680
+ * @param info - Video info
707
681
  */
708
682
  _patchOther(info) {
709
683
  this.id = info.id;
@@ -737,7 +711,6 @@ var _Song = class _Song {
737
711
  }
738
712
  /**
739
713
  * The playlist added this song
740
- * @type {Playlist?}
741
714
  */
742
715
  get playlist() {
743
716
  return __privateGet(this, _playlist);
@@ -750,7 +723,6 @@ var _Song = class _Song {
750
723
  }
751
724
  /**
752
725
  * User requested.
753
- * @type {Discord.GuildMember?}
754
726
  */
755
727
  get member() {
756
728
  return __privateGet(this, _member2);
@@ -761,7 +733,6 @@ var _Song = class _Song {
761
733
  }
762
734
  /**
763
735
  * User requested.
764
- * @type {Discord.User?}
765
736
  */
766
737
  get user() {
767
738
  return this.member?.user;
@@ -787,57 +758,48 @@ var _DisTubeBase = class _DisTubeBase {
787
758
  }
788
759
  /**
789
760
  * Emit the {@link DisTube} of this base
790
- * @param {string} eventName Event name
791
- * @param {...any} args arguments
792
- * @returns {boolean}
761
+ *
762
+ * @param eventName - Event name
763
+ * @param args - arguments
793
764
  */
794
765
  emit(eventName, ...args) {
795
766
  return this.distube.emit(eventName, ...args);
796
767
  }
797
768
  /**
798
769
  * Emit error event
799
- * @param {Error} error error
800
- * @param {Discord.BaseGuildTextChannel} [channel] Text channel where the error is encountered.
770
+ *
771
+ * @param error - error
772
+ * @param channel - Text channel where the error is encountered.
801
773
  */
802
774
  emitError(error, channel) {
803
775
  this.distube.emitError(error, channel);
804
776
  }
805
777
  /**
806
778
  * The queue manager
807
- * @type {QueueManager}
808
- * @readonly
809
779
  */
810
780
  get queues() {
811
781
  return this.distube.queues;
812
782
  }
813
783
  /**
814
784
  * The voice manager
815
- * @type {DisTubeVoiceManager}
816
- * @readonly
817
785
  */
818
786
  get voices() {
819
787
  return this.distube.voices;
820
788
  }
821
789
  /**
822
790
  * Discord.js client
823
- * @type {Discord.Client}
824
- * @readonly
825
791
  */
826
792
  get client() {
827
793
  return this.distube.client;
828
794
  }
829
795
  /**
830
796
  * DisTube options
831
- * @type {DisTubeOptions}
832
- * @readonly
833
797
  */
834
798
  get options() {
835
799
  return this.distube.options;
836
800
  }
837
801
  /**
838
802
  * DisTube handler
839
- * @type {DisTubeHandler}
840
- * @readonly
841
803
  */
842
804
  get handler() {
843
805
  return this.distube.handler;
@@ -863,6 +825,7 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
863
825
  __publicField(this, "audioResource");
864
826
  __publicField(this, "emittedError");
865
827
  __publicField(this, "isDisconnected", false);
828
+ __publicField(this, "stream");
866
829
  __privateAdd(this, _channel, void 0);
867
830
  __privateAdd(this, _volume, 100);
868
831
  this.voices = voiceManager;
@@ -906,7 +869,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
906
869
  }
907
870
  /**
908
871
  * The voice channel id the bot is in
909
- * @type {Snowflake?}
910
872
  */
911
873
  get channelId() {
912
874
  return this.connection?.joinConfig?.channelId ?? void 0;
@@ -949,8 +911,8 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
949
911
  }
950
912
  /**
951
913
  * Join a voice channel with this connection
952
- * @param {Discord.BaseGuildVoiceChannel} [channel] A voice channel
953
- * @returns {Promise<DisTubeVoice>}
914
+ *
915
+ * @param channel - A voice channel
954
916
  */
955
917
  async join(channel) {
956
918
  const TIMEOUT = 3e4;
@@ -970,7 +932,8 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
970
932
  }
971
933
  /**
972
934
  * Leave the voice channel of this connection
973
- * @param {Error} [error] Optional, an error to emit with 'error' event.
935
+ *
936
+ * @param error - Optional, an error to emit with 'error' event.
974
937
  */
975
938
  leave(error) {
976
939
  this.stop(true);
@@ -984,33 +947,36 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
984
947
  }
985
948
  /**
986
949
  * Stop the playing stream
987
- * @param {boolean} [force=false] If true, will force the {@link DisTubeVoice#audioPlayer} to enter the Idle state
988
- * even if the {@link DisTubeVoice#audioResource} has silence padding frames.
989
- * @private
950
+ *
951
+ * @param force - If true, will force the {@link DisTubeVoice#audioPlayer} to enter the Idle state even
952
+ * if the {@link DisTubeVoice#audioResource} has silence padding frames.
990
953
  */
991
954
  stop(force = false) {
992
955
  this.audioPlayer.stop(force);
956
+ this.stream?.kill?.();
993
957
  }
994
958
  /**
995
- * Play a readable stream
996
- * @private
997
- * @param {DisTubeStream} stream Readable stream
959
+ * Play a {@link DisTubeStream}
960
+ *
961
+ * @param dtStream - DisTubeStream
998
962
  */
999
- play(stream) {
963
+ play(dtStream) {
1000
964
  this.emittedError = false;
1001
- stream.stream.on("error", (error) => {
965
+ dtStream.stream.on("error", (error) => {
1002
966
  if (this.emittedError || error.code === "ERR_STREAM_PREMATURE_CLOSE")
1003
967
  return;
1004
968
  this.emittedError = true;
1005
969
  this.emit("error", error);
1006
970
  });
1007
- this.audioResource = (0, import_voice.createAudioResource)(stream.stream, {
1008
- inputType: stream.type,
971
+ this.audioResource = (0, import_voice.createAudioResource)(dtStream.stream, {
972
+ inputType: dtStream.type,
1009
973
  inlineVolume: true
1010
974
  });
1011
975
  this.volume = __privateGet(this, _volume);
1012
976
  if (this.audioPlayer.state.status !== import_voice.AudioPlayerStatus.Paused)
1013
977
  this.audioPlayer.play(this.audioResource);
978
+ this.stream?.kill?.();
979
+ this.stream = dtStream;
1014
980
  }
1015
981
  set volume(volume) {
1016
982
  if (typeof volume !== "number" || isNaN(volume)) {
@@ -1027,7 +993,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1027
993
  }
1028
994
  /**
1029
995
  * Playback duration of the audio resource in seconds
1030
- * @type {number}
1031
996
  */
1032
997
  get playbackDuration() {
1033
998
  return (this.audioResource?.playbackDuration ?? 0) / 1e3;
@@ -1046,22 +1011,22 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1046
1011
  }
1047
1012
  /**
1048
1013
  * Whether the bot is self-deafened
1049
- * @type {boolean}
1050
1014
  */
1051
1015
  get selfDeaf() {
1052
1016
  return this.connection.joinConfig.selfDeaf;
1053
1017
  }
1054
1018
  /**
1055
1019
  * Whether the bot is self-muted
1056
- * @type {boolean}
1057
1020
  */
1058
1021
  get selfMute() {
1059
1022
  return this.connection.joinConfig.selfMute;
1060
1023
  }
1061
1024
  /**
1062
1025
  * Self-deafens/undeafens the bot.
1063
- * @param {boolean} selfDeaf Whether or not the bot should be self-deafened
1064
- * @returns {boolean} true if the voice state was successfully updated, otherwise false
1026
+ *
1027
+ * @param selfDeaf - Whether or not the bot should be self-deafened
1028
+ *
1029
+ * @returns true if the voice state was successfully updated, otherwise false
1065
1030
  */
1066
1031
  setSelfDeaf(selfDeaf) {
1067
1032
  if (typeof selfDeaf !== "boolean") {
@@ -1074,8 +1039,10 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1074
1039
  }
1075
1040
  /**
1076
1041
  * Self-mutes/unmutes the bot.
1077
- * @param {boolean} selfMute Whether or not the bot should be self-muted
1078
- * @returns {boolean} true if the voice state was successfully updated, otherwise false
1042
+ *
1043
+ * @param selfMute - Whether or not the bot should be self-muted
1044
+ *
1045
+ * @returns true if the voice state was successfully updated, otherwise false
1079
1046
  */
1080
1047
  setSelfMute(selfMute) {
1081
1048
  if (typeof selfMute !== "boolean") {
@@ -1088,7 +1055,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1088
1055
  }
1089
1056
  /**
1090
1057
  * The voice state of this connection
1091
- * @type {Discord.VoiceState?}
1092
1058
  */
1093
1059
  get voiceState() {
1094
1060
  return this.channel?.guild?.members?.me?.voice;
@@ -1114,90 +1080,119 @@ __name(_DisTubeVoice, "DisTubeVoice");
1114
1080
  var DisTubeVoice = _DisTubeVoice;
1115
1081
 
1116
1082
  // src/core/DisTubeStream.ts
1117
- var import_prism_media = require("prism-media");
1083
+ var import_child_process = require("child_process");
1084
+ var import_node_stream = require("stream");
1085
+ var import_tiny_typed_emitter2 = require("tiny-typed-emitter");
1118
1086
  var import_voice2 = require("@discordjs/voice");
1119
- var chooseBestVideoFormat = /* @__PURE__ */ __name((formats, isLive = false) => {
1120
- let filter = /* @__PURE__ */ __name((format) => format.hasAudio, "filter");
1121
- if (isLive)
1122
- filter = /* @__PURE__ */ __name((format) => format.hasAudio && format.isHLS, "filter");
1123
- formats = formats.filter(filter).sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate) || Number(a.bitrate) - Number(b.bitrate));
1124
- return formats.find((format) => !format.hasVideo) || formats.sort((a, b) => Number(a.bitrate) - Number(b.bitrate))[0];
1125
- }, "chooseBestVideoFormat");
1126
- var _DisTubeStream = class _DisTubeStream {
1087
+ var chooseBestVideoFormat = /* @__PURE__ */ __name(({ duration, formats, isLive }) => formats && formats.filter((f) => f.hasAudio && (duration < 10 * 60 || f.hasVideo) && (!isLive || f.isHLS)).sort((a, b) => Number(b.audioBitrate) - Number(a.audioBitrate) || Number(a.bitrate) - Number(b.bitrate))[0], "chooseBestVideoFormat");
1088
+ var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.TypedEmitter {
1127
1089
  /**
1128
1090
  * Create a DisTubeStream to play with {@link DisTubeVoice}
1129
- * @param {string} url Stream URL
1130
- * @param {StreamOptions} options Stream options
1131
- * @private
1091
+ *
1092
+ * @param url - Stream URL
1093
+ * @param options - Stream options
1132
1094
  */
1133
- constructor(url, options) {
1134
- __publicField(this, "type");
1095
+ constructor(url, { ffmpeg, seek, type }) {
1096
+ super();
1097
+ __publicField(this, "killed", false);
1098
+ __publicField(this, "process");
1135
1099
  __publicField(this, "stream");
1100
+ __publicField(this, "type");
1136
1101
  __publicField(this, "url");
1137
1102
  this.url = url;
1138
- this.type = !options.type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
1139
- const args = [
1140
- "-reconnect",
1141
- "1",
1142
- "-reconnect_streamed",
1143
- "1",
1144
- "-reconnect_delay_max",
1145
- "5",
1146
- "-i",
1147
- url,
1148
- "-analyzeduration",
1149
- "0",
1150
- "-loglevel",
1151
- "0",
1152
- "-ar",
1153
- "48000",
1154
- "-ac",
1155
- "2",
1156
- "-f"
1157
- ];
1158
- if (!options.type)
1159
- args.push("opus", "-acodec", "libopus");
1160
- else
1161
- args.push("s16le");
1162
- if (typeof options.seek === "number" && options.seek > 0)
1163
- args.unshift("-ss", options.seek.toString());
1164
- if (Array.isArray(options.ffmpegArgs) && options.ffmpegArgs.length)
1165
- args.push(...options.ffmpegArgs);
1166
- this.stream = new import_prism_media.FFmpeg({ args, shell: false });
1167
- this.stream._readableState && (this.stream._readableState.highWaterMark = 1 << 25);
1168
- }
1169
- /**
1170
- * Create a stream from ytdl video formats
1171
- * @param {ytdl.videoFormat[]} formats ytdl video formats
1172
- * @param {StreamOptions} options options
1173
- * @returns {DisTubeStream}
1174
- * @private
1175
- */
1176
- static YouTube(formats, options = {}) {
1177
- if (!formats || !formats.length)
1103
+ this.type = !type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
1104
+ const opts = {
1105
+ reconnect: 1,
1106
+ reconnect_on_network_error: 1,
1107
+ reconnect_streamed: 1,
1108
+ reconnect_delay_max: 5,
1109
+ i: url,
1110
+ ar: 48e3,
1111
+ ac: 2,
1112
+ ...ffmpeg.args
1113
+ };
1114
+ if (!type) {
1115
+ opts.f = "opus";
1116
+ opts.acodec = "libopus";
1117
+ } else {
1118
+ opts.f = "s16le";
1119
+ }
1120
+ if (typeof seek === "number" && seek > 0)
1121
+ opts.ss = seek.toString();
1122
+ if (typeof ffmpeg.args === "object")
1123
+ Object.assign(opts, ffmpeg.args);
1124
+ this.process = (0, import_child_process.spawn)(
1125
+ ffmpeg.path,
1126
+ [
1127
+ ...Object.entries(opts).flatMap(
1128
+ ([key, value]) => Array.isArray(value) ? value.filter(Boolean).map((v) => [`-${key}`, String(v)]) : value == null || value === false ? [] : [value === true ? `-${key}` : [`-${key}`, String(value)]]
1129
+ ).flat(),
1130
+ "pipe:1"
1131
+ ],
1132
+ { stdio: ["ignore", "pipe", "pipe"] }
1133
+ ).on("error", (err) => this.debug(`[process] error: ${err.message}`)).on("exit", (code, signal) => {
1134
+ this.debug(`[process] exit: code=${code ?? "unknown"} signal=${signal ?? "unknown"}`);
1135
+ if (!code || [0, 255].includes(code))
1136
+ return;
1137
+ this.debug(`[process] error: ffmpeg exited with code ${code}`);
1138
+ });
1139
+ if (!this.process.stdout || !this.process.stderr) {
1140
+ this.kill();
1141
+ throw new Error("Failed to create ffmpeg process");
1142
+ }
1143
+ this.stream = new import_node_stream.PassThrough();
1144
+ this.stream.on("close", () => this.kill()).on("error", (err) => this.debug(`[stream] error: ${err.message}`)).on("finish", () => this.debug("[stream] log: stream finished"));
1145
+ this.process.stdout.pipe(this.stream);
1146
+ this.process.stderr.setEncoding("utf8")?.on("data", (data) => {
1147
+ const lines = data.split(/\r\n|\r|\n/u);
1148
+ for (const line of lines) {
1149
+ if (/^\s*$/.test(line))
1150
+ continue;
1151
+ this.debug(`[ffmpeg] log: ${line}`);
1152
+ }
1153
+ });
1154
+ }
1155
+ debug(debug) {
1156
+ this.emit("debug", debug);
1157
+ }
1158
+ kill() {
1159
+ if (this.killed)
1160
+ return;
1161
+ this.process.kill("SIGKILL");
1162
+ this.killed = true;
1163
+ }
1164
+ /**
1165
+ * Create a stream from a YouTube {@link Song}
1166
+ *
1167
+ * @param song - A YouTube Song
1168
+ * @param options - options
1169
+ */
1170
+ static YouTube(song, options) {
1171
+ if (song.source !== "youtube")
1172
+ throw new DisTubeError("INVALID_TYPE", "youtube", song.source, "Song#source");
1173
+ if (!song.formats?.length)
1178
1174
  throw new DisTubeError("UNAVAILABLE_VIDEO");
1179
1175
  if (!options || typeof options !== "object" || Array.isArray(options)) {
1180
1176
  throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1181
1177
  }
1182
- const bestFormat = chooseBestVideoFormat(formats, options.isLive);
1178
+ const bestFormat = chooseBestVideoFormat(song);
1183
1179
  if (!bestFormat)
1184
1180
  throw new DisTubeError("UNPLAYABLE_FORMATS");
1185
1181
  return new _DisTubeStream(bestFormat.url, options);
1186
1182
  }
1187
1183
  /**
1188
1184
  * Create a stream from a stream url
1189
- * @param {string} url stream url
1190
- * @param {StreamOptions} options options
1191
- * @returns {DisTubeStream}
1192
- * @private
1185
+ *
1186
+ * @param url - stream url
1187
+ * @param options - options
1193
1188
  */
1194
- static DirectLink(url, options = {}) {
1195
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1196
- throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1197
- }
1189
+ static DirectLink(url, options) {
1198
1190
  if (typeof url !== "string" || !isURL(url)) {
1199
1191
  throw new DisTubeError("INVALID_TYPE", "an URL", url);
1200
1192
  }
1193
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
1194
+ throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1195
+ }
1201
1196
  return new _DisTubeStream(url, options);
1202
1197
  }
1203
1198
  };
@@ -1237,7 +1232,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1237
1232
  delete queue._emptyTimeout;
1238
1233
  if (isVoiceChannelEmpty(oldState)) {
1239
1234
  queue.voice.leave();
1240
- this.emit("empty", queue);
1235
+ this.emit("empty" /* EMPTY */, queue);
1241
1236
  if (queue.stopped)
1242
1237
  queue.remove();
1243
1238
  }
@@ -1271,9 +1266,8 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1271
1266
  return jar.getCookieStringSync("https://www.youtube.com");
1272
1267
  }
1273
1268
  /**
1274
- * @param {string} url url
1275
- * @param {boolean} [basic=false] getBasicInfo?
1276
- * @returns {Promise<ytdl.videoInfo>}
1269
+ * @param url - url
1270
+ * @param basic - getBasicInfo?
1277
1271
  */
1278
1272
  getYouTubeInfo(url, basic = false) {
1279
1273
  if (basic)
@@ -1282,10 +1276,13 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1282
1276
  }
1283
1277
  /**
1284
1278
  * Resolve a url or a supported object to a {@link Song} or {@link Playlist}
1285
- * @param {string|Song|SearchResult|Playlist} song URL | {@link Song}| {@link SearchResult} | {@link Playlist}
1286
- * @param {ResolveOptions} [options] Optional options
1287
- * @returns {Promise<Song|Playlist|null>} Resolved
1288
- * @throws {DisTubeError}
1279
+ *
1280
+ * @throws {@link DisTubeError}
1281
+ *
1282
+ * @param song - URL | {@link Song}| {@link SearchResult} | {@link Playlist}
1283
+ * @param options - Optional options
1284
+ *
1285
+ * @returns Resolved
1289
1286
  */
1290
1287
  async resolve(song, options = {}) {
1291
1288
  if (song instanceof Song || song instanceof Playlist) {
@@ -1319,9 +1316,9 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1319
1316
  }
1320
1317
  /**
1321
1318
  * Resolve Song[] or YouTube playlist url to a Playlist
1322
- * @param {Playlist|Song[]|string} playlist Resolvable playlist
1323
- * @param {ResolvePlaylistOptions} options Optional options
1324
- * @returns {Promise<Playlist>}
1319
+ *
1320
+ * @param playlist - Resolvable playlist
1321
+ * @param options - Optional options
1325
1322
  */
1326
1323
  async resolvePlaylist(playlist, options = {}) {
1327
1324
  const { member, source, metadata } = { source: "youtube", ...options };
@@ -1350,11 +1347,14 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1350
1347
  return new Playlist(playlist, { member, properties: { source }, metadata });
1351
1348
  }
1352
1349
  /**
1353
- * Search for a song, fire {@link DisTube#event:error} if not found.
1354
- * @param {Discord.Message} message The original message from an user
1355
- * @param {string} query The query string
1356
- * @returns {Promise<SearchResult?>} Song info
1357
- * @throws {DisTubeError}
1350
+ * Search for a song, fire {@link DisTube#error} if not found.
1351
+ *
1352
+ * @throws {@link DisTubeError}
1353
+ *
1354
+ * @param message - The original message from an user
1355
+ * @param query - The query string
1356
+ *
1357
+ * @returns Song info
1358
1358
  */
1359
1359
  async searchSong(message, query) {
1360
1360
  if (!isMessageInstance(message))
@@ -1368,7 +1368,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1368
1368
  limit,
1369
1369
  safeSearch: this.options.nsfw ? false : !isNsfwChannel(message.channel)
1370
1370
  }).catch(() => {
1371
- if (!this.emit("searchNoResult", message, query)) {
1371
+ if (!this.emit("searchNoResult" /* SEARCH_NO_RESULT */, message, query)) {
1372
1372
  console.warn("searchNoResult event does not have any listeners! Emits `error` event instead.");
1373
1373
  throw new DisTubeError("NO_RESULT");
1374
1374
  }
@@ -1380,13 +1380,16 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1380
1380
  /**
1381
1381
  * Create a message collector for selecting search results.
1382
1382
  *
1383
- * Needed events: {@link DisTube#event:searchResult}, {@link DisTube#event:searchCancel},
1384
- * {@link DisTube#event:searchInvalidAnswer}, {@link DisTube#event:searchDone}.
1385
- * @param {Discord.Message} message The original message from an user
1386
- * @param {Array<SearchResult|Song|Playlist>} results The search results
1387
- * @param {string?} [query] The query string
1388
- * @returns {Promise<SearchResult|Song|Playlist|null>} Selected result
1389
- * @throws {DisTubeError}
1383
+ * Needed events: {@link DisTube#searchResult}, {@link DisTube#searchCancel},
1384
+ * {@link DisTube#searchInvalidAnswer}, {@link DisTube#searchDone}.
1385
+ *
1386
+ * @throws {@link DisTubeError}
1387
+ *
1388
+ * @param message - The original message from an user
1389
+ * @param results - The search results
1390
+ * @param query - The query string
1391
+ *
1392
+ * @returns Selected result
1390
1393
  */
1391
1394
  async createSearchMessageCollector(message, results, query) {
1392
1395
  if (!isMessageInstance(message))
@@ -1396,11 +1399,11 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1396
1399
  }
1397
1400
  if (this.options.searchSongs > 1) {
1398
1401
  const searchEvents = [
1399
- "searchNoResult",
1400
- "searchResult",
1401
- "searchCancel",
1402
- "searchInvalidAnswer",
1403
- "searchDone"
1402
+ "searchNoResult" /* SEARCH_NO_RESULT */,
1403
+ "searchResult" /* SEARCH_RESULT */,
1404
+ "searchCancel" /* SEARCH_CANCEL */,
1405
+ "searchInvalidAnswer" /* SEARCH_INVALID_ANSWER */,
1406
+ "searchDone" /* SEARCH_DONE */
1404
1407
  ];
1405
1408
  for (const evn of searchEvents) {
1406
1409
  if (this.distube.listenerCount(evn) === 0) {
@@ -1417,7 +1420,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1417
1420
  let result = results[0];
1418
1421
  if (limit > 1) {
1419
1422
  results.splice(limit);
1420
- this.emit("searchResult", message, results, query);
1423
+ this.emit("searchResult" /* SEARCH_RESULT */, message, results, query);
1421
1424
  const answers = await message.channel.awaitMessages({
1422
1425
  filter: (m) => m.author.id === message.author.id,
1423
1426
  max: 1,
@@ -1426,26 +1429,27 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1426
1429
  }).catch(() => void 0);
1427
1430
  const ans = answers?.first();
1428
1431
  if (!ans) {
1429
- this.emit("searchCancel", message, query);
1432
+ this.emit("searchCancel" /* SEARCH_CANCEL */, message, query);
1430
1433
  return null;
1431
1434
  }
1432
1435
  const index = parseInt(ans.content, 10);
1433
1436
  if (isNaN(index) || index > results.length || index < 1) {
1434
- this.emit("searchInvalidAnswer", message, ans, query);
1437
+ this.emit("searchInvalidAnswer" /* SEARCH_INVALID_ANSWER */, message, ans, query);
1435
1438
  return null;
1436
1439
  }
1437
- this.emit("searchDone", message, ans, query);
1440
+ this.emit("searchDone" /* SEARCH_DONE */, message, ans, query);
1438
1441
  result = results[index - 1];
1439
1442
  }
1440
1443
  return result;
1441
1444
  }
1442
1445
  /**
1443
1446
  * Play or add a {@link Playlist} to the queue.
1444
- * @param {Discord.BaseGuildVoiceChannel} voiceChannel A voice channel
1445
- * @param {Playlist|string} playlist A YouTube playlist url | a Playlist
1446
- * @param {PlayHandlerOptions} [options] Optional options
1447
- * @returns {Promise<void>}
1448
- * @throws {DisTubeError}
1447
+ *
1448
+ * @throws {@link DisTubeError}
1449
+ *
1450
+ * @param voiceChannel - A voice channel
1451
+ * @param playlist - A YouTube playlist url | a Playlist
1452
+ * @param options - Optional options
1449
1453
  */
1450
1454
  async playPlaylist(voiceChannel, playlist, options = {}) {
1451
1455
  const { textChannel, skip } = { skip: false, ...options };
@@ -1468,23 +1472,24 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1468
1472
  if (skip)
1469
1473
  queue.skip();
1470
1474
  else
1471
- this.emit("addList", queue, playlist);
1475
+ this.emit("addList" /* ADD_LIST */, queue, playlist);
1472
1476
  } else {
1473
1477
  const newQueue = await this.queues.create(voiceChannel, playlist.songs, textChannel);
1474
1478
  if (newQueue instanceof Queue) {
1475
1479
  if (this.options.emitAddListWhenCreatingQueue)
1476
- this.emit("addList", newQueue, playlist);
1477
- this.emit("playSong", newQueue, newQueue.songs[0]);
1480
+ this.emit("addList" /* ADD_LIST */, newQueue, playlist);
1481
+ this.emit("playSong" /* PLAY_SONG */, newQueue, newQueue.songs[0]);
1478
1482
  }
1479
1483
  }
1480
1484
  }
1481
1485
  /**
1482
1486
  * Play or add a {@link Song} to the queue.
1483
- * @param {Discord.BaseGuildVoiceChannel} voiceChannel A voice channel
1484
- * @param {Song} song A YouTube playlist url | a Playlist
1485
- * @param {PlayHandlerOptions} [options] Optional options
1486
- * @returns {Promise<void>}
1487
- * @throws {DisTubeError}
1487
+ *
1488
+ * @throws {@link DisTubeError}
1489
+ *
1490
+ * @param voiceChannel - A voice channel
1491
+ * @param song - A YouTube playlist url | a Playlist
1492
+ * @param options - Optional options
1488
1493
  */
1489
1494
  async playSong(voiceChannel, song, options = {}) {
1490
1495
  if (!(song instanceof Song))
@@ -1502,27 +1507,26 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1502
1507
  if (skip)
1503
1508
  queue.skip();
1504
1509
  else
1505
- this.emit("addSong", queue, song);
1510
+ this.emit("addSong" /* ADD_SONG */, queue, song);
1506
1511
  } else {
1507
1512
  const newQueue = await this.queues.create(voiceChannel, song, textChannel);
1508
1513
  if (newQueue instanceof Queue) {
1509
1514
  if (this.options.emitAddSongWhenCreatingQueue)
1510
- this.emit("addSong", newQueue, song);
1511
- this.emit("playSong", newQueue, song);
1515
+ this.emit("addSong" /* ADD_SONG */, newQueue, song);
1516
+ this.emit("playSong" /* PLAY_SONG */, newQueue, song);
1512
1517
  }
1513
1518
  }
1514
1519
  }
1515
1520
  /**
1516
1521
  * Get {@link Song}'s stream info and attach it to the song.
1517
- * @param {Song} song A Song
1522
+ *
1523
+ * @param song - A Song
1518
1524
  */
1519
1525
  async attachStreamInfo(song) {
1520
- const { url, source, formats, streamURL, isLive } = song;
1526
+ const { url, source } = song;
1521
1527
  if (source === "youtube") {
1522
- if (!formats || !chooseBestVideoFormat(formats, isLive)) {
1523
- song._patchYouTube(await this.handler.getYouTubeInfo(url));
1524
- }
1525
- } else if (!streamURL) {
1528
+ song._patchYouTube(await this.handler.getYouTubeInfo(url));
1529
+ } else {
1526
1530
  for (const plugin of [...this.distube.extractorPlugins, ...this.distube.customPlugins]) {
1527
1531
  if (await plugin.validate(url)) {
1528
1532
  const info = [plugin.getStreamURL(url), plugin.getRelatedSongs(url)];
@@ -1562,6 +1566,8 @@ var _Options = class _Options {
1562
1566
  __publicField(this, "joinNewVoiceChannel");
1563
1567
  __publicField(this, "streamType");
1564
1568
  __publicField(this, "directLink");
1569
+ __publicField(this, "ffmpegPath");
1570
+ __publicField(this, "ffmpegDefaultArgs");
1565
1571
  if (typeof options !== "object" || Array.isArray(options)) {
1566
1572
  throw new DisTubeError("INVALID_TYPE", "object", options, "DisTubeOptions");
1567
1573
  }
@@ -1584,6 +1590,8 @@ var _Options = class _Options {
1584
1590
  this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
1585
1591
  this.streamType = opts.streamType;
1586
1592
  this.directLink = opts.directLink;
1593
+ this.ffmpegPath = opts.ffmpegPath;
1594
+ this.ffmpegDefaultArgs = opts.ffmpegDefaultArgs;
1587
1595
  checkInvalidKey(opts, this, "DisTubeOptions");
1588
1596
  __privateMethod(this, _validateOptions, validateOptions_fn).call(this);
1589
1597
  }
@@ -1603,8 +1611,8 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1603
1611
  "directLink"
1604
1612
  ]);
1605
1613
  const numberOptions = /* @__PURE__ */ new Set(["searchCooldown", "emptyCooldown", "searchSongs"]);
1606
- const stringOptions = /* @__PURE__ */ new Set();
1607
- const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions"]);
1614
+ const stringOptions = /* @__PURE__ */ new Set(["ffmpegPath"]);
1615
+ const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpegDefaultArgs"]);
1608
1616
  const optionalOptions = /* @__PURE__ */ new Set(["youtubeCookie", "customFilters"]);
1609
1617
  for (const [key, value] of Object.entries(options)) {
1610
1618
  if (value === void 0 && optionalOptions.has(key))
@@ -1644,14 +1652,11 @@ var _BaseManager = class _BaseManager extends DisTubeBase {
1644
1652
  super(...arguments);
1645
1653
  /**
1646
1654
  * The collection of items for this manager.
1647
- * @type {Collection}
1648
- * @name BaseManager#collection
1649
1655
  */
1650
1656
  __publicField(this, "collection", new import_discord2.Collection());
1651
1657
  }
1652
1658
  /**
1653
1659
  * The size of the collection.
1654
- * @type {number}
1655
1660
  */
1656
1661
  get size() {
1657
1662
  return this.collection.size;
@@ -1667,7 +1672,8 @@ var _GuildIdManager = class _GuildIdManager extends BaseManager {
1667
1672
  const existing = this.get(id);
1668
1673
  if (existing)
1669
1674
  return this;
1670
- return this.collection.set(id, data);
1675
+ this.collection.set(id, data);
1676
+ return this;
1671
1677
  }
1672
1678
  get(idOrInstance) {
1673
1679
  return this.collection.get(resolveGuildId(idOrInstance));
@@ -1687,21 +1693,16 @@ var import_voice3 = require("@discordjs/voice");
1687
1693
  var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
1688
1694
  /**
1689
1695
  * Get a {@link DisTubeVoice}.
1690
- * @method get
1691
- * @memberof DisTubeVoiceManager#
1692
- * @param {GuildIdResolvable} guild The queue resolvable to resolve
1693
- * @returns {DisTubeVoice?}
1696
+ *
1697
+ * @param guild - The queue resolvable to resolve
1694
1698
  */
1695
1699
  /**
1696
1700
  * Collection of {@link DisTubeVoice}.
1697
- * @name DisTubeVoiceManager#collection
1698
- * @type {Discord.Collection<string, DisTubeVoice>}
1699
1701
  */
1700
1702
  /**
1701
1703
  * Create a {@link DisTubeVoice}
1702
- * @param {Discord.BaseGuildVoiceChannel} channel A voice channel to join
1703
- * @returns {DisTubeVoice}
1704
- * @private
1704
+ *
1705
+ * @param channel - A voice channel to join
1705
1706
  */
1706
1707
  create(channel) {
1707
1708
  const existing = this.get(channel.guildId);
@@ -1713,8 +1714,8 @@ var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
1713
1714
  }
1714
1715
  /**
1715
1716
  * Join a voice channel
1716
- * @param {Discord.BaseGuildVoiceChannel} channel A voice channel to join
1717
- * @returns {Promise<DisTubeVoice>}
1717
+ *
1718
+ * @param channel - A voice channel to join
1718
1719
  */
1719
1720
  join(channel) {
1720
1721
  const existing = this.get(channel.guildId);
@@ -1724,7 +1725,8 @@ var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
1724
1725
  }
1725
1726
  /**
1726
1727
  * Leave the connected voice channel in a guild
1727
- * @param {GuildIdResolvable} guild Queue Resolvable
1728
+ *
1729
+ * @param guild - Queue Resolvable
1728
1730
  */
1729
1731
  leave(guild) {
1730
1732
  const voice = this.get(guild);
@@ -1751,17 +1753,15 @@ var _FilterManager = class _FilterManager extends BaseManager {
1751
1753
  __privateAdd(this, _removeFn);
1752
1754
  /**
1753
1755
  * Collection of {@link Filter}.
1754
- * @name FilterManager#collection
1755
- * @type {Discord.Collection<string, DisTubeVoice>}
1756
1756
  */
1757
1757
  __publicField(this, "queue");
1758
1758
  this.queue = queue;
1759
1759
  }
1760
1760
  /**
1761
1761
  * Enable a filter or multiple filters to the manager
1762
- * @param {FilterResolvable|FilterResolvable[]} filterOrFilters The filter or filters to enable
1763
- * @param {boolean} [override=false] Wether or not override the applied filter with new filter value
1764
- * @returns {FilterManager}
1762
+ *
1763
+ * @param filterOrFilters - The filter or filters to enable
1764
+ * @param override - Wether or not override the applied filter with new filter value
1765
1765
  */
1766
1766
  add(filterOrFilters, override = false) {
1767
1767
  if (Array.isArray(filterOrFilters)) {
@@ -1780,15 +1780,14 @@ var _FilterManager = class _FilterManager extends BaseManager {
1780
1780
  }
1781
1781
  /**
1782
1782
  * Clear enabled filters of the manager
1783
- * @returns {FilterManager}
1784
1783
  */
1785
1784
  clear() {
1786
1785
  return this.set([]);
1787
1786
  }
1788
1787
  /**
1789
1788
  * Set the filters applied to the manager
1790
- * @param {FilterResolvable[]} filters The filters to apply
1791
- * @returns {FilterManager}
1789
+ *
1790
+ * @param filters - The filters to apply
1792
1791
  */
1793
1792
  set(filters) {
1794
1793
  if (!Array.isArray(filters))
@@ -1803,8 +1802,8 @@ var _FilterManager = class _FilterManager extends BaseManager {
1803
1802
  }
1804
1803
  /**
1805
1804
  * Disable a filter or multiple filters
1806
- * @param {FilterResolvable|FilterResolvable[]} filterOrFilters The filter or filters to disable
1807
- * @returns {FilterManager}
1805
+ *
1806
+ * @param filterOrFilters - The filter or filters to disable
1808
1807
  */
1809
1808
  remove(filterOrFilters) {
1810
1809
  if (Array.isArray(filterOrFilters))
@@ -1816,30 +1815,26 @@ var _FilterManager = class _FilterManager extends BaseManager {
1816
1815
  }
1817
1816
  /**
1818
1817
  * Check whether a filter enabled or not
1819
- * @param {FilterResolvable} filter The filter to check
1820
- * @returns {boolean}
1818
+ *
1819
+ * @param filter - The filter to check
1821
1820
  */
1822
1821
  has(filter) {
1823
1822
  return this.collection.has(typeof filter === "string" ? filter : __privateMethod(this, _resolve, resolve_fn).call(this, filter).name);
1824
1823
  }
1825
1824
  /**
1826
1825
  * Array of enabled filter names
1827
- * @type {Array<string>}
1828
- * @readonly
1829
1826
  */
1830
1827
  get names() {
1831
1828
  return [...this.collection.keys()];
1832
1829
  }
1833
1830
  /**
1834
1831
  * Array of enabled filters
1835
- * @type {Array<Filter>}
1836
- * @readonly
1837
1832
  */
1838
1833
  get values() {
1839
1834
  return [...this.collection.values()];
1840
1835
  }
1841
1836
  get ffmpegArgs() {
1842
- return this.size ? ["-af", this.values.map((f) => f.value).join(",")] : [];
1837
+ return this.size ? { af: this.values.map((f) => f.value).join(",") } : {};
1843
1838
  }
1844
1839
  toString() {
1845
1840
  return this.names.toString();
@@ -1877,51 +1872,46 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1877
1872
  super(...arguments);
1878
1873
  /**
1879
1874
  * Get a Queue from this QueueManager.
1880
- * @method get
1881
- * @memberof QueueManager#
1882
- * @param {GuildIdResolvable} guild Resolvable thing from a guild
1883
- * @returns {Queue?}
1875
+ *
1876
+ * @param guild - Resolvable thing from a guild
1884
1877
  */
1885
1878
  /**
1886
1879
  * Listen to DisTubeVoice events and handle the Queue
1887
- * @private
1888
- * @param {Queue} queue Queue
1880
+ *
1881
+ * @param queue - Queue
1889
1882
  */
1890
1883
  __privateAdd(this, _voiceEventHandler);
1891
1884
  /**
1892
1885
  * Whether or not emit playSong event
1893
- * @param {Queue} queue Queue
1894
- * @private
1895
- * @returns {boolean}
1886
+ *
1887
+ * @param queue - Queue
1896
1888
  */
1897
1889
  __privateAdd(this, _emitPlaySong);
1898
1890
  /**
1899
1891
  * Handle the queue when a Song finish
1900
- * @private
1901
- * @param {Queue} queue queue
1902
- * @returns {Promise<void>}
1892
+ *
1893
+ * @param queue - queue
1903
1894
  */
1904
1895
  __privateAdd(this, _handleSongFinish);
1905
1896
  /**
1906
1897
  * Handle error while playing
1907
- * @private
1908
- * @param {Queue} queue queue
1909
- * @param {Error} error error
1898
+ *
1899
+ * @param queue - queue
1900
+ * @param error - error
1910
1901
  */
1911
1902
  __privateAdd(this, _handlePlayingError);
1912
1903
  }
1913
1904
  /**
1914
1905
  * Collection of {@link Queue}.
1915
- * @name QueueManager#collection
1916
- * @type {Discord.Collection<string, Queue>}
1917
1906
  */
1918
1907
  /**
1919
1908
  * Create a {@link Queue}
1920
- * @private
1921
- * @param {Discord.BaseGuildVoiceChannel} channel A voice channel
1922
- * @param {Song|Song[]} song First song
1923
- * @param {Discord.BaseGuildTextChannel} textChannel Default text channel
1924
- * @returns {Promise<Queue|true>} Returns `true` if encounter an error
1909
+ *
1910
+ * @param channel - A voice channel
1911
+ * @param song - First song
1912
+ * @param textChannel - Default text channel
1913
+ *
1914
+ * @returns Returns `true` if encounter an error
1925
1915
  */
1926
1916
  async create(channel, song, textChannel) {
1927
1917
  if (this.has(channel.guildId))
@@ -1933,7 +1923,7 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1933
1923
  await voice.join();
1934
1924
  __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1935
1925
  this.add(queue.id, queue);
1936
- this.emit("initQueue", queue);
1926
+ this.emit("initQueue" /* INIT_QUEUE */, queue);
1937
1927
  const err = await this.playSong(queue);
1938
1928
  return err || queue;
1939
1929
  } finally {
@@ -1942,26 +1932,33 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1942
1932
  }
1943
1933
  /**
1944
1934
  * Create a ytdl stream
1945
- * @param {Queue} queue Queue
1946
- * @returns {DisTubeStream}
1935
+ *
1936
+ * @param queue - Queue
1947
1937
  */
1948
1938
  createStream(queue) {
1949
- const { duration, formats, isLive, source, streamURL } = queue.songs[0];
1939
+ const song = queue.songs[0];
1940
+ const { duration, source, streamURL } = song;
1950
1941
  const streamOptions = {
1951
- ffmpegArgs: queue.filters.ffmpegArgs,
1942
+ ffmpeg: {
1943
+ path: this.options.ffmpegPath,
1944
+ args: {
1945
+ ...this.options.ffmpegDefaultArgs,
1946
+ ...queue.filters.ffmpegArgs
1947
+ }
1948
+ },
1952
1949
  seek: duration ? queue.beginTime : void 0,
1953
- isLive,
1954
1950
  type: this.options.streamType
1955
1951
  };
1956
1952
  if (source === "youtube")
1957
- return DisTubeStream.YouTube(formats, streamOptions);
1953
+ return DisTubeStream.YouTube(song, streamOptions);
1958
1954
  return DisTubeStream.DirectLink(streamURL, streamOptions);
1959
1955
  }
1960
1956
  /**
1961
1957
  * Play a song on voice connection
1962
- * @private
1963
- * @param {Queue} queue The guild queue
1964
- * @returns {Promise<boolean>} error?
1958
+ *
1959
+ * @param queue - The guild queue
1960
+ *
1961
+ * @returns error?
1965
1962
  */
1966
1963
  async playSong(queue) {
1967
1964
  if (!queue)
@@ -1978,6 +1975,7 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1978
1975
  return true;
1979
1976
  }
1980
1977
  const stream = this.createStream(queue);
1978
+ stream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}]: ${data}`));
1981
1979
  queue.voice.play(stream);
1982
1980
  song.streamURL = stream.url;
1983
1981
  return false;
@@ -1992,7 +1990,7 @@ voiceEventHandler_fn = /* @__PURE__ */ __name(function(queue) {
1992
1990
  queue._listeners = {
1993
1991
  disconnect: (error) => {
1994
1992
  queue.remove();
1995
- this.emit("disconnect", queue);
1993
+ this.emit("disconnect" /* DISCONNECT */, queue);
1996
1994
  if (error)
1997
1995
  this.emitError(error, queue.textChannel);
1998
1996
  },
@@ -2009,7 +2007,7 @@ emitPlaySong_fn = /* @__PURE__ */ __name(function(queue) {
2009
2007
  }, "#emitPlaySong");
2010
2008
  _handleSongFinish = new WeakSet();
2011
2009
  handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
2012
- this.emit("finishSong", queue, queue.songs[0]);
2010
+ this.emit("finishSong" /* FINISH_SONG */, queue, queue.songs[0]);
2013
2011
  await queue._taskQueue.queuing();
2014
2012
  try {
2015
2013
  if (queue.stopped)
@@ -2027,14 +2025,14 @@ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
2027
2025
  try {
2028
2026
  await queue.addRelatedSong();
2029
2027
  } catch {
2030
- this.emit("noRelated", queue);
2028
+ this.emit("noRelated" /* NO_RELATED */, queue);
2031
2029
  }
2032
2030
  }
2033
2031
  if (queue.songs.length <= 1) {
2034
2032
  if (this.options.leaveOnFinish)
2035
2033
  queue.voice.leave();
2036
2034
  if (!queue.autoplay)
2037
- this.emit("finish", queue);
2035
+ this.emit("finish" /* FINISH */, queue);
2038
2036
  queue.remove();
2039
2037
  return;
2040
2038
  }
@@ -2053,7 +2051,7 @@ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
2053
2051
  queue.beginTime = 0;
2054
2052
  const err = await this.playSong(queue);
2055
2053
  if (!err && emitPlaySong)
2056
- this.emit("playSong", queue, queue.songs[0]);
2054
+ this.emit("playSong" /* PLAY_SONG */, queue, queue.songs[0]);
2057
2055
  } finally {
2058
2056
  queue._taskQueue.resolve();
2059
2057
  }
@@ -2074,7 +2072,7 @@ Name: ${song.name}`;
2074
2072
  queue.beginTime = 0;
2075
2073
  this.playSong(queue).then((e) => {
2076
2074
  if (!e)
2077
- this.emit("playSong", queue, queue.songs[0]);
2075
+ this.emit("playSong" /* PLAY_SONG */, queue, queue.songs[0]);
2078
2076
  });
2079
2077
  } else {
2080
2078
  queue.stop();
@@ -2088,10 +2086,11 @@ var _filters;
2088
2086
  var _Queue = class _Queue extends DisTubeBase {
2089
2087
  /**
2090
2088
  * Create a queue for the guild
2091
- * @param {DisTube} distube DisTube
2092
- * @param {DisTubeVoice} voice Voice connection
2093
- * @param {Song|Song[]} song First song(s)
2094
- * @param {Discord.BaseGuildTextChannel?} textChannel Default text channel
2089
+ *
2090
+ * @param distube - DisTube
2091
+ * @param voice - Voice connection
2092
+ * @param song - First song(s)
2093
+ * @param textChannel - Default text channel
2095
2094
  */
2096
2095
  constructor(distube, voice, song, textChannel) {
2097
2096
  super(distube);
@@ -2133,55 +2132,42 @@ var _Queue = class _Queue extends DisTubeBase {
2133
2132
  }
2134
2133
  /**
2135
2134
  * The client user as a `GuildMember` of this queue's guild
2136
- * @type {Discord.GuildMember?}
2137
2135
  */
2138
2136
  get clientMember() {
2139
2137
  return this.voice.channel.guild.members.me ?? void 0;
2140
2138
  }
2141
2139
  /**
2142
2140
  * The filter manager of the queue
2143
- * @type {FilterManager}
2144
- * @readonly
2145
2141
  */
2146
2142
  get filters() {
2147
2143
  return __privateGet(this, _filters);
2148
2144
  }
2149
2145
  /**
2150
2146
  * Formatted duration string.
2151
- * @type {string}
2152
- * @readonly
2153
2147
  */
2154
2148
  get formattedDuration() {
2155
2149
  return formatDuration(this.duration);
2156
2150
  }
2157
2151
  /**
2158
2152
  * Queue's duration.
2159
- * @type {number}
2160
- * @readonly
2161
2153
  */
2162
2154
  get duration() {
2163
2155
  return this.songs.length ? this.songs.reduce((prev, next) => prev + next.duration, 0) : 0;
2164
2156
  }
2165
2157
  /**
2166
2158
  * What time in the song is playing (in seconds).
2167
- * @type {number}
2168
- * @readonly
2169
2159
  */
2170
2160
  get currentTime() {
2171
2161
  return this.voice.playbackDuration + this.beginTime;
2172
2162
  }
2173
2163
  /**
2174
2164
  * Formatted {@link Queue#currentTime} string.
2175
- * @type {string}
2176
- * @readonly
2177
2165
  */
2178
2166
  get formattedCurrentTime() {
2179
2167
  return formatDuration(this.currentTime);
2180
2168
  }
2181
2169
  /**
2182
2170
  * The voice channel playing in.
2183
- * @type {Discord.VoiceChannel|Discord.StageChannel|null}
2184
- * @readonly
2185
2171
  */
2186
2172
  get voiceChannel() {
2187
2173
  return this.clientMember?.voice?.channel ?? null;
@@ -2193,12 +2179,12 @@ var _Queue = class _Queue extends DisTubeBase {
2193
2179
  this.voice.volume = value;
2194
2180
  }
2195
2181
  /**
2196
- * @private
2197
- * Add a Song or an array of Song to the queue
2198
- * @param {Song|Song[]} song Song to add
2199
- * @param {number} [position=0] Position to add, <= 0 to add to the end of the queue
2200
- * @throws {Error}
2201
- * @returns {Queue} The guild queue
2182
+ * @throws {DisTubeError}
2183
+ *
2184
+ * @param song - Song to add
2185
+ * @param position - Position to add, \<= 0 to add to the end of the queue
2186
+ *
2187
+ * @returns The guild queue
2202
2188
  */
2203
2189
  addToQueue(song, position = 0) {
2204
2190
  if (!song || Array.isArray(song) && !song.length) {
@@ -2225,7 +2211,8 @@ var _Queue = class _Queue extends DisTubeBase {
2225
2211
  }
2226
2212
  /**
2227
2213
  * Pause the guild stream
2228
- * @returns {Queue} The guild queue
2214
+ *
2215
+ * @returns The guild queue
2229
2216
  */
2230
2217
  pause() {
2231
2218
  if (this.paused)
@@ -2237,7 +2224,8 @@ var _Queue = class _Queue extends DisTubeBase {
2237
2224
  }
2238
2225
  /**
2239
2226
  * Resume the guild stream
2240
- * @returns {Queue} The guild queue
2227
+ *
2228
+ * @returns The guild queue
2241
2229
  */
2242
2230
  resume() {
2243
2231
  if (this.playing)
@@ -2249,19 +2237,21 @@ var _Queue = class _Queue extends DisTubeBase {
2249
2237
  }
2250
2238
  /**
2251
2239
  * Set the guild stream's volume
2252
- * @param {number} percent The percentage of volume you want to set
2253
- * @returns {Queue} The guild queue
2240
+ *
2241
+ * @param percent - The percentage of volume you want to set
2242
+ *
2243
+ * @returns The guild queue
2254
2244
  */
2255
2245
  setVolume(percent) {
2256
2246
  this.volume = percent;
2257
2247
  return this;
2258
2248
  }
2259
2249
  /**
2260
- * Skip the playing song if there is a next song in the queue.
2261
- * <info>If {@link Queue#autoplay} is `true` and there is no up next song,
2262
- * DisTube will add and play a related song.</info>
2263
- * @returns {Promise<Song>} The song will skip to
2264
- * @throws {Error}
2250
+ * Skip the playing song if there is a next song in the queue. <info>If {@link
2251
+ * Queue#autoplay} is `true` and there is no up next song, DisTube will add and
2252
+ * play a related song.</info>
2253
+ *
2254
+ * @returns The song will skip to
2265
2255
  */
2266
2256
  async skip() {
2267
2257
  await this._taskQueue.queuing();
@@ -2282,8 +2272,8 @@ var _Queue = class _Queue extends DisTubeBase {
2282
2272
  }
2283
2273
  /**
2284
2274
  * Play the previous song if exists
2285
- * @returns {Promise<Song>} The guild queue
2286
- * @throws {Error}
2275
+ *
2276
+ * @returns The guild queue
2287
2277
  */
2288
2278
  async previous() {
2289
2279
  await this._taskQueue.queuing();
@@ -2303,7 +2293,8 @@ var _Queue = class _Queue extends DisTubeBase {
2303
2293
  }
2304
2294
  /**
2305
2295
  * Shuffle the queue's songs
2306
- * @returns {Promise<Queue>} The guild queue
2296
+ *
2297
+ * @returns The guild queue
2307
2298
  */
2308
2299
  async shuffle() {
2309
2300
  await this._taskQueue.queuing();
@@ -2322,12 +2313,13 @@ var _Queue = class _Queue extends DisTubeBase {
2322
2313
  }
2323
2314
  }
2324
2315
  /**
2325
- * Jump to the song position in the queue.
2326
- * The next one is 1, 2,...
2327
- * The previous one is -1, -2,...
2328
- * @param {number} position The song position to play
2329
- * @returns {Promise<Song>} The new Song will be played
2330
- * @throws {Error} if `num` is invalid number
2316
+ * Jump to the song position in the queue. The next one is 1, 2,... The previous
2317
+ * one is -1, -2,...
2318
+ * if `num` is invalid number
2319
+ *
2320
+ * @param position - The song position to play
2321
+ *
2322
+ * @returns The new Song will be played
2331
2323
  */
2332
2324
  async jump(position) {
2333
2325
  await this._taskQueue.queuing();
@@ -2363,10 +2355,12 @@ var _Queue = class _Queue extends DisTubeBase {
2363
2355
  }
2364
2356
  }
2365
2357
  /**
2366
- * Set the repeat mode of the guild queue.\
2358
+ * Set the repeat mode of the guild queue.
2367
2359
  * Toggle mode `(Disabled -> Song -> Queue -> Disabled ->...)` if `mode` is `undefined`
2368
- * @param {RepeatMode?} [mode] The repeat modes (toggle if `undefined`)
2369
- * @returns {RepeatMode} The new repeat mode
2360
+ *
2361
+ * @param mode - The repeat modes (toggle if `undefined`)
2362
+ *
2363
+ * @returns The new repeat mode
2370
2364
  */
2371
2365
  setRepeatMode(mode) {
2372
2366
  if (mode !== void 0 && !Object.values(RepeatMode).includes(mode)) {
@@ -2382,8 +2376,10 @@ var _Queue = class _Queue extends DisTubeBase {
2382
2376
  }
2383
2377
  /**
2384
2378
  * Set the playing time to another position
2385
- * @param {number} time Time in seconds
2386
- * @returns {Queue} The guild queue
2379
+ *
2380
+ * @param time - Time in seconds
2381
+ *
2382
+ * @returns The guild queue
2387
2383
  */
2388
2384
  seek(time) {
2389
2385
  if (typeof time !== "number")
@@ -2396,8 +2392,8 @@ var _Queue = class _Queue extends DisTubeBase {
2396
2392
  }
2397
2393
  /**
2398
2394
  * Add a related song of the playing song to the queue
2399
- * @returns {Promise<Song>} The added song
2400
- * @throws {Error}
2395
+ *
2396
+ * @returns The added song
2401
2397
  */
2402
2398
  async addRelatedSong() {
2403
2399
  if (!this.songs?.[0])
@@ -2430,9 +2426,8 @@ var _Queue = class _Queue extends DisTubeBase {
2430
2426
  }
2431
2427
  }
2432
2428
  /**
2433
- * Remove the queue from the manager
2434
- * (This does not leave the voice channel even if {@link DisTubeOptions|DisTubeOptions.leaveOnStop} is enabled)
2435
- * @private
2429
+ * Remove the queue from the manager (This does not leave the voice channel even if
2430
+ * {@link DisTubeOptions | DisTubeOptions.leaveOnStop} is enabled)
2436
2431
  */
2437
2432
  remove() {
2438
2433
  this.stopped = true;
@@ -2444,11 +2439,12 @@ var _Queue = class _Queue extends DisTubeBase {
2444
2439
  }
2445
2440
  }
2446
2441
  this.queues.remove(this.id);
2447
- this.emit("deleteQueue", this);
2442
+ this.emit("deleteQueue" /* DELETE_QUEUE */, this);
2448
2443
  }
2449
2444
  /**
2450
2445
  * Toggle autoplay mode
2451
- * @returns {boolean} Autoplay mode state
2446
+ *
2447
+ * @returns Autoplay mode state
2452
2448
  */
2453
2449
  toggleAutoplay() {
2454
2450
  this.autoplay = !this.autoplay;
@@ -2469,70 +2465,59 @@ var _Plugin = class _Plugin {
2469
2465
  }
2470
2466
  /**
2471
2467
  * Type of the plugin
2472
- * @name Plugin#type
2473
- * @type {PluginType}
2474
2468
  */
2475
2469
  /**
2476
2470
  * Emit an event to the {@link DisTube} class
2477
- * @param {string} eventName Event name
2478
- * @param {...any} args arguments
2479
- * @returns {boolean}
2471
+ *
2472
+ * @param eventName - Event name
2473
+ * @param args - arguments
2480
2474
  */
2481
2475
  emit(eventName, ...args) {
2482
2476
  return this.distube.emit(eventName, ...args);
2483
2477
  }
2484
2478
  /**
2485
2479
  * Emit error event to the {@link DisTube} class
2486
- * @param {Error} error error
2487
- * @param {Discord.BaseGuildTextChannel} [channel] Text channel where the error is encountered.
2480
+ *
2481
+ * @param error - error
2482
+ * @param channel - Text channel where the error is encountered.
2488
2483
  */
2489
2484
  emitError(error, channel) {
2490
2485
  this.distube.emitError(error, channel);
2491
2486
  }
2492
2487
  /**
2493
2488
  * The queue manager
2494
- * @type {QueueManager}
2495
- * @readonly
2496
2489
  */
2497
2490
  get queues() {
2498
2491
  return this.distube.queues;
2499
2492
  }
2500
2493
  /**
2501
2494
  * The voice manager
2502
- * @type {DisTubeVoiceManager}
2503
- * @readonly
2504
2495
  */
2505
2496
  get voices() {
2506
2497
  return this.distube.voices;
2507
2498
  }
2508
2499
  /**
2509
2500
  * Discord.js client
2510
- * @type {Discord.Client}
2511
- * @readonly
2512
2501
  */
2513
2502
  get client() {
2514
2503
  return this.distube.client;
2515
2504
  }
2516
2505
  /**
2517
2506
  * DisTube options
2518
- * @type {DisTubeOptions}
2519
- * @readonly
2520
2507
  */
2521
2508
  get options() {
2522
2509
  return this.distube.options;
2523
2510
  }
2524
2511
  /**
2525
2512
  * DisTube handler
2526
- * @type {DisTubeHandler}
2527
- * @readonly
2528
2513
  */
2529
2514
  get handler() {
2530
2515
  return this.distube.handler;
2531
2516
  }
2532
2517
  /**
2533
2518
  * Check if the string is working with this plugin
2534
- * @param {string} _string Input string
2535
- * @returns {boolean|Promise<boolean>}
2519
+ *
2520
+ * @param _string - Input string
2536
2521
  */
2537
2522
  validate(_string) {
2538
2523
  return false;
@@ -2540,17 +2525,18 @@ var _Plugin = class _Plugin {
2540
2525
  /**
2541
2526
  * Get the stream url from {@link Song#url}. Returns {@link Song#url} by default.
2542
2527
  * Not needed if the plugin plays song from YouTube.
2543
- * @param {string} url Input url
2544
- * @returns {string|Promise<string>}
2528
+ *
2529
+ * @param url - Input url
2545
2530
  */
2546
2531
  getStreamURL(url) {
2547
2532
  return url;
2548
2533
  }
2549
2534
  /**
2550
- * Get related songs from a supported url. {@link Song#member} should be `undefined`.
2551
- * Not needed to add {@link Song#related} because it will be added with this function later.
2552
- * @param {string} _url Input url
2553
- * @returns {Song[]|Promise<Song[]>}
2535
+ * Get related songs from a supported url. {@link Song#member} should be
2536
+ * `undefined`. Not needed to add {@link Song#related} because it will be added
2537
+ * with this function later.
2538
+ *
2539
+ * @param _url - Input url
2554
2540
  */
2555
2541
  getRelatedSongs(_url) {
2556
2542
  return [];
@@ -2767,10 +2753,10 @@ var DirectLinkPlugin = _DirectLinkPlugin;
2767
2753
 
2768
2754
  // src/DisTube.ts
2769
2755
  var import_ytsr = __toESM(require("@distube/ytsr"));
2770
- var import_tiny_typed_emitter2 = require("tiny-typed-emitter");
2756
+ var import_tiny_typed_emitter3 = require("tiny-typed-emitter");
2771
2757
  var { version } = require_package();
2772
2758
  var _getQueue, getQueue_fn;
2773
- var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2759
+ var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
2774
2760
  constructor(client, otp = {}) {
2775
2761
  super();
2776
2762
  __privateAdd(this, _getQueue);
@@ -2803,21 +2789,16 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2803
2789
  }
2804
2790
  /**
2805
2791
  * DisTube version
2806
- * @type {string}
2807
2792
  */
2808
2793
  get version() {
2809
2794
  return version;
2810
2795
  }
2811
2796
  /**
2812
- * Play / add a song or playlist from url. Search and play a song if it is not a valid url.
2797
+ * Play / add a song or playlist from url. Search and play a song if it is not a
2798
+ * valid url.
2813
2799
  *
2814
- * @param {Discord.BaseGuildVoiceChannel} voiceChannel The channel will be joined if the bot isn't in any channels,
2815
- * the bot will be moved to this channel if {@link DisTubeOptions}.joinNewVoiceChannel is `true`
2816
- * @param {string|Song|SearchResult|Playlist} song URL | Search string |
2817
- * {@link Song} | {@link SearchResult} | {@link Playlist}
2818
- * @param {PlayOptions} [options] Optional options
2819
- * @throws {DisTubeError}
2820
2800
  * @example
2801
+ * ```ts
2821
2802
  * client.on('message', (message) => {
2822
2803
  * if (!message.content.startsWith(config.prefix)) return;
2823
2804
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -2829,7 +2810,14 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2829
2810
  * message
2830
2811
  * });
2831
2812
  * });
2832
- * @returns {Promise<void>}
2813
+ * ```ts
2814
+ *
2815
+ * @throws {@link DisTubeError}
2816
+ *
2817
+ * @param voiceChannel - The channel will be joined if the bot isn't in any channels, the bot will be
2818
+ * moved to this channel if {@link DisTubeOptions}.joinNewVoiceChannel is `true`
2819
+ * @param song - URL | Search string | {@link Song} | {@link SearchResult} | {@link Playlist}
2820
+ * @param options - Optional options
2833
2821
  */
2834
2822
  async play(voiceChannel, song, options = {}) {
2835
2823
  if (!isSupportedVoiceChannel(voiceChannel)) {
@@ -2899,10 +2887,9 @@ ${e.message}`;
2899
2887
  }
2900
2888
  /**
2901
2889
  * Create a custom playlist
2902
- * @returns {Promise<Playlist>}
2903
- * @param {Array<string|Song|SearchResult>} songs Array of url, Song or SearchResult
2904
- * @param {CustomPlaylistOptions} [options] Optional options
2890
+ *
2905
2891
  * @example
2892
+ * ```ts
2906
2893
  * const songs = ["https://www.youtube.com/watch?v=xxx", "https://www.youtube.com/watch?v=yyy"];
2907
2894
  * const playlist = await distube.createCustomPlaylist(songs, {
2908
2895
  * member: message.member,
@@ -2910,6 +2897,10 @@ ${e.message}`;
2910
2897
  * parallel: true
2911
2898
  * });
2912
2899
  * distube.play(voiceChannel, playlist, { ... });
2900
+ * ```ts
2901
+ *
2902
+ * @param songs - Array of url, Song or SearchResult
2903
+ * @param options - Optional options
2913
2904
  */
2914
2905
  async createCustomPlaylist(songs, options = {}) {
2915
2906
  const { member, properties, parallel, metadata } = { parallel: true, ...options };
@@ -2930,13 +2921,14 @@ ${e.message}`;
2930
2921
  const promises = filteredSongs.map(
2931
2922
  (song) => this.handler.resolve(song, { member, metadata }).catch(() => void 0)
2932
2923
  );
2933
- resolvedSongs = (await Promise.all(promises)).filter((s) => Boolean(s));
2924
+ resolvedSongs = (await Promise.all(promises)).filter((s) => s instanceof Song);
2934
2925
  } else {
2935
- const resolved = [];
2926
+ resolvedSongs = [];
2936
2927
  for (const song of filteredSongs) {
2937
- resolved.push(await this.handler.resolve(song, { member, metadata }).catch(() => void 0));
2928
+ const resolved = await this.handler.resolve(song, { member, metadata }).catch(() => void 0);
2929
+ if (resolved instanceof Song)
2930
+ resolvedSongs.push(resolved);
2938
2931
  }
2939
- resolvedSongs = resolved.filter((s) => Boolean(s));
2940
2932
  }
2941
2933
  return new Playlist(resolvedSongs, { member, properties, metadata });
2942
2934
  }
@@ -2944,13 +2936,13 @@ ${e.message}`;
2944
2936
  * Search for a song. You can customize how user answers instead of send a number.
2945
2937
  * Then use {@link DisTube#play} to play it.
2946
2938
  *
2947
- * @param {string} string The string search for
2948
- * @param {Object} options Search options
2949
- * @param {number} [options.limit=10] Limit the results
2950
- * @param {SearchResultType} [options.type=SearchResultType.VIDEO] Type of results (`video` or `playlist`).
2951
- * @param {boolean} [options.safeSearch=false] Whether or not use safe search (YouTube restricted mode)
2952
- * @throws {Error}
2953
- * @returns {Promise<Array<SearchResult>>} Array of results
2939
+ * @param string - The string search for
2940
+ * @param options - Search options
2941
+ * @param options.limit - Limit the results
2942
+ * @param options.type - Type of results (`video` or `playlist`).
2943
+ * @param options.safeSearch - Whether or not use safe search (YouTube restricted mode)
2944
+ *
2945
+ * @returns Array of results
2954
2946
  */
2955
2947
  async search(string, options = {}) {
2956
2948
  const opts = { type: "video" /* VIDEO */, limit: 10, safeSearch: false, ...options };
@@ -2983,10 +2975,9 @@ ${e.message}`;
2983
2975
  }
2984
2976
  /**
2985
2977
  * Get the guild queue
2986
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
2987
- * @returns {Queue?}
2988
- * @throws {Error}
2978
+ *
2989
2979
  * @example
2980
+ * ```ts
2990
2981
  * client.on('message', (message) => {
2991
2982
  * if (!message.content.startsWith(config.prefix)) return;
2992
2983
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -2998,34 +2989,38 @@ ${e.message}`;
2998
2989
  * ).join("\n"));
2999
2990
  * }
3000
2991
  * });
2992
+ * ```ts
2993
+ *
2994
+ * @param guild - The type can be resolved to give a {@link Queue}
3001
2995
  */
3002
2996
  getQueue(guild) {
3003
2997
  return this.queues.get(guild);
3004
2998
  }
3005
2999
  /**
3006
3000
  * Pause the guild stream
3007
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3008
- * @returns {Queue} The guild queue
3009
- * @throws {Error}
3001
+ *
3002
+ * @param guild - The type can be resolved to give a {@link Queue}
3003
+ *
3004
+ * @returns The guild queue
3010
3005
  */
3011
3006
  pause(guild) {
3012
3007
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).pause();
3013
3008
  }
3014
3009
  /**
3015
3010
  * Resume the guild stream
3016
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3017
- * @returns {Queue} The guild queue
3018
- * @throws {Error}
3011
+ *
3012
+ * @param guild - The type can be resolved to give a {@link Queue}
3013
+ *
3014
+ * @returns The guild queue
3019
3015
  */
3020
3016
  resume(guild) {
3021
3017
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).resume();
3022
3018
  }
3023
3019
  /**
3024
3020
  * Stop the guild stream
3025
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3026
- * @returns {Promise<void>}
3027
- * @throws {Error}
3021
+ *
3028
3022
  * @example
3023
+ * ```ts
3029
3024
  * client.on('message', (message) => {
3030
3025
  * if (!message.content.startsWith(config.prefix)) return;
3031
3026
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3035,17 +3030,18 @@ ${e.message}`;
3035
3030
  * message.channel.send("Stopped the queue!");
3036
3031
  * }
3037
3032
  * });
3033
+ * ```ts
3034
+ *
3035
+ * @param guild - The type can be resolved to give a {@link Queue}
3038
3036
  */
3039
3037
  stop(guild) {
3040
3038
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).stop();
3041
3039
  }
3042
3040
  /**
3043
3041
  * Set the guild stream's volume
3044
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3045
- * @param {number} percent The percentage of volume you want to set
3046
- * @returns {Queue} The guild queue
3047
- * @throws {Error}
3042
+ *
3048
3043
  * @example
3044
+ * ```ts
3049
3045
  * client.on('message', (message) => {
3050
3046
  * if (!message.content.startsWith(config.prefix)) return;
3051
3047
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3053,18 +3049,23 @@ ${e.message}`;
3053
3049
  * if (command == "volume")
3054
3050
  * distube.setVolume(message, Number(args[0]));
3055
3051
  * });
3052
+ * ```ts
3053
+ *
3054
+ * @param guild - The type can be resolved to give a {@link Queue}
3055
+ * @param percent - The percentage of volume you want to set
3056
+ *
3057
+ * @returns The guild queue
3056
3058
  */
3057
3059
  setVolume(guild, percent) {
3058
3060
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).setVolume(percent);
3059
3061
  }
3060
3062
  /**
3061
- * Skip the playing song if there is a next song in the queue.
3062
- * <info>If {@link Queue#autoplay} is `true` and there is no up next song,
3063
- * DisTube will add and play a related song.</info>
3064
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3065
- * @returns {Promise<Song>} The new Song will be played
3066
- * @throws {Error}
3063
+ * Skip the playing song if there is a next song in the queue. <info>If {@link
3064
+ * Queue#autoplay} is `true` and there is no up next song, DisTube will add and
3065
+ * play a related song.</info>
3066
+ *
3067
3067
  * @example
3068
+ * ```ts
3068
3069
  * client.on('message', (message) => {
3069
3070
  * if (!message.content.startsWith(config.prefix)) return;
3070
3071
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3072,16 +3073,20 @@ ${e.message}`;
3072
3073
  * if (command == "skip")
3073
3074
  * distube.skip(message);
3074
3075
  * });
3076
+ * ```ts
3077
+ *
3078
+ * @param guild - The type can be resolved to give a {@link Queue}
3079
+ *
3080
+ * @returns The new Song will be played
3075
3081
  */
3076
3082
  skip(guild) {
3077
3083
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).skip();
3078
3084
  }
3079
3085
  /**
3080
3086
  * Play the previous song
3081
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3082
- * @returns {Promise<Song>} The new Song will be played
3083
- * @throws {Error}
3087
+ *
3084
3088
  * @example
3089
+ * ```ts
3085
3090
  * client.on('message', (message) => {
3086
3091
  * if (!message.content.startsWith(config.prefix)) return;
3087
3092
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3089,15 +3094,20 @@ ${e.message}`;
3089
3094
  * if (command == "previous")
3090
3095
  * distube.previous(message);
3091
3096
  * });
3097
+ * ```ts
3098
+ *
3099
+ * @param guild - The type can be resolved to give a {@link Queue}
3100
+ *
3101
+ * @returns The new Song will be played
3092
3102
  */
3093
3103
  previous(guild) {
3094
3104
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).previous();
3095
3105
  }
3096
3106
  /**
3097
3107
  * Shuffle the guild queue songs
3098
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3099
- * @returns {Promise<Queue>} The guild queue
3108
+ *
3100
3109
  * @example
3110
+ * ```ts
3101
3111
  * client.on('message', (message) => {
3102
3112
  * if (!message.content.startsWith(config.prefix)) return;
3103
3113
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3105,19 +3115,21 @@ ${e.message}`;
3105
3115
  * if (command == "shuffle")
3106
3116
  * distube.shuffle(message);
3107
3117
  * });
3118
+ * ```ts
3119
+ *
3120
+ * @param guild - The type can be resolved to give a {@link Queue}
3121
+ *
3122
+ * @returns The guild queue
3108
3123
  */
3109
3124
  shuffle(guild) {
3110
3125
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).shuffle();
3111
3126
  }
3112
3127
  /**
3113
- * Jump to the song number in the queue.
3114
- * The next one is 1, 2,...
3115
- * The previous one is -1, -2,...
3116
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3117
- * @param {number} num The song number to play
3118
- * @returns {Promise<Song>} The new Song will be played
3119
- * @throws {Error} if `num` is invalid number (0 < num < {@link Queue#songs}.length)
3128
+ * Jump to the song number in the queue. The next one is 1, 2,... The previous one
3129
+ * is -1, -2,...
3130
+ *
3120
3131
  * @example
3132
+ * ```ts
3121
3133
  * client.on('message', (message) => {
3122
3134
  * if (!message.content.startsWith(config.prefix)) return;
3123
3135
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3126,17 +3138,22 @@ ${e.message}`;
3126
3138
  * distube.jump(message, parseInt(args[0]))
3127
3139
  * .catch(err => message.channel.send("Invalid song number."));
3128
3140
  * });
3141
+ * ```ts
3142
+ *
3143
+ * @param guild - The type can be resolved to give a {@link Queue}
3144
+ * @param num - The song number to play
3145
+ *
3146
+ * @returns The new Song will be played
3129
3147
  */
3130
3148
  jump(guild, num) {
3131
3149
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).jump(num);
3132
3150
  }
3133
3151
  /**
3134
- * Set the repeat mode of the guild queue.\
3152
+ * Set the repeat mode of the guild queue.
3135
3153
  * Toggle mode `(Disabled -> Song -> Queue -> Disabled ->...)` if `mode` is `undefined`
3136
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3137
- * @param {RepeatMode?} [mode] The repeat modes (toggle if `undefined`)
3138
- * @returns {RepeatMode} The new repeat mode
3154
+ *
3139
3155
  * @example
3156
+ * ```ts
3140
3157
  * client.on('message', (message) => {
3141
3158
  * if (!message.content.startsWith(config.prefix)) return;
3142
3159
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3147,7 +3164,9 @@ ${e.message}`;
3147
3164
  * message.channel.send("Set repeat mode to `" + mode + "`");
3148
3165
  * }
3149
3166
  * });
3167
+ * ```ts
3150
3168
  * @example
3169
+ * ```ts
3151
3170
  * const { RepeatMode } = require("distube");
3152
3171
  * let mode;
3153
3172
  * switch(distube.setRepeatMode(message, parseInt(args[0]))) {
@@ -3162,16 +3181,21 @@ ${e.message}`;
3162
3181
  * break;
3163
3182
  * }
3164
3183
  * message.channel.send("Set repeat mode to `" + mode + "`");
3184
+ * ```ts
3185
+ *
3186
+ * @param guild - The type can be resolved to give a {@link Queue}
3187
+ * @param mode - The repeat modes (toggle if `undefined`)
3188
+ *
3189
+ * @returns The new repeat mode
3165
3190
  */
3166
3191
  setRepeatMode(guild, mode) {
3167
3192
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).setRepeatMode(mode);
3168
3193
  }
3169
3194
  /**
3170
3195
  * Toggle autoplay mode
3171
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3172
- * @returns {boolean} Autoplay mode state
3173
- * @throws {Error}
3196
+ *
3174
3197
  * @example
3198
+ * ```ts
3175
3199
  * client.on('message', (message) => {
3176
3200
  * if (!message.content.startsWith(config.prefix)) return;
3177
3201
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3181,6 +3205,11 @@ ${e.message}`;
3181
3205
  * message.channel.send("Set autoplay mode to `" + (mode ? "On" : "Off") + "`");
3182
3206
  * }
3183
3207
  * });
3208
+ * ```ts
3209
+ *
3210
+ * @param guild - The type can be resolved to give a {@link Queue}
3211
+ *
3212
+ * @returns Autoplay mode state
3184
3213
  */
3185
3214
  toggleAutoplay(guild) {
3186
3215
  const queue = __privateMethod(this, _getQueue, getQueue_fn).call(this, guild);
@@ -3189,18 +3218,19 @@ ${e.message}`;
3189
3218
  }
3190
3219
  /**
3191
3220
  * Add related song to the queue
3192
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3193
- * @returns {Promise<Song>} The guild queue
3221
+ *
3222
+ * @param guild - The type can be resolved to give a {@link Queue}
3223
+ *
3224
+ * @returns The guild queue
3194
3225
  */
3195
3226
  addRelatedSong(guild) {
3196
3227
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).addRelatedSong();
3197
3228
  }
3198
3229
  /**
3199
3230
  * Set the playing time to another position
3200
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3201
- * @param {number} time Time in seconds
3202
- * @returns {Queue} Seeked queue
3231
+ *
3203
3232
  * @example
3233
+ * ```ts
3204
3234
  * client.on('message', message => {
3205
3235
  * if (!message.content.startsWith(config.prefix)) return;
3206
3236
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3208,19 +3238,25 @@ ${e.message}`;
3208
3238
  * if (command = 'seek')
3209
3239
  * distube.seek(message, Number(args[0]));
3210
3240
  * });
3241
+ * ```ts
3242
+ *
3243
+ * @param guild - The type can be resolved to give a {@link Queue}
3244
+ * @param time - Time in seconds
3245
+ *
3246
+ * @returns Seeked queue
3211
3247
  */
3212
3248
  seek(guild, time) {
3213
3249
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).seek(time);
3214
3250
  }
3215
3251
  /**
3216
3252
  * Emit error event
3217
- * @param {Error} error error
3218
- * @param {Discord.BaseGuildTextChannel} [channel] Text channel where the error is encountered.
3219
- * @private
3253
+ *
3254
+ * @param error - error
3255
+ * @param channel - Text channel where the error is encountered.
3220
3256
  */
3221
3257
  emitError(error, channel) {
3222
- if (this.listeners("error").length) {
3223
- this.emit("error", channel, error);
3258
+ if (this.listeners("error" /* ERROR */).length) {
3259
+ this.emit("error" /* ERROR */, channel, error);
3224
3260
  } else {
3225
3261
  console.error(error);
3226
3262
  console.warn("Unhandled 'error' event.");