distube 4.1.1 → 4.2.1

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.1",
66
+ version: "4.2.1",
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.13.2",
118
+ "@distube/ytdl-core": "^4.13.3",
128
119
  "@distube/ytpl": "^1.2.1",
129
- "@distube/ytsr": "^1.2.0",
130
- "prism-media": "npm:@distube/prism-media@latest",
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.27.2"
124
+ undici: "^6.13.0"
135
125
  },
136
126
  devDependencies: {
137
- "@babel/core": "^7.23.2",
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.23.2",
142
- "@babel/preset-typescript": "^7.23.2",
143
- "@commitlint/cli": "^18.2.0",
144
- "@commitlint/config-conventional": "^18.1.0",
145
- "@discordjs/voice": "^0.16.0",
146
- "@distubejs/docgen": "distubejs/docgen",
147
- "@types/jest": "^29.5.8",
148
- "@types/node": "^20.9.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",
149
138
  "@types/tough-cookie": "^4.0.5",
150
- "@typescript-eslint/eslint-plugin": "^6.10.0",
151
- "@typescript-eslint/parser": "^6.10.0",
139
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
140
+ "@typescript-eslint/parser": "^7.7.0",
152
141
  "babel-jest": "^29.7.0",
153
- "discord.js": "^14.13.0",
154
- eslint: "^8.53.0",
155
- "eslint-config-distube": "^1.6.4",
156
- "eslint-config-prettier": "^9.0.0",
157
- "eslint-plugin-deprecation": "^2.0.0",
158
- "eslint-plugin-jsdoc": "^46.8.2",
159
- husky: "^8.0.3",
142
+ "discord.js": "^14.14.1",
143
+ eslint: "^8.57.0",
144
+ "eslint-config-distube": "^1.7.0",
145
+ husky: "^9.0.11",
160
146
  jest: "^29.7.0",
161
- "jsdoc-babel": "^0.5.0",
162
147
  "nano-staged": "^0.8.0",
163
- "npm-check-updates": "^16.14.6",
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
  });
@@ -223,6 +202,7 @@ __export(src_exports, {
223
202
  Song: () => Song,
224
203
  StreamType: () => StreamType,
225
204
  TaskQueue: () => TaskQueue,
205
+ checkFFmpeg: () => checkFFmpeg,
226
206
  checkIntents: () => checkIntents,
227
207
  checkInvalidKey: () => checkInvalidKey,
228
208
  chooseBestVideoFormat: () => chooseBestVideoFormat,
@@ -290,6 +270,7 @@ var Events = /* @__PURE__ */ ((Events2) => {
290
270
  Events2["SEARCH_DONE"] = "searchDone";
291
271
  Events2["SEARCH_INVALID_ANSWER"] = "searchInvalidAnswer";
292
272
  Events2["SEARCH_RESULT"] = "searchResult";
273
+ Events2["FFMPEG_DEBUG"] = "ffmpegDebug";
293
274
  return Events2;
294
275
  })(Events || {});
295
276
 
@@ -351,6 +332,8 @@ var ERROR_MESSAGES = {
351
332
  VOICE_RECONNECT_FAILED: "Cannot reconnect to the voice channel",
352
333
  VOICE_DIFFERENT_GUILD: "Cannot join a voice channel in a different guild",
353
334
  VOICE_DIFFERENT_CLIENT: "Cannot join a voice channel created by a different client",
335
+ FFMPEG_EXITED: (code) => `ffmpeg exited with code ${code}`,
336
+ FFMPEG_NOT_INSTALLED: (path) => `ffmpeg is not installed at '${path}' path`,
354
337
  NO_QUEUE: "There is no playing queue in this guild",
355
338
  QUEUE_EXIST: "This guild has a Queue already",
356
339
  PAUSED: "The queue has been paused already",
@@ -411,15 +394,13 @@ var _TaskQueue = class _TaskQueue {
411
394
  constructor() {
412
395
  /**
413
396
  * The task array
414
- * @type {Task[]}
415
- * @private
416
397
  */
417
398
  __privateAdd(this, _tasks, []);
418
399
  }
419
400
  /**
420
401
  * 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>}
402
+ *
403
+ * @param resolveInfo - Whether the task is a resolving info task
423
404
  */
424
405
  queuing(resolveInfo = false) {
425
406
  const next = this.remaining ? __privateGet(this, _tasks)[__privateGet(this, _tasks).length - 1].promise : Promise.resolve();
@@ -434,14 +415,12 @@ var _TaskQueue = class _TaskQueue {
434
415
  }
435
416
  /**
436
417
  * The remaining number of tasks
437
- * @type {number}
438
418
  */
439
419
  get remaining() {
440
420
  return __privateGet(this, _tasks).length;
441
421
  }
442
422
  /**
443
423
  * Whether or not having a resolving info task
444
- * @type {boolean}
445
424
  */
446
425
  get hasResolveTask() {
447
426
  return __privateGet(this, _tasks).some((t) => t.resolveInfo);
@@ -456,11 +435,9 @@ var _metadata, _member;
456
435
  var _Playlist = class _Playlist {
457
436
  /**
458
437
  * 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
438
+ *
439
+ * @param playlist - Playlist
440
+ * @param options - Optional options
464
441
  */
465
442
  constructor(playlist, options = {}) {
466
443
  __publicField(this, "source");
@@ -504,21 +481,18 @@ var _Playlist = class _Playlist {
504
481
  }
505
482
  /**
506
483
  * Playlist duration in second.
507
- * @type {number}
508
484
  */
509
485
  get duration() {
510
486
  return this.songs.reduce((prev, next) => prev + next.duration, 0);
511
487
  }
512
488
  /**
513
489
  * Formatted duration string `hh:mm:ss`.
514
- * @type {string}
515
490
  */
516
491
  get formattedDuration() {
517
492
  return formatDuration(this.duration);
518
493
  }
519
494
  /**
520
495
  * User requested.
521
- * @type {Discord.GuildMember?}
522
496
  */
523
497
  get member() {
524
498
  return __privateGet(this, _member);
@@ -531,7 +505,6 @@ var _Playlist = class _Playlist {
531
505
  }
532
506
  /**
533
507
  * User requested.
534
- * @type {Discord.User?}
535
508
  */
536
509
  get user() {
537
510
  return this.member?.user;
@@ -553,7 +526,8 @@ var Playlist = _Playlist;
553
526
  var _ISearchResult = class _ISearchResult {
554
527
  /**
555
528
  * Create a search result
556
- * @param {Object} info ytsr result
529
+ *
530
+ * @param info - ytsr result
557
531
  */
558
532
  constructor(info) {
559
533
  __publicField(this, "source");
@@ -621,11 +595,9 @@ var _metadata2, _member2, _playlist;
621
595
  var _Song = class _Song {
622
596
  /**
623
597
  * 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
598
+ *
599
+ * @param info - Raw info
600
+ * @param options - Optional options
629
601
  */
630
602
  constructor(info, options = {}) {
631
603
  __publicField(this, "source");
@@ -702,8 +674,8 @@ var _Song = class _Song {
702
674
  }
703
675
  /**
704
676
  * Patch data from other source
705
- * @param {OtherSongInfo} info Video info
706
- * @private
677
+ *
678
+ * @param info - Video info
707
679
  */
708
680
  _patchOther(info) {
709
681
  this.id = info.id;
@@ -737,7 +709,6 @@ var _Song = class _Song {
737
709
  }
738
710
  /**
739
711
  * The playlist added this song
740
- * @type {Playlist?}
741
712
  */
742
713
  get playlist() {
743
714
  return __privateGet(this, _playlist);
@@ -750,7 +721,6 @@ var _Song = class _Song {
750
721
  }
751
722
  /**
752
723
  * User requested.
753
- * @type {Discord.GuildMember?}
754
724
  */
755
725
  get member() {
756
726
  return __privateGet(this, _member2);
@@ -761,7 +731,6 @@ var _Song = class _Song {
761
731
  }
762
732
  /**
763
733
  * User requested.
764
- * @type {Discord.User?}
765
734
  */
766
735
  get user() {
767
736
  return this.member?.user;
@@ -787,57 +756,48 @@ var _DisTubeBase = class _DisTubeBase {
787
756
  }
788
757
  /**
789
758
  * Emit the {@link DisTube} of this base
790
- * @param {string} eventName Event name
791
- * @param {...any} args arguments
792
- * @returns {boolean}
759
+ *
760
+ * @param eventName - Event name
761
+ * @param args - arguments
793
762
  */
794
763
  emit(eventName, ...args) {
795
764
  return this.distube.emit(eventName, ...args);
796
765
  }
797
766
  /**
798
767
  * Emit error event
799
- * @param {Error} error error
800
- * @param {Discord.BaseGuildTextChannel} [channel] Text channel where the error is encountered.
768
+ *
769
+ * @param error - error
770
+ * @param channel - Text channel where the error is encountered.
801
771
  */
802
772
  emitError(error, channel) {
803
773
  this.distube.emitError(error, channel);
804
774
  }
805
775
  /**
806
776
  * The queue manager
807
- * @type {QueueManager}
808
- * @readonly
809
777
  */
810
778
  get queues() {
811
779
  return this.distube.queues;
812
780
  }
813
781
  /**
814
782
  * The voice manager
815
- * @type {DisTubeVoiceManager}
816
- * @readonly
817
783
  */
818
784
  get voices() {
819
785
  return this.distube.voices;
820
786
  }
821
787
  /**
822
788
  * Discord.js client
823
- * @type {Discord.Client}
824
- * @readonly
825
789
  */
826
790
  get client() {
827
791
  return this.distube.client;
828
792
  }
829
793
  /**
830
794
  * DisTube options
831
- * @type {DisTubeOptions}
832
- * @readonly
833
795
  */
834
796
  get options() {
835
797
  return this.distube.options;
836
798
  }
837
799
  /**
838
800
  * DisTube handler
839
- * @type {DisTubeHandler}
840
- * @readonly
841
801
  */
842
802
  get handler() {
843
803
  return this.distube.handler;
@@ -863,6 +823,7 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
863
823
  __publicField(this, "audioResource");
864
824
  __publicField(this, "emittedError");
865
825
  __publicField(this, "isDisconnected", false);
826
+ __publicField(this, "stream");
866
827
  __privateAdd(this, _channel, void 0);
867
828
  __privateAdd(this, _volume, 100);
868
829
  this.voices = voiceManager;
@@ -906,7 +867,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
906
867
  }
907
868
  /**
908
869
  * The voice channel id the bot is in
909
- * @type {Snowflake?}
910
870
  */
911
871
  get channelId() {
912
872
  return this.connection?.joinConfig?.channelId ?? void 0;
@@ -949,8 +909,8 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
949
909
  }
950
910
  /**
951
911
  * Join a voice channel with this connection
952
- * @param {Discord.BaseGuildVoiceChannel} [channel] A voice channel
953
- * @returns {Promise<DisTubeVoice>}
912
+ *
913
+ * @param channel - A voice channel
954
914
  */
955
915
  async join(channel) {
956
916
  const TIMEOUT = 3e4;
@@ -970,7 +930,8 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
970
930
  }
971
931
  /**
972
932
  * Leave the voice channel of this connection
973
- * @param {Error} [error] Optional, an error to emit with 'error' event.
933
+ *
934
+ * @param error - Optional, an error to emit with 'error' event.
974
935
  */
975
936
  leave(error) {
976
937
  this.stop(true);
@@ -984,33 +945,36 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
984
945
  }
985
946
  /**
986
947
  * 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
948
+ *
949
+ * @param force - If true, will force the {@link DisTubeVoice#audioPlayer} to enter the Idle state even
950
+ * if the {@link DisTubeVoice#audioResource} has silence padding frames.
990
951
  */
991
952
  stop(force = false) {
992
953
  this.audioPlayer.stop(force);
954
+ this.stream?.kill?.();
993
955
  }
994
956
  /**
995
- * Play a readable stream
996
- * @private
997
- * @param {DisTubeStream} stream Readable stream
957
+ * Play a {@link DisTubeStream}
958
+ *
959
+ * @param dtStream - DisTubeStream
998
960
  */
999
- play(stream) {
961
+ play(dtStream) {
1000
962
  this.emittedError = false;
1001
- stream.stream.on("error", (error) => {
963
+ dtStream.on("error", (error) => {
1002
964
  if (this.emittedError || error.code === "ERR_STREAM_PREMATURE_CLOSE")
1003
965
  return;
1004
966
  this.emittedError = true;
1005
967
  this.emit("error", error);
1006
968
  });
1007
- this.audioResource = (0, import_voice.createAudioResource)(stream.stream, {
1008
- inputType: stream.type,
969
+ this.audioResource = (0, import_voice.createAudioResource)(dtStream.stream, {
970
+ inputType: dtStream.type,
1009
971
  inlineVolume: true
1010
972
  });
1011
973
  this.volume = __privateGet(this, _volume);
1012
974
  if (this.audioPlayer.state.status !== import_voice.AudioPlayerStatus.Paused)
1013
975
  this.audioPlayer.play(this.audioResource);
976
+ this.stream?.kill?.();
977
+ this.stream = dtStream;
1014
978
  }
1015
979
  set volume(volume) {
1016
980
  if (typeof volume !== "number" || isNaN(volume)) {
@@ -1027,7 +991,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1027
991
  }
1028
992
  /**
1029
993
  * Playback duration of the audio resource in seconds
1030
- * @type {number}
1031
994
  */
1032
995
  get playbackDuration() {
1033
996
  return (this.audioResource?.playbackDuration ?? 0) / 1e3;
@@ -1046,22 +1009,22 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1046
1009
  }
1047
1010
  /**
1048
1011
  * Whether the bot is self-deafened
1049
- * @type {boolean}
1050
1012
  */
1051
1013
  get selfDeaf() {
1052
1014
  return this.connection.joinConfig.selfDeaf;
1053
1015
  }
1054
1016
  /**
1055
1017
  * Whether the bot is self-muted
1056
- * @type {boolean}
1057
1018
  */
1058
1019
  get selfMute() {
1059
1020
  return this.connection.joinConfig.selfMute;
1060
1021
  }
1061
1022
  /**
1062
1023
  * 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
1024
+ *
1025
+ * @param selfDeaf - Whether or not the bot should be self-deafened
1026
+ *
1027
+ * @returns true if the voice state was successfully updated, otherwise false
1065
1028
  */
1066
1029
  setSelfDeaf(selfDeaf) {
1067
1030
  if (typeof selfDeaf !== "boolean") {
@@ -1074,8 +1037,10 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1074
1037
  }
1075
1038
  /**
1076
1039
  * 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
1040
+ *
1041
+ * @param selfMute - Whether or not the bot should be self-muted
1042
+ *
1043
+ * @returns true if the voice state was successfully updated, otherwise false
1079
1044
  */
1080
1045
  setSelfMute(selfMute) {
1081
1046
  if (typeof selfMute !== "boolean") {
@@ -1088,7 +1053,6 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
1088
1053
  }
1089
1054
  /**
1090
1055
  * The voice state of this connection
1091
- * @type {Discord.VoiceState?}
1092
1056
  */
1093
1057
  get voiceState() {
1094
1058
  return this.channel?.guild?.members?.me?.voice;
@@ -1114,60 +1078,134 @@ __name(_DisTubeVoice, "DisTubeVoice");
1114
1078
  var DisTubeVoice = _DisTubeVoice;
1115
1079
 
1116
1080
  // src/core/DisTubeStream.ts
1117
- var import_prism_media = require("prism-media");
1081
+ var import_node_stream = require("stream");
1082
+ var import_child_process = require("child_process");
1083
+ var import_tiny_typed_emitter2 = require("tiny-typed-emitter");
1118
1084
  var import_voice2 = require("@discordjs/voice");
1119
1085
  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");
1120
- var _DisTubeStream = class _DisTubeStream {
1086
+ var checked = process.env.NODE_ENV === "test";
1087
+ var checkFFmpeg = /* @__PURE__ */ __name((distube) => {
1088
+ if (checked)
1089
+ return;
1090
+ const path = distube.options.ffmpeg.path;
1091
+ const debug = /* @__PURE__ */ __name((str) => distube.emit("ffmpegDebug" /* FFMPEG_DEBUG */, str), "debug");
1092
+ try {
1093
+ debug(`[test] spawn ffmpeg at '${path}' path`);
1094
+ const process2 = (0, import_child_process.spawnSync)(path, ["-h"], { windowsHide: true, shell: true, encoding: "utf-8" });
1095
+ if (process2.error)
1096
+ throw process2.error;
1097
+ if (process2.stderr && !process2.stdout)
1098
+ throw new Error(process2.stderr);
1099
+ const result = process2.output.join("\n");
1100
+ const version2 = /ffmpeg version (\S+)/iu.exec(result)?.[1];
1101
+ if (!version2)
1102
+ throw new Error("Invalid FFmpeg version");
1103
+ debug(`[test] ffmpeg version: ${version2}`);
1104
+ if (result.includes("--enable-libopus")) {
1105
+ debug("[test] ffmpeg supports libopus");
1106
+ } else {
1107
+ debug("[test] ffmpeg does not support libopus");
1108
+ distube.options.streamType = 1 /* RAW */;
1109
+ }
1110
+ } catch (e) {
1111
+ debug(`[test] failed to spawn ffmpeg at '${path}': ${e?.stack ?? e}`);
1112
+ throw new DisTubeError("FFMPEG_NOT_INSTALLED", path);
1113
+ }
1114
+ checked = true;
1115
+ }, "checkFFmpeg");
1116
+ var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.TypedEmitter {
1121
1117
  /**
1122
1118
  * Create a DisTubeStream to play with {@link DisTubeVoice}
1123
- * @param {string} url Stream URL
1124
- * @param {StreamOptions} options Stream options
1125
- * @private
1119
+ *
1120
+ * @param url - Stream URL
1121
+ * @param options - Stream options
1126
1122
  */
1127
- constructor(url, options) {
1128
- __publicField(this, "type");
1123
+ constructor(url, { ffmpeg, seek, type }) {
1124
+ super();
1125
+ __publicField(this, "killed", false);
1126
+ __publicField(this, "process");
1129
1127
  __publicField(this, "stream");
1128
+ __publicField(this, "type");
1130
1129
  __publicField(this, "url");
1131
1130
  this.url = url;
1132
- this.type = !options.type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
1133
- const args = [
1134
- "-reconnect",
1135
- "1",
1136
- "-reconnect_streamed",
1137
- "1",
1138
- "-reconnect_delay_max",
1139
- "5",
1140
- "-i",
1141
- url,
1142
- "-analyzeduration",
1143
- "0",
1144
- "-loglevel",
1145
- "0",
1146
- "-ar",
1147
- "48000",
1148
- "-ac",
1149
- "2",
1150
- "-f"
1151
- ];
1152
- if (!options.type)
1153
- args.push("opus", "-acodec", "libopus");
1154
- else
1155
- args.push("s16le");
1156
- if (typeof options.seek === "number" && options.seek > 0)
1157
- args.unshift("-ss", options.seek.toString());
1158
- if (Array.isArray(options.ffmpegArgs) && options.ffmpegArgs.length)
1159
- args.push(...options.ffmpegArgs);
1160
- this.stream = new import_prism_media.FFmpeg({ args, shell: false });
1161
- this.stream._readableState && (this.stream._readableState.highWaterMark = 1 << 25);
1162
- }
1163
- /**
1164
- * Create a stream from ytdl video formats
1165
- * @param {Song} song A YouTube Song
1166
- * @param {StreamOptions} options options
1167
- * @returns {DisTubeStream}
1168
- * @private
1169
- */
1170
- static YouTube(song, options = {}) {
1131
+ this.type = !type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
1132
+ const opts = {
1133
+ reconnect: 1,
1134
+ reconnect_streamed: 1,
1135
+ reconnect_delay_max: 5,
1136
+ analyzeduration: 0,
1137
+ hide_banner: true,
1138
+ ...ffmpeg.args.global,
1139
+ ...ffmpeg.args.input,
1140
+ i: url,
1141
+ ar: 48e3,
1142
+ ac: 2,
1143
+ ...ffmpeg.args.output
1144
+ };
1145
+ if (!type) {
1146
+ opts.f = "opus";
1147
+ opts.acodec = "libopus";
1148
+ } else {
1149
+ opts.f = "s16le";
1150
+ }
1151
+ if (typeof seek === "number" && seek > 0)
1152
+ opts.ss = seek.toString();
1153
+ if (typeof ffmpeg.args === "object")
1154
+ Object.assign(opts, ffmpeg.args);
1155
+ this.process = (0, import_child_process.spawn)(
1156
+ ffmpeg.path,
1157
+ [
1158
+ ...Object.entries(opts).flatMap(
1159
+ ([key, value]) => Array.isArray(value) ? value.filter(Boolean).map((v) => [`-${key}`, String(v)]) : value == null || value === false ? [] : [value === true ? `-${key}` : [`-${key}`, String(value)]]
1160
+ ).flat(),
1161
+ "pipe:1"
1162
+ ],
1163
+ { stdio: ["ignore", "pipe", "pipe"], shell: false, windowsHide: true }
1164
+ ).on("error", (err) => {
1165
+ this.debug(`[process] error: ${err.message}`);
1166
+ this.emit("error", err);
1167
+ }).on("exit", (code, signal) => {
1168
+ this.debug(`[process] exit: code=${code ?? "unknown"} signal=${signal ?? "unknown"}`);
1169
+ if (!code || [0, 255].includes(code))
1170
+ return;
1171
+ this.debug(`[process] error: ffmpeg exited with code ${code}`);
1172
+ this.emit("error", new DisTubeError("FFMPEG_EXITED", code));
1173
+ });
1174
+ if (!this.process.stdout || !this.process.stderr) {
1175
+ this.kill();
1176
+ throw new Error("Failed to create ffmpeg process");
1177
+ }
1178
+ this.stream = new import_node_stream.PassThrough();
1179
+ this.stream.on("close", () => this.kill()).on("error", (err) => {
1180
+ this.debug(`[stream] error: ${err.message}`);
1181
+ this.emit("error", err);
1182
+ }).on("finish", () => this.debug("[stream] log: stream finished"));
1183
+ this.process.stdout.pipe(this.stream);
1184
+ this.process.stderr.setEncoding("utf8")?.on("data", (data) => {
1185
+ const lines = data.split(/\r\n|\r|\n/u);
1186
+ for (const line of lines) {
1187
+ if (/^\s*$/.test(line))
1188
+ continue;
1189
+ this.debug(`[ffmpeg] log: ${line}`);
1190
+ }
1191
+ });
1192
+ }
1193
+ debug(debug) {
1194
+ this.emit("debug", debug);
1195
+ }
1196
+ kill() {
1197
+ if (this.killed)
1198
+ return;
1199
+ this.process.kill("SIGKILL");
1200
+ this.killed = true;
1201
+ }
1202
+ /**
1203
+ * Create a stream from a YouTube {@link Song}
1204
+ *
1205
+ * @param song - A YouTube Song
1206
+ * @param options - options
1207
+ */
1208
+ static YouTube(song, options) {
1171
1209
  if (song.source !== "youtube")
1172
1210
  throw new DisTubeError("INVALID_TYPE", "youtube", song.source, "Song#source");
1173
1211
  if (!song.formats?.length)
@@ -1182,18 +1220,17 @@ var _DisTubeStream = class _DisTubeStream {
1182
1220
  }
1183
1221
  /**
1184
1222
  * Create a stream from a stream url
1185
- * @param {string} url stream url
1186
- * @param {StreamOptions} options options
1187
- * @returns {DisTubeStream}
1188
- * @private
1223
+ *
1224
+ * @param url - stream url
1225
+ * @param options - options
1189
1226
  */
1190
- static DirectLink(url, options = {}) {
1191
- if (!options || typeof options !== "object" || Array.isArray(options)) {
1192
- throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1193
- }
1227
+ static DirectLink(url, options) {
1194
1228
  if (typeof url !== "string" || !isURL(url)) {
1195
1229
  throw new DisTubeError("INVALID_TYPE", "an URL", url);
1196
1230
  }
1231
+ if (!options || typeof options !== "object" || Array.isArray(options)) {
1232
+ throw new DisTubeError("INVALID_TYPE", "object", options, "options");
1233
+ }
1197
1234
  return new _DisTubeStream(url, options);
1198
1235
  }
1199
1236
  };
@@ -1233,7 +1270,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1233
1270
  delete queue._emptyTimeout;
1234
1271
  if (isVoiceChannelEmpty(oldState)) {
1235
1272
  queue.voice.leave();
1236
- this.emit("empty", queue);
1273
+ this.emit("empty" /* EMPTY */, queue);
1237
1274
  if (queue.stopped)
1238
1275
  queue.remove();
1239
1276
  }
@@ -1248,7 +1285,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1248
1285
  const cookies = __privateSet(this, _cookie, this.options.youtubeCookie);
1249
1286
  if (typeof cookies === "string") {
1250
1287
  console.warn(
1251
- "\x1B[33mWARNING:\x1B[0m You are using the old YouTube cookie format, please use the new one instead. (https://distube.js.org/#/docs/DisTube/main/general/cookie)"
1288
+ "\x1B[33mWARNING:\x1B[0m You are using the old YouTube cookie format, please use the new one instead. (https://github.com/skick1234/DisTube/wiki/YouTube-Cookies)"
1252
1289
  );
1253
1290
  options.agent = import_ytdl_core.default.createAgent(
1254
1291
  cookies.split(";").map((c) => import_tough_cookie.Cookie.parse(c)).filter(isTruthy)
@@ -1267,9 +1304,8 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1267
1304
  return jar.getCookieStringSync("https://www.youtube.com");
1268
1305
  }
1269
1306
  /**
1270
- * @param {string} url url
1271
- * @param {boolean} [basic=false] getBasicInfo?
1272
- * @returns {Promise<ytdl.videoInfo>}
1307
+ * @param url - url
1308
+ * @param basic - getBasicInfo?
1273
1309
  */
1274
1310
  getYouTubeInfo(url, basic = false) {
1275
1311
  if (basic)
@@ -1278,10 +1314,13 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1278
1314
  }
1279
1315
  /**
1280
1316
  * Resolve a url or a supported object to a {@link Song} or {@link Playlist}
1281
- * @param {string|Song|SearchResult|Playlist} song URL | {@link Song}| {@link SearchResult} | {@link Playlist}
1282
- * @param {ResolveOptions} [options] Optional options
1283
- * @returns {Promise<Song|Playlist|null>} Resolved
1284
- * @throws {DisTubeError}
1317
+ *
1318
+ * @throws {@link DisTubeError}
1319
+ *
1320
+ * @param song - URL | {@link Song}| {@link SearchResult} | {@link Playlist}
1321
+ * @param options - Optional options
1322
+ *
1323
+ * @returns Resolved
1285
1324
  */
1286
1325
  async resolve(song, options = {}) {
1287
1326
  if (song instanceof Song || song instanceof Playlist) {
@@ -1315,9 +1354,9 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1315
1354
  }
1316
1355
  /**
1317
1356
  * Resolve Song[] or YouTube playlist url to a Playlist
1318
- * @param {Playlist|Song[]|string} playlist Resolvable playlist
1319
- * @param {ResolvePlaylistOptions} options Optional options
1320
- * @returns {Promise<Playlist>}
1357
+ *
1358
+ * @param playlist - Resolvable playlist
1359
+ * @param options - Optional options
1321
1360
  */
1322
1361
  async resolvePlaylist(playlist, options = {}) {
1323
1362
  const { member, source, metadata } = { source: "youtube", ...options };
@@ -1346,11 +1385,14 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1346
1385
  return new Playlist(playlist, { member, properties: { source }, metadata });
1347
1386
  }
1348
1387
  /**
1349
- * Search for a song, fire {@link DisTube#event:error} if not found.
1350
- * @param {Discord.Message} message The original message from an user
1351
- * @param {string} query The query string
1352
- * @returns {Promise<SearchResult?>} Song info
1353
- * @throws {DisTubeError}
1388
+ * Search for a song, fire {@link DisTube#error} if not found.
1389
+ *
1390
+ * @throws {@link DisTubeError}
1391
+ *
1392
+ * @param message - The original message from an user
1393
+ * @param query - The query string
1394
+ *
1395
+ * @returns Song info
1354
1396
  */
1355
1397
  async searchSong(message, query) {
1356
1398
  if (!isMessageInstance(message))
@@ -1364,7 +1406,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1364
1406
  limit,
1365
1407
  safeSearch: this.options.nsfw ? false : !isNsfwChannel(message.channel)
1366
1408
  }).catch(() => {
1367
- if (!this.emit("searchNoResult", message, query)) {
1409
+ if (!this.emit("searchNoResult" /* SEARCH_NO_RESULT */, message, query)) {
1368
1410
  console.warn("searchNoResult event does not have any listeners! Emits `error` event instead.");
1369
1411
  throw new DisTubeError("NO_RESULT");
1370
1412
  }
@@ -1376,13 +1418,16 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1376
1418
  /**
1377
1419
  * Create a message collector for selecting search results.
1378
1420
  *
1379
- * Needed events: {@link DisTube#event:searchResult}, {@link DisTube#event:searchCancel},
1380
- * {@link DisTube#event:searchInvalidAnswer}, {@link DisTube#event:searchDone}.
1381
- * @param {Discord.Message} message The original message from an user
1382
- * @param {Array<SearchResult|Song|Playlist>} results The search results
1383
- * @param {string?} [query] The query string
1384
- * @returns {Promise<SearchResult|Song|Playlist|null>} Selected result
1385
- * @throws {DisTubeError}
1421
+ * Needed events: {@link DisTube#searchResult}, {@link DisTube#searchCancel},
1422
+ * {@link DisTube#searchInvalidAnswer}, {@link DisTube#searchDone}.
1423
+ *
1424
+ * @throws {@link DisTubeError}
1425
+ *
1426
+ * @param message - The original message from an user
1427
+ * @param results - The search results
1428
+ * @param query - The query string
1429
+ *
1430
+ * @returns Selected result
1386
1431
  */
1387
1432
  async createSearchMessageCollector(message, results, query) {
1388
1433
  if (!isMessageInstance(message))
@@ -1392,11 +1437,11 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1392
1437
  }
1393
1438
  if (this.options.searchSongs > 1) {
1394
1439
  const searchEvents = [
1395
- "searchNoResult",
1396
- "searchResult",
1397
- "searchCancel",
1398
- "searchInvalidAnswer",
1399
- "searchDone"
1440
+ "searchNoResult" /* SEARCH_NO_RESULT */,
1441
+ "searchResult" /* SEARCH_RESULT */,
1442
+ "searchCancel" /* SEARCH_CANCEL */,
1443
+ "searchInvalidAnswer" /* SEARCH_INVALID_ANSWER */,
1444
+ "searchDone" /* SEARCH_DONE */
1400
1445
  ];
1401
1446
  for (const evn of searchEvents) {
1402
1447
  if (this.distube.listenerCount(evn) === 0) {
@@ -1413,7 +1458,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1413
1458
  let result = results[0];
1414
1459
  if (limit > 1) {
1415
1460
  results.splice(limit);
1416
- this.emit("searchResult", message, results, query);
1461
+ this.emit("searchResult" /* SEARCH_RESULT */, message, results, query);
1417
1462
  const answers = await message.channel.awaitMessages({
1418
1463
  filter: (m) => m.author.id === message.author.id,
1419
1464
  max: 1,
@@ -1422,26 +1467,27 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1422
1467
  }).catch(() => void 0);
1423
1468
  const ans = answers?.first();
1424
1469
  if (!ans) {
1425
- this.emit("searchCancel", message, query);
1470
+ this.emit("searchCancel" /* SEARCH_CANCEL */, message, query);
1426
1471
  return null;
1427
1472
  }
1428
1473
  const index = parseInt(ans.content, 10);
1429
1474
  if (isNaN(index) || index > results.length || index < 1) {
1430
- this.emit("searchInvalidAnswer", message, ans, query);
1475
+ this.emit("searchInvalidAnswer" /* SEARCH_INVALID_ANSWER */, message, ans, query);
1431
1476
  return null;
1432
1477
  }
1433
- this.emit("searchDone", message, ans, query);
1478
+ this.emit("searchDone" /* SEARCH_DONE */, message, ans, query);
1434
1479
  result = results[index - 1];
1435
1480
  }
1436
1481
  return result;
1437
1482
  }
1438
1483
  /**
1439
1484
  * Play or add a {@link Playlist} to the queue.
1440
- * @param {Discord.BaseGuildVoiceChannel} voiceChannel A voice channel
1441
- * @param {Playlist|string} playlist A YouTube playlist url | a Playlist
1442
- * @param {PlayHandlerOptions} [options] Optional options
1443
- * @returns {Promise<void>}
1444
- * @throws {DisTubeError}
1485
+ *
1486
+ * @throws {@link DisTubeError}
1487
+ *
1488
+ * @param voiceChannel - A voice channel
1489
+ * @param playlist - A YouTube playlist url | a Playlist
1490
+ * @param options - Optional options
1445
1491
  */
1446
1492
  async playPlaylist(voiceChannel, playlist, options = {}) {
1447
1493
  const { textChannel, skip } = { skip: false, ...options };
@@ -1464,23 +1510,24 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1464
1510
  if (skip)
1465
1511
  queue.skip();
1466
1512
  else
1467
- this.emit("addList", queue, playlist);
1513
+ this.emit("addList" /* ADD_LIST */, queue, playlist);
1468
1514
  } else {
1469
1515
  const newQueue = await this.queues.create(voiceChannel, playlist.songs, textChannel);
1470
1516
  if (newQueue instanceof Queue) {
1471
1517
  if (this.options.emitAddListWhenCreatingQueue)
1472
- this.emit("addList", newQueue, playlist);
1473
- this.emit("playSong", newQueue, newQueue.songs[0]);
1518
+ this.emit("addList" /* ADD_LIST */, newQueue, playlist);
1519
+ this.emit("playSong" /* PLAY_SONG */, newQueue, newQueue.songs[0]);
1474
1520
  }
1475
1521
  }
1476
1522
  }
1477
1523
  /**
1478
1524
  * Play or add a {@link Song} to the queue.
1479
- * @param {Discord.BaseGuildVoiceChannel} voiceChannel A voice channel
1480
- * @param {Song} song A YouTube playlist url | a Playlist
1481
- * @param {PlayHandlerOptions} [options] Optional options
1482
- * @returns {Promise<void>}
1483
- * @throws {DisTubeError}
1525
+ *
1526
+ * @throws {@link DisTubeError}
1527
+ *
1528
+ * @param voiceChannel - A voice channel
1529
+ * @param song - A YouTube playlist url | a Playlist
1530
+ * @param options - Optional options
1484
1531
  */
1485
1532
  async playSong(voiceChannel, song, options = {}) {
1486
1533
  if (!(song instanceof Song))
@@ -1498,27 +1545,26 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1498
1545
  if (skip)
1499
1546
  queue.skip();
1500
1547
  else
1501
- this.emit("addSong", queue, song);
1548
+ this.emit("addSong" /* ADD_SONG */, queue, song);
1502
1549
  } else {
1503
1550
  const newQueue = await this.queues.create(voiceChannel, song, textChannel);
1504
1551
  if (newQueue instanceof Queue) {
1505
1552
  if (this.options.emitAddSongWhenCreatingQueue)
1506
- this.emit("addSong", newQueue, song);
1507
- this.emit("playSong", newQueue, song);
1553
+ this.emit("addSong" /* ADD_SONG */, newQueue, song);
1554
+ this.emit("playSong" /* PLAY_SONG */, newQueue, song);
1508
1555
  }
1509
1556
  }
1510
1557
  }
1511
1558
  /**
1512
1559
  * Get {@link Song}'s stream info and attach it to the song.
1513
- * @param {Song} song A Song
1560
+ *
1561
+ * @param song - A Song
1514
1562
  */
1515
1563
  async attachStreamInfo(song) {
1516
- const { url, source, formats, streamURL } = song;
1564
+ const { url, source } = song;
1517
1565
  if (source === "youtube") {
1518
- if (!formats || !chooseBestVideoFormat(song)) {
1519
- song._patchYouTube(await this.handler.getYouTubeInfo(url));
1520
- }
1521
- } else if (!streamURL) {
1566
+ song._patchYouTube(await this.handler.getYouTubeInfo(url));
1567
+ } else {
1522
1568
  for (const plugin of [...this.distube.extractorPlugins, ...this.distube.customPlugins]) {
1523
1569
  if (await plugin.validate(url)) {
1524
1570
  const info = [plugin.getStreamURL(url), plugin.getRelatedSongs(url)];
@@ -1536,10 +1582,11 @@ __name(_DisTubeHandler, "DisTubeHandler");
1536
1582
  var DisTubeHandler = _DisTubeHandler;
1537
1583
 
1538
1584
  // src/core/DisTubeOptions.ts
1539
- var _validateOptions, validateOptions_fn;
1585
+ var _validateOptions, validateOptions_fn, _ffmpegOption, ffmpegOption_fn;
1540
1586
  var _Options = class _Options {
1541
1587
  constructor(options) {
1542
1588
  __privateAdd(this, _validateOptions);
1589
+ __privateAdd(this, _ffmpegOption);
1543
1590
  __publicField(this, "plugins");
1544
1591
  __publicField(this, "emitNewSongOnly");
1545
1592
  __publicField(this, "leaveOnFinish");
@@ -1558,6 +1605,11 @@ var _Options = class _Options {
1558
1605
  __publicField(this, "joinNewVoiceChannel");
1559
1606
  __publicField(this, "streamType");
1560
1607
  __publicField(this, "directLink");
1608
+ /** @deprecated */
1609
+ __publicField(this, "ffmpegPath");
1610
+ /** @deprecated */
1611
+ __publicField(this, "ffmpegDefaultArgs");
1612
+ __publicField(this, "ffmpeg");
1561
1613
  if (typeof options !== "object" || Array.isArray(options)) {
1562
1614
  throw new DisTubeError("INVALID_TYPE", "object", options, "DisTubeOptions");
1563
1615
  }
@@ -1580,6 +1632,7 @@ var _Options = class _Options {
1580
1632
  this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
1581
1633
  this.streamType = opts.streamType;
1582
1634
  this.directLink = opts.directLink;
1635
+ this.ffmpeg = __privateMethod(this, _ffmpegOption, ffmpegOption_fn).call(this, options);
1583
1636
  checkInvalidKey(opts, this, "DisTubeOptions");
1584
1637
  __privateMethod(this, _validateOptions, validateOptions_fn).call(this);
1585
1638
  }
@@ -1600,7 +1653,7 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1600
1653
  ]);
1601
1654
  const numberOptions = /* @__PURE__ */ new Set(["searchCooldown", "emptyCooldown", "searchSongs"]);
1602
1655
  const stringOptions = /* @__PURE__ */ new Set();
1603
- const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions"]);
1656
+ const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpeg"]);
1604
1657
  const optionalOptions = /* @__PURE__ */ new Set(["youtubeCookie", "customFilters"]);
1605
1658
  for (const [key, value] of Object.entries(options)) {
1606
1659
  if (value === void 0 && optionalOptions.has(key))
@@ -1630,6 +1683,29 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1630
1683
  }
1631
1684
  }
1632
1685
  }, "#validateOptions");
1686
+ _ffmpegOption = new WeakSet();
1687
+ ffmpegOption_fn = /* @__PURE__ */ __name(function(opts) {
1688
+ let path;
1689
+ const args = { global: {}, input: {}, output: {} };
1690
+ if (opts.ffmpegPath) {
1691
+ console.warn("`DisTubeOptions.ffmpegPath` is deprecated. Use `ffmpeg.path` instead.");
1692
+ path = opts.ffmpegPath;
1693
+ }
1694
+ if (opts.ffmpegDefaultArgs) {
1695
+ console.warn("`DisTubeOptions.ffmpegDefaultArgs` is deprecated. Use `ffmpeg.args` instead.");
1696
+ args.global = opts.ffmpegDefaultArgs;
1697
+ }
1698
+ path ??= opts.ffmpeg?.path ?? `ffmpeg${process.platform === "win32" ? ".exe" : ""}`;
1699
+ if (opts.ffmpeg?.args) {
1700
+ if (opts.ffmpeg.args.global)
1701
+ args.global = opts.ffmpeg.args.global;
1702
+ if (opts.ffmpeg.args.input)
1703
+ args.input = opts.ffmpeg.args.input;
1704
+ if (opts.ffmpeg.args.output)
1705
+ args.output = opts.ffmpeg.args.output;
1706
+ }
1707
+ return { path, args };
1708
+ }, "#ffmpegOption");
1633
1709
  __name(_Options, "Options");
1634
1710
  var Options = _Options;
1635
1711
 
@@ -1640,14 +1716,11 @@ var _BaseManager = class _BaseManager extends DisTubeBase {
1640
1716
  super(...arguments);
1641
1717
  /**
1642
1718
  * The collection of items for this manager.
1643
- * @type {Collection}
1644
- * @name BaseManager#collection
1645
1719
  */
1646
1720
  __publicField(this, "collection", new import_discord2.Collection());
1647
1721
  }
1648
1722
  /**
1649
1723
  * The size of the collection.
1650
- * @type {number}
1651
1724
  */
1652
1725
  get size() {
1653
1726
  return this.collection.size;
@@ -1663,7 +1736,8 @@ var _GuildIdManager = class _GuildIdManager extends BaseManager {
1663
1736
  const existing = this.get(id);
1664
1737
  if (existing)
1665
1738
  return this;
1666
- return this.collection.set(id, data);
1739
+ this.collection.set(id, data);
1740
+ return this;
1667
1741
  }
1668
1742
  get(idOrInstance) {
1669
1743
  return this.collection.get(resolveGuildId(idOrInstance));
@@ -1683,21 +1757,16 @@ var import_voice3 = require("@discordjs/voice");
1683
1757
  var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
1684
1758
  /**
1685
1759
  * Get a {@link DisTubeVoice}.
1686
- * @method get
1687
- * @memberof DisTubeVoiceManager#
1688
- * @param {GuildIdResolvable} guild The queue resolvable to resolve
1689
- * @returns {DisTubeVoice?}
1760
+ *
1761
+ * @param guild - The queue resolvable to resolve
1690
1762
  */
1691
1763
  /**
1692
1764
  * Collection of {@link DisTubeVoice}.
1693
- * @name DisTubeVoiceManager#collection
1694
- * @type {Discord.Collection<string, DisTubeVoice>}
1695
1765
  */
1696
1766
  /**
1697
1767
  * Create a {@link DisTubeVoice}
1698
- * @param {Discord.BaseGuildVoiceChannel} channel A voice channel to join
1699
- * @returns {DisTubeVoice}
1700
- * @private
1768
+ *
1769
+ * @param channel - A voice channel to join
1701
1770
  */
1702
1771
  create(channel) {
1703
1772
  const existing = this.get(channel.guildId);
@@ -1709,8 +1778,8 @@ var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
1709
1778
  }
1710
1779
  /**
1711
1780
  * Join a voice channel
1712
- * @param {Discord.BaseGuildVoiceChannel} channel A voice channel to join
1713
- * @returns {Promise<DisTubeVoice>}
1781
+ *
1782
+ * @param channel - A voice channel to join
1714
1783
  */
1715
1784
  join(channel) {
1716
1785
  const existing = this.get(channel.guildId);
@@ -1720,7 +1789,8 @@ var _DisTubeVoiceManager = class _DisTubeVoiceManager extends GuildIdManager {
1720
1789
  }
1721
1790
  /**
1722
1791
  * Leave the connected voice channel in a guild
1723
- * @param {GuildIdResolvable} guild Queue Resolvable
1792
+ *
1793
+ * @param guild - Queue Resolvable
1724
1794
  */
1725
1795
  leave(guild) {
1726
1796
  const voice = this.get(guild);
@@ -1747,17 +1817,15 @@ var _FilterManager = class _FilterManager extends BaseManager {
1747
1817
  __privateAdd(this, _removeFn);
1748
1818
  /**
1749
1819
  * Collection of {@link Filter}.
1750
- * @name FilterManager#collection
1751
- * @type {Discord.Collection<string, DisTubeVoice>}
1752
1820
  */
1753
1821
  __publicField(this, "queue");
1754
1822
  this.queue = queue;
1755
1823
  }
1756
1824
  /**
1757
1825
  * Enable a filter or multiple filters to the manager
1758
- * @param {FilterResolvable|FilterResolvable[]} filterOrFilters The filter or filters to enable
1759
- * @param {boolean} [override=false] Wether or not override the applied filter with new filter value
1760
- * @returns {FilterManager}
1826
+ *
1827
+ * @param filterOrFilters - The filter or filters to enable
1828
+ * @param override - Wether or not override the applied filter with new filter value
1761
1829
  */
1762
1830
  add(filterOrFilters, override = false) {
1763
1831
  if (Array.isArray(filterOrFilters)) {
@@ -1776,15 +1844,14 @@ var _FilterManager = class _FilterManager extends BaseManager {
1776
1844
  }
1777
1845
  /**
1778
1846
  * Clear enabled filters of the manager
1779
- * @returns {FilterManager}
1780
1847
  */
1781
1848
  clear() {
1782
1849
  return this.set([]);
1783
1850
  }
1784
1851
  /**
1785
1852
  * Set the filters applied to the manager
1786
- * @param {FilterResolvable[]} filters The filters to apply
1787
- * @returns {FilterManager}
1853
+ *
1854
+ * @param filters - The filters to apply
1788
1855
  */
1789
1856
  set(filters) {
1790
1857
  if (!Array.isArray(filters))
@@ -1799,8 +1866,8 @@ var _FilterManager = class _FilterManager extends BaseManager {
1799
1866
  }
1800
1867
  /**
1801
1868
  * Disable a filter or multiple filters
1802
- * @param {FilterResolvable|FilterResolvable[]} filterOrFilters The filter or filters to disable
1803
- * @returns {FilterManager}
1869
+ *
1870
+ * @param filterOrFilters - The filter or filters to disable
1804
1871
  */
1805
1872
  remove(filterOrFilters) {
1806
1873
  if (Array.isArray(filterOrFilters))
@@ -1812,30 +1879,26 @@ var _FilterManager = class _FilterManager extends BaseManager {
1812
1879
  }
1813
1880
  /**
1814
1881
  * Check whether a filter enabled or not
1815
- * @param {FilterResolvable} filter The filter to check
1816
- * @returns {boolean}
1882
+ *
1883
+ * @param filter - The filter to check
1817
1884
  */
1818
1885
  has(filter) {
1819
1886
  return this.collection.has(typeof filter === "string" ? filter : __privateMethod(this, _resolve, resolve_fn).call(this, filter).name);
1820
1887
  }
1821
1888
  /**
1822
1889
  * Array of enabled filter names
1823
- * @type {Array<string>}
1824
- * @readonly
1825
1890
  */
1826
1891
  get names() {
1827
1892
  return [...this.collection.keys()];
1828
1893
  }
1829
1894
  /**
1830
1895
  * Array of enabled filters
1831
- * @type {Array<Filter>}
1832
- * @readonly
1833
1896
  */
1834
1897
  get values() {
1835
1898
  return [...this.collection.values()];
1836
1899
  }
1837
1900
  get ffmpegArgs() {
1838
- return this.size ? ["-af", this.values.map((f) => f.value).join(",")] : [];
1901
+ return this.size ? { af: this.values.map((f) => f.value).join(",") } : {};
1839
1902
  }
1840
1903
  toString() {
1841
1904
  return this.names.toString();
@@ -1873,51 +1936,46 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1873
1936
  super(...arguments);
1874
1937
  /**
1875
1938
  * Get a Queue from this QueueManager.
1876
- * @method get
1877
- * @memberof QueueManager#
1878
- * @param {GuildIdResolvable} guild Resolvable thing from a guild
1879
- * @returns {Queue?}
1939
+ *
1940
+ * @param guild - Resolvable thing from a guild
1880
1941
  */
1881
1942
  /**
1882
1943
  * Listen to DisTubeVoice events and handle the Queue
1883
- * @private
1884
- * @param {Queue} queue Queue
1944
+ *
1945
+ * @param queue - Queue
1885
1946
  */
1886
1947
  __privateAdd(this, _voiceEventHandler);
1887
1948
  /**
1888
1949
  * Whether or not emit playSong event
1889
- * @param {Queue} queue Queue
1890
- * @private
1891
- * @returns {boolean}
1950
+ *
1951
+ * @param queue - Queue
1892
1952
  */
1893
1953
  __privateAdd(this, _emitPlaySong);
1894
1954
  /**
1895
1955
  * Handle the queue when a Song finish
1896
- * @private
1897
- * @param {Queue} queue queue
1898
- * @returns {Promise<void>}
1956
+ *
1957
+ * @param queue - queue
1899
1958
  */
1900
1959
  __privateAdd(this, _handleSongFinish);
1901
1960
  /**
1902
1961
  * Handle error while playing
1903
- * @private
1904
- * @param {Queue} queue queue
1905
- * @param {Error} error error
1962
+ *
1963
+ * @param queue - queue
1964
+ * @param error - error
1906
1965
  */
1907
1966
  __privateAdd(this, _handlePlayingError);
1908
1967
  }
1909
1968
  /**
1910
1969
  * Collection of {@link Queue}.
1911
- * @name QueueManager#collection
1912
- * @type {Discord.Collection<string, Queue>}
1913
1970
  */
1914
1971
  /**
1915
1972
  * Create a {@link Queue}
1916
- * @private
1917
- * @param {Discord.BaseGuildVoiceChannel} channel A voice channel
1918
- * @param {Song|Song[]} song First song
1919
- * @param {Discord.BaseGuildTextChannel} textChannel Default text channel
1920
- * @returns {Promise<Queue|true>} Returns `true` if encounter an error
1973
+ *
1974
+ * @param channel - A voice channel
1975
+ * @param song - First song
1976
+ * @param textChannel - Default text channel
1977
+ *
1978
+ * @returns Returns `true` if encounter an error
1921
1979
  */
1922
1980
  async create(channel, song, textChannel) {
1923
1981
  if (this.has(channel.guildId))
@@ -1926,10 +1984,11 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1926
1984
  const queue = new Queue(this.distube, voice, song, textChannel);
1927
1985
  await queue._taskQueue.queuing();
1928
1986
  try {
1987
+ checkFFmpeg(this.distube);
1929
1988
  await voice.join();
1930
1989
  __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1931
1990
  this.add(queue.id, queue);
1932
- this.emit("initQueue", queue);
1991
+ this.emit("initQueue" /* INIT_QUEUE */, queue);
1933
1992
  const err = await this.playSong(queue);
1934
1993
  return err || queue;
1935
1994
  } finally {
@@ -1938,26 +1997,36 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1938
1997
  }
1939
1998
  /**
1940
1999
  * Create a ytdl stream
1941
- * @param {Queue} queue Queue
1942
- * @returns {DisTubeStream}
2000
+ *
2001
+ * @param queue - Queue
1943
2002
  */
1944
2003
  createStream(queue) {
1945
2004
  const song = queue.songs[0];
1946
2005
  const { duration, source, streamURL } = song;
1947
2006
  const streamOptions = {
1948
- ffmpegArgs: queue.filters.ffmpegArgs,
2007
+ ffmpeg: {
2008
+ path: this.options.ffmpeg.path,
2009
+ args: {
2010
+ global: { ...this.options.ffmpeg.args.global },
2011
+ input: { ...this.options.ffmpeg.args.input },
2012
+ output: { ...this.options.ffmpeg.args.output, ...queue.filters.ffmpegArgs }
2013
+ }
2014
+ },
1949
2015
  seek: duration ? queue.beginTime : void 0,
1950
2016
  type: this.options.streamType
1951
2017
  };
1952
2018
  if (source === "youtube")
1953
2019
  return DisTubeStream.YouTube(song, streamOptions);
2020
+ if (!streamURL)
2021
+ throw new Error("No streamURL, something went wrong");
1954
2022
  return DisTubeStream.DirectLink(streamURL, streamOptions);
1955
2023
  }
1956
2024
  /**
1957
2025
  * Play a song on voice connection
1958
- * @private
1959
- * @param {Queue} queue The guild queue
1960
- * @returns {Promise<boolean>} error?
2026
+ *
2027
+ * @param queue - The guild queue
2028
+ *
2029
+ * @returns error?
1961
2030
  */
1962
2031
  async playSong(queue) {
1963
2032
  if (!queue)
@@ -1974,6 +2043,7 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1974
2043
  return true;
1975
2044
  }
1976
2045
  const stream = this.createStream(queue);
2046
+ stream.on("debug", (data) => this.emit("ffmpegDebug" /* FFMPEG_DEBUG */, `[${queue.id}]: ${data}`));
1977
2047
  queue.voice.play(stream);
1978
2048
  song.streamURL = stream.url;
1979
2049
  return false;
@@ -1988,7 +2058,7 @@ voiceEventHandler_fn = /* @__PURE__ */ __name(function(queue) {
1988
2058
  queue._listeners = {
1989
2059
  disconnect: (error) => {
1990
2060
  queue.remove();
1991
- this.emit("disconnect", queue);
2061
+ this.emit("disconnect" /* DISCONNECT */, queue);
1992
2062
  if (error)
1993
2063
  this.emitError(error, queue.textChannel);
1994
2064
  },
@@ -2005,7 +2075,7 @@ emitPlaySong_fn = /* @__PURE__ */ __name(function(queue) {
2005
2075
  }, "#emitPlaySong");
2006
2076
  _handleSongFinish = new WeakSet();
2007
2077
  handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
2008
- this.emit("finishSong", queue, queue.songs[0]);
2078
+ this.emit("finishSong" /* FINISH_SONG */, queue, queue.songs[0]);
2009
2079
  await queue._taskQueue.queuing();
2010
2080
  try {
2011
2081
  if (queue.stopped)
@@ -2023,14 +2093,14 @@ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
2023
2093
  try {
2024
2094
  await queue.addRelatedSong();
2025
2095
  } catch {
2026
- this.emit("noRelated", queue);
2096
+ this.emit("noRelated" /* NO_RELATED */, queue);
2027
2097
  }
2028
2098
  }
2029
2099
  if (queue.songs.length <= 1) {
2030
2100
  if (this.options.leaveOnFinish)
2031
2101
  queue.voice.leave();
2032
2102
  if (!queue.autoplay)
2033
- this.emit("finish", queue);
2103
+ this.emit("finish" /* FINISH */, queue);
2034
2104
  queue.remove();
2035
2105
  return;
2036
2106
  }
@@ -2049,7 +2119,7 @@ handleSongFinish_fn = /* @__PURE__ */ __name(async function(queue) {
2049
2119
  queue.beginTime = 0;
2050
2120
  const err = await this.playSong(queue);
2051
2121
  if (!err && emitPlaySong)
2052
- this.emit("playSong", queue, queue.songs[0]);
2122
+ this.emit("playSong" /* PLAY_SONG */, queue, queue.songs[0]);
2053
2123
  } finally {
2054
2124
  queue._taskQueue.resolve();
2055
2125
  }
@@ -2070,7 +2140,7 @@ Name: ${song.name}`;
2070
2140
  queue.beginTime = 0;
2071
2141
  this.playSong(queue).then((e) => {
2072
2142
  if (!e)
2073
- this.emit("playSong", queue, queue.songs[0]);
2143
+ this.emit("playSong" /* PLAY_SONG */, queue, queue.songs[0]);
2074
2144
  });
2075
2145
  } else {
2076
2146
  queue.stop();
@@ -2084,10 +2154,11 @@ var _filters;
2084
2154
  var _Queue = class _Queue extends DisTubeBase {
2085
2155
  /**
2086
2156
  * Create a queue for the guild
2087
- * @param {DisTube} distube DisTube
2088
- * @param {DisTubeVoice} voice Voice connection
2089
- * @param {Song|Song[]} song First song(s)
2090
- * @param {Discord.BaseGuildTextChannel?} textChannel Default text channel
2157
+ *
2158
+ * @param distube - DisTube
2159
+ * @param voice - Voice connection
2160
+ * @param song - First song(s)
2161
+ * @param textChannel - Default text channel
2091
2162
  */
2092
2163
  constructor(distube, voice, song, textChannel) {
2093
2164
  super(distube);
@@ -2129,55 +2200,42 @@ var _Queue = class _Queue extends DisTubeBase {
2129
2200
  }
2130
2201
  /**
2131
2202
  * The client user as a `GuildMember` of this queue's guild
2132
- * @type {Discord.GuildMember?}
2133
2203
  */
2134
2204
  get clientMember() {
2135
2205
  return this.voice.channel.guild.members.me ?? void 0;
2136
2206
  }
2137
2207
  /**
2138
2208
  * The filter manager of the queue
2139
- * @type {FilterManager}
2140
- * @readonly
2141
2209
  */
2142
2210
  get filters() {
2143
2211
  return __privateGet(this, _filters);
2144
2212
  }
2145
2213
  /**
2146
2214
  * Formatted duration string.
2147
- * @type {string}
2148
- * @readonly
2149
2215
  */
2150
2216
  get formattedDuration() {
2151
2217
  return formatDuration(this.duration);
2152
2218
  }
2153
2219
  /**
2154
2220
  * Queue's duration.
2155
- * @type {number}
2156
- * @readonly
2157
2221
  */
2158
2222
  get duration() {
2159
2223
  return this.songs.length ? this.songs.reduce((prev, next) => prev + next.duration, 0) : 0;
2160
2224
  }
2161
2225
  /**
2162
2226
  * What time in the song is playing (in seconds).
2163
- * @type {number}
2164
- * @readonly
2165
2227
  */
2166
2228
  get currentTime() {
2167
2229
  return this.voice.playbackDuration + this.beginTime;
2168
2230
  }
2169
2231
  /**
2170
2232
  * Formatted {@link Queue#currentTime} string.
2171
- * @type {string}
2172
- * @readonly
2173
2233
  */
2174
2234
  get formattedCurrentTime() {
2175
2235
  return formatDuration(this.currentTime);
2176
2236
  }
2177
2237
  /**
2178
2238
  * The voice channel playing in.
2179
- * @type {Discord.VoiceChannel|Discord.StageChannel|null}
2180
- * @readonly
2181
2239
  */
2182
2240
  get voiceChannel() {
2183
2241
  return this.clientMember?.voice?.channel ?? null;
@@ -2189,12 +2247,12 @@ var _Queue = class _Queue extends DisTubeBase {
2189
2247
  this.voice.volume = value;
2190
2248
  }
2191
2249
  /**
2192
- * @private
2193
- * Add a Song or an array of Song to the queue
2194
- * @param {Song|Song[]} song Song to add
2195
- * @param {number} [position=0] Position to add, <= 0 to add to the end of the queue
2196
- * @throws {Error}
2197
- * @returns {Queue} The guild queue
2250
+ * @throws {DisTubeError}
2251
+ *
2252
+ * @param song - Song to add
2253
+ * @param position - Position to add, \<= 0 to add to the end of the queue
2254
+ *
2255
+ * @returns The guild queue
2198
2256
  */
2199
2257
  addToQueue(song, position = 0) {
2200
2258
  if (!song || Array.isArray(song) && !song.length) {
@@ -2221,7 +2279,8 @@ var _Queue = class _Queue extends DisTubeBase {
2221
2279
  }
2222
2280
  /**
2223
2281
  * Pause the guild stream
2224
- * @returns {Queue} The guild queue
2282
+ *
2283
+ * @returns The guild queue
2225
2284
  */
2226
2285
  pause() {
2227
2286
  if (this.paused)
@@ -2233,7 +2292,8 @@ var _Queue = class _Queue extends DisTubeBase {
2233
2292
  }
2234
2293
  /**
2235
2294
  * Resume the guild stream
2236
- * @returns {Queue} The guild queue
2295
+ *
2296
+ * @returns The guild queue
2237
2297
  */
2238
2298
  resume() {
2239
2299
  if (this.playing)
@@ -2245,19 +2305,21 @@ var _Queue = class _Queue extends DisTubeBase {
2245
2305
  }
2246
2306
  /**
2247
2307
  * Set the guild stream's volume
2248
- * @param {number} percent The percentage of volume you want to set
2249
- * @returns {Queue} The guild queue
2308
+ *
2309
+ * @param percent - The percentage of volume you want to set
2310
+ *
2311
+ * @returns The guild queue
2250
2312
  */
2251
2313
  setVolume(percent) {
2252
2314
  this.volume = percent;
2253
2315
  return this;
2254
2316
  }
2255
2317
  /**
2256
- * Skip the playing song if there is a next song in the queue.
2257
- * <info>If {@link Queue#autoplay} is `true` and there is no up next song,
2258
- * DisTube will add and play a related song.</info>
2259
- * @returns {Promise<Song>} The song will skip to
2260
- * @throws {Error}
2318
+ * Skip the playing song if there is a next song in the queue. <info>If {@link
2319
+ * Queue#autoplay} is `true` and there is no up next song, DisTube will add and
2320
+ * play a related song.</info>
2321
+ *
2322
+ * @returns The song will skip to
2261
2323
  */
2262
2324
  async skip() {
2263
2325
  await this._taskQueue.queuing();
@@ -2278,8 +2340,8 @@ var _Queue = class _Queue extends DisTubeBase {
2278
2340
  }
2279
2341
  /**
2280
2342
  * Play the previous song if exists
2281
- * @returns {Promise<Song>} The guild queue
2282
- * @throws {Error}
2343
+ *
2344
+ * @returns The guild queue
2283
2345
  */
2284
2346
  async previous() {
2285
2347
  await this._taskQueue.queuing();
@@ -2299,7 +2361,8 @@ var _Queue = class _Queue extends DisTubeBase {
2299
2361
  }
2300
2362
  /**
2301
2363
  * Shuffle the queue's songs
2302
- * @returns {Promise<Queue>} The guild queue
2364
+ *
2365
+ * @returns The guild queue
2303
2366
  */
2304
2367
  async shuffle() {
2305
2368
  await this._taskQueue.queuing();
@@ -2318,12 +2381,13 @@ var _Queue = class _Queue extends DisTubeBase {
2318
2381
  }
2319
2382
  }
2320
2383
  /**
2321
- * Jump to the song position in the queue.
2322
- * The next one is 1, 2,...
2323
- * The previous one is -1, -2,...
2324
- * @param {number} position The song position to play
2325
- * @returns {Promise<Song>} The new Song will be played
2326
- * @throws {Error} if `num` is invalid number
2384
+ * Jump to the song position in the queue. The next one is 1, 2,... The previous
2385
+ * one is -1, -2,...
2386
+ * if `num` is invalid number
2387
+ *
2388
+ * @param position - The song position to play
2389
+ *
2390
+ * @returns The new Song will be played
2327
2391
  */
2328
2392
  async jump(position) {
2329
2393
  await this._taskQueue.queuing();
@@ -2359,10 +2423,12 @@ var _Queue = class _Queue extends DisTubeBase {
2359
2423
  }
2360
2424
  }
2361
2425
  /**
2362
- * Set the repeat mode of the guild queue.\
2426
+ * Set the repeat mode of the guild queue.
2363
2427
  * Toggle mode `(Disabled -> Song -> Queue -> Disabled ->...)` if `mode` is `undefined`
2364
- * @param {RepeatMode?} [mode] The repeat modes (toggle if `undefined`)
2365
- * @returns {RepeatMode} The new repeat mode
2428
+ *
2429
+ * @param mode - The repeat modes (toggle if `undefined`)
2430
+ *
2431
+ * @returns The new repeat mode
2366
2432
  */
2367
2433
  setRepeatMode(mode) {
2368
2434
  if (mode !== void 0 && !Object.values(RepeatMode).includes(mode)) {
@@ -2378,8 +2444,10 @@ var _Queue = class _Queue extends DisTubeBase {
2378
2444
  }
2379
2445
  /**
2380
2446
  * Set the playing time to another position
2381
- * @param {number} time Time in seconds
2382
- * @returns {Queue} The guild queue
2447
+ *
2448
+ * @param time - Time in seconds
2449
+ *
2450
+ * @returns The guild queue
2383
2451
  */
2384
2452
  seek(time) {
2385
2453
  if (typeof time !== "number")
@@ -2392,8 +2460,8 @@ var _Queue = class _Queue extends DisTubeBase {
2392
2460
  }
2393
2461
  /**
2394
2462
  * Add a related song of the playing song to the queue
2395
- * @returns {Promise<Song>} The added song
2396
- * @throws {Error}
2463
+ *
2464
+ * @returns The added song
2397
2465
  */
2398
2466
  async addRelatedSong() {
2399
2467
  if (!this.songs?.[0])
@@ -2426,9 +2494,8 @@ var _Queue = class _Queue extends DisTubeBase {
2426
2494
  }
2427
2495
  }
2428
2496
  /**
2429
- * Remove the queue from the manager
2430
- * (This does not leave the voice channel even if {@link DisTubeOptions|DisTubeOptions.leaveOnStop} is enabled)
2431
- * @private
2497
+ * Remove the queue from the manager (This does not leave the voice channel even if
2498
+ * {@link DisTubeOptions | DisTubeOptions.leaveOnStop} is enabled)
2432
2499
  */
2433
2500
  remove() {
2434
2501
  this.stopped = true;
@@ -2440,11 +2507,12 @@ var _Queue = class _Queue extends DisTubeBase {
2440
2507
  }
2441
2508
  }
2442
2509
  this.queues.remove(this.id);
2443
- this.emit("deleteQueue", this);
2510
+ this.emit("deleteQueue" /* DELETE_QUEUE */, this);
2444
2511
  }
2445
2512
  /**
2446
2513
  * Toggle autoplay mode
2447
- * @returns {boolean} Autoplay mode state
2514
+ *
2515
+ * @returns Autoplay mode state
2448
2516
  */
2449
2517
  toggleAutoplay() {
2450
2518
  this.autoplay = !this.autoplay;
@@ -2465,70 +2533,59 @@ var _Plugin = class _Plugin {
2465
2533
  }
2466
2534
  /**
2467
2535
  * Type of the plugin
2468
- * @name Plugin#type
2469
- * @type {PluginType}
2470
2536
  */
2471
2537
  /**
2472
2538
  * Emit an event to the {@link DisTube} class
2473
- * @param {string} eventName Event name
2474
- * @param {...any} args arguments
2475
- * @returns {boolean}
2539
+ *
2540
+ * @param eventName - Event name
2541
+ * @param args - arguments
2476
2542
  */
2477
2543
  emit(eventName, ...args) {
2478
2544
  return this.distube.emit(eventName, ...args);
2479
2545
  }
2480
2546
  /**
2481
2547
  * Emit error event to the {@link DisTube} class
2482
- * @param {Error} error error
2483
- * @param {Discord.BaseGuildTextChannel} [channel] Text channel where the error is encountered.
2548
+ *
2549
+ * @param error - error
2550
+ * @param channel - Text channel where the error is encountered.
2484
2551
  */
2485
2552
  emitError(error, channel) {
2486
2553
  this.distube.emitError(error, channel);
2487
2554
  }
2488
2555
  /**
2489
2556
  * The queue manager
2490
- * @type {QueueManager}
2491
- * @readonly
2492
2557
  */
2493
2558
  get queues() {
2494
2559
  return this.distube.queues;
2495
2560
  }
2496
2561
  /**
2497
2562
  * The voice manager
2498
- * @type {DisTubeVoiceManager}
2499
- * @readonly
2500
2563
  */
2501
2564
  get voices() {
2502
2565
  return this.distube.voices;
2503
2566
  }
2504
2567
  /**
2505
2568
  * Discord.js client
2506
- * @type {Discord.Client}
2507
- * @readonly
2508
2569
  */
2509
2570
  get client() {
2510
2571
  return this.distube.client;
2511
2572
  }
2512
2573
  /**
2513
2574
  * DisTube options
2514
- * @type {DisTubeOptions}
2515
- * @readonly
2516
2575
  */
2517
2576
  get options() {
2518
2577
  return this.distube.options;
2519
2578
  }
2520
2579
  /**
2521
2580
  * DisTube handler
2522
- * @type {DisTubeHandler}
2523
- * @readonly
2524
2581
  */
2525
2582
  get handler() {
2526
2583
  return this.distube.handler;
2527
2584
  }
2528
2585
  /**
2529
2586
  * Check if the string is working with this plugin
2530
- * @param {string} _string Input string
2531
- * @returns {boolean|Promise<boolean>}
2587
+ *
2588
+ * @param _string - Input string
2532
2589
  */
2533
2590
  validate(_string) {
2534
2591
  return false;
@@ -2536,17 +2593,18 @@ var _Plugin = class _Plugin {
2536
2593
  /**
2537
2594
  * Get the stream url from {@link Song#url}. Returns {@link Song#url} by default.
2538
2595
  * Not needed if the plugin plays song from YouTube.
2539
- * @param {string} url Input url
2540
- * @returns {string|Promise<string>}
2596
+ *
2597
+ * @param url - Input url
2541
2598
  */
2542
2599
  getStreamURL(url) {
2543
2600
  return url;
2544
2601
  }
2545
2602
  /**
2546
- * Get related songs from a supported url. {@link Song#member} should be `undefined`.
2547
- * Not needed to add {@link Song#related} because it will be added with this function later.
2548
- * @param {string} _url Input url
2549
- * @returns {Song[]|Promise<Song[]>}
2603
+ * Get related songs from a supported url. {@link Song#member} should be
2604
+ * `undefined`. Not needed to add {@link Song#related} because it will be added
2605
+ * with this function later.
2606
+ *
2607
+ * @param _url - Input url
2550
2608
  */
2551
2609
  getRelatedSongs(_url) {
2552
2610
  return [];
@@ -2747,15 +2805,9 @@ var _DirectLinkPlugin = class _DirectLinkPlugin extends ExtractorPlugin {
2747
2805
  return false;
2748
2806
  }
2749
2807
  resolve(url, options = {}) {
2750
- url = url.replace(/\/+$/, "");
2751
- return new Song(
2752
- {
2753
- name: url.substring(url.lastIndexOf("/") + 1).replace(/((\?|#).*)?$/, "") || url,
2754
- url,
2755
- src: "direct_link"
2756
- },
2757
- options
2758
- );
2808
+ const u = new URL(url);
2809
+ const name = u.pathname.split("/").pop() || u.href;
2810
+ return new Song({ name, url, src: "direct_link" }, options);
2759
2811
  }
2760
2812
  };
2761
2813
  __name(_DirectLinkPlugin, "DirectLinkPlugin");
@@ -2763,11 +2815,11 @@ var DirectLinkPlugin = _DirectLinkPlugin;
2763
2815
 
2764
2816
  // src/DisTube.ts
2765
2817
  var import_ytsr = __toESM(require("@distube/ytsr"));
2766
- var import_tiny_typed_emitter2 = require("tiny-typed-emitter");
2818
+ var import_tiny_typed_emitter3 = require("tiny-typed-emitter");
2767
2819
  var { version } = require_package();
2768
2820
  var _getQueue, getQueue_fn;
2769
- var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2770
- constructor(client, otp = {}) {
2821
+ var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
2822
+ constructor(client, opts = {}) {
2771
2823
  super();
2772
2824
  __privateAdd(this, _getQueue);
2773
2825
  __publicField(this, "handler");
@@ -2783,7 +2835,7 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2783
2835
  throw new DisTubeError("INVALID_TYPE", "Discord.Client", client, "client");
2784
2836
  this.client = client;
2785
2837
  checkIntents(client.options);
2786
- this.options = new Options(otp);
2838
+ this.options = new Options(opts);
2787
2839
  this.voices = new DisTubeVoiceManager(this);
2788
2840
  this.handler = new DisTubeHandler(this);
2789
2841
  this.queues = new QueueManager(this);
@@ -2799,21 +2851,16 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2799
2851
  }
2800
2852
  /**
2801
2853
  * DisTube version
2802
- * @type {string}
2803
2854
  */
2804
2855
  get version() {
2805
2856
  return version;
2806
2857
  }
2807
2858
  /**
2808
- * Play / add a song or playlist from url. Search and play a song if it is not a valid url.
2859
+ * Play / add a song or playlist from url. Search and play a song if it is not a
2860
+ * valid url.
2809
2861
  *
2810
- * @param {Discord.BaseGuildVoiceChannel} voiceChannel The channel will be joined if the bot isn't in any channels,
2811
- * the bot will be moved to this channel if {@link DisTubeOptions}.joinNewVoiceChannel is `true`
2812
- * @param {string|Song|SearchResult|Playlist} song URL | Search string |
2813
- * {@link Song} | {@link SearchResult} | {@link Playlist}
2814
- * @param {PlayOptions} [options] Optional options
2815
- * @throws {DisTubeError}
2816
2862
  * @example
2863
+ * ```ts
2817
2864
  * client.on('message', (message) => {
2818
2865
  * if (!message.content.startsWith(config.prefix)) return;
2819
2866
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -2825,7 +2872,14 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter2.TypedEmitter {
2825
2872
  * message
2826
2873
  * });
2827
2874
  * });
2828
- * @returns {Promise<void>}
2875
+ * ```ts
2876
+ *
2877
+ * @throws {@link DisTubeError}
2878
+ *
2879
+ * @param voiceChannel - The channel will be joined if the bot isn't in any channels, the bot will be
2880
+ * moved to this channel if {@link DisTubeOptions}.joinNewVoiceChannel is `true`
2881
+ * @param song - URL | Search string | {@link Song} | {@link SearchResult} | {@link Playlist}
2882
+ * @param options - Optional options
2829
2883
  */
2830
2884
  async play(voiceChannel, song, options = {}) {
2831
2885
  if (!isSupportedVoiceChannel(voiceChannel)) {
@@ -2895,10 +2949,9 @@ ${e.message}`;
2895
2949
  }
2896
2950
  /**
2897
2951
  * Create a custom playlist
2898
- * @returns {Promise<Playlist>}
2899
- * @param {Array<string|Song|SearchResult>} songs Array of url, Song or SearchResult
2900
- * @param {CustomPlaylistOptions} [options] Optional options
2952
+ *
2901
2953
  * @example
2954
+ * ```ts
2902
2955
  * const songs = ["https://www.youtube.com/watch?v=xxx", "https://www.youtube.com/watch?v=yyy"];
2903
2956
  * const playlist = await distube.createCustomPlaylist(songs, {
2904
2957
  * member: message.member,
@@ -2906,6 +2959,10 @@ ${e.message}`;
2906
2959
  * parallel: true
2907
2960
  * });
2908
2961
  * distube.play(voiceChannel, playlist, { ... });
2962
+ * ```ts
2963
+ *
2964
+ * @param songs - Array of url, Song or SearchResult
2965
+ * @param options - Optional options
2909
2966
  */
2910
2967
  async createCustomPlaylist(songs, options = {}) {
2911
2968
  const { member, properties, parallel, metadata } = { parallel: true, ...options };
@@ -2926,13 +2983,14 @@ ${e.message}`;
2926
2983
  const promises = filteredSongs.map(
2927
2984
  (song) => this.handler.resolve(song, { member, metadata }).catch(() => void 0)
2928
2985
  );
2929
- resolvedSongs = (await Promise.all(promises)).filter((s) => Boolean(s));
2986
+ resolvedSongs = (await Promise.all(promises)).filter((s) => s instanceof Song);
2930
2987
  } else {
2931
- const resolved = [];
2988
+ resolvedSongs = [];
2932
2989
  for (const song of filteredSongs) {
2933
- resolved.push(await this.handler.resolve(song, { member, metadata }).catch(() => void 0));
2990
+ const resolved = await this.handler.resolve(song, { member, metadata }).catch(() => void 0);
2991
+ if (resolved instanceof Song)
2992
+ resolvedSongs.push(resolved);
2934
2993
  }
2935
- resolvedSongs = resolved.filter((s) => Boolean(s));
2936
2994
  }
2937
2995
  return new Playlist(resolvedSongs, { member, properties, metadata });
2938
2996
  }
@@ -2940,13 +2998,13 @@ ${e.message}`;
2940
2998
  * Search for a song. You can customize how user answers instead of send a number.
2941
2999
  * Then use {@link DisTube#play} to play it.
2942
3000
  *
2943
- * @param {string} string The string search for
2944
- * @param {Object} options Search options
2945
- * @param {number} [options.limit=10] Limit the results
2946
- * @param {SearchResultType} [options.type=SearchResultType.VIDEO] Type of results (`video` or `playlist`).
2947
- * @param {boolean} [options.safeSearch=false] Whether or not use safe search (YouTube restricted mode)
2948
- * @throws {Error}
2949
- * @returns {Promise<Array<SearchResult>>} Array of results
3001
+ * @param string - The string search for
3002
+ * @param options - Search options
3003
+ * @param options.limit - Limit the results
3004
+ * @param options.type - Type of results (`video` or `playlist`).
3005
+ * @param options.safeSearch - Whether or not use safe search (YouTube restricted mode)
3006
+ *
3007
+ * @returns Array of results
2950
3008
  */
2951
3009
  async search(string, options = {}) {
2952
3010
  const opts = { type: "video" /* VIDEO */, limit: 10, safeSearch: false, ...options };
@@ -2979,10 +3037,9 @@ ${e.message}`;
2979
3037
  }
2980
3038
  /**
2981
3039
  * Get the guild queue
2982
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
2983
- * @returns {Queue?}
2984
- * @throws {Error}
3040
+ *
2985
3041
  * @example
3042
+ * ```ts
2986
3043
  * client.on('message', (message) => {
2987
3044
  * if (!message.content.startsWith(config.prefix)) return;
2988
3045
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -2994,34 +3051,38 @@ ${e.message}`;
2994
3051
  * ).join("\n"));
2995
3052
  * }
2996
3053
  * });
3054
+ * ```ts
3055
+ *
3056
+ * @param guild - The type can be resolved to give a {@link Queue}
2997
3057
  */
2998
3058
  getQueue(guild) {
2999
3059
  return this.queues.get(guild);
3000
3060
  }
3001
3061
  /**
3002
3062
  * Pause the guild stream
3003
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3004
- * @returns {Queue} The guild queue
3005
- * @throws {Error}
3063
+ *
3064
+ * @param guild - The type can be resolved to give a {@link Queue}
3065
+ *
3066
+ * @returns The guild queue
3006
3067
  */
3007
3068
  pause(guild) {
3008
3069
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).pause();
3009
3070
  }
3010
3071
  /**
3011
3072
  * Resume the guild stream
3012
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3013
- * @returns {Queue} The guild queue
3014
- * @throws {Error}
3073
+ *
3074
+ * @param guild - The type can be resolved to give a {@link Queue}
3075
+ *
3076
+ * @returns The guild queue
3015
3077
  */
3016
3078
  resume(guild) {
3017
3079
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).resume();
3018
3080
  }
3019
3081
  /**
3020
3082
  * Stop the guild stream
3021
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3022
- * @returns {Promise<void>}
3023
- * @throws {Error}
3083
+ *
3024
3084
  * @example
3085
+ * ```ts
3025
3086
  * client.on('message', (message) => {
3026
3087
  * if (!message.content.startsWith(config.prefix)) return;
3027
3088
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3031,17 +3092,18 @@ ${e.message}`;
3031
3092
  * message.channel.send("Stopped the queue!");
3032
3093
  * }
3033
3094
  * });
3095
+ * ```ts
3096
+ *
3097
+ * @param guild - The type can be resolved to give a {@link Queue}
3034
3098
  */
3035
3099
  stop(guild) {
3036
3100
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).stop();
3037
3101
  }
3038
3102
  /**
3039
3103
  * Set the guild stream's volume
3040
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3041
- * @param {number} percent The percentage of volume you want to set
3042
- * @returns {Queue} The guild queue
3043
- * @throws {Error}
3104
+ *
3044
3105
  * @example
3106
+ * ```ts
3045
3107
  * client.on('message', (message) => {
3046
3108
  * if (!message.content.startsWith(config.prefix)) return;
3047
3109
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3049,18 +3111,23 @@ ${e.message}`;
3049
3111
  * if (command == "volume")
3050
3112
  * distube.setVolume(message, Number(args[0]));
3051
3113
  * });
3114
+ * ```ts
3115
+ *
3116
+ * @param guild - The type can be resolved to give a {@link Queue}
3117
+ * @param percent - The percentage of volume you want to set
3118
+ *
3119
+ * @returns The guild queue
3052
3120
  */
3053
3121
  setVolume(guild, percent) {
3054
3122
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).setVolume(percent);
3055
3123
  }
3056
3124
  /**
3057
- * Skip the playing song if there is a next song in the queue.
3058
- * <info>If {@link Queue#autoplay} is `true` and there is no up next song,
3059
- * DisTube will add and play a related song.</info>
3060
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3061
- * @returns {Promise<Song>} The new Song will be played
3062
- * @throws {Error}
3125
+ * Skip the playing song if there is a next song in the queue. <info>If {@link
3126
+ * Queue#autoplay} is `true` and there is no up next song, DisTube will add and
3127
+ * play a related song.</info>
3128
+ *
3063
3129
  * @example
3130
+ * ```ts
3064
3131
  * client.on('message', (message) => {
3065
3132
  * if (!message.content.startsWith(config.prefix)) return;
3066
3133
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3068,16 +3135,20 @@ ${e.message}`;
3068
3135
  * if (command == "skip")
3069
3136
  * distube.skip(message);
3070
3137
  * });
3138
+ * ```ts
3139
+ *
3140
+ * @param guild - The type can be resolved to give a {@link Queue}
3141
+ *
3142
+ * @returns The new Song will be played
3071
3143
  */
3072
3144
  skip(guild) {
3073
3145
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).skip();
3074
3146
  }
3075
3147
  /**
3076
3148
  * Play the previous song
3077
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3078
- * @returns {Promise<Song>} The new Song will be played
3079
- * @throws {Error}
3149
+ *
3080
3150
  * @example
3151
+ * ```ts
3081
3152
  * client.on('message', (message) => {
3082
3153
  * if (!message.content.startsWith(config.prefix)) return;
3083
3154
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3085,15 +3156,20 @@ ${e.message}`;
3085
3156
  * if (command == "previous")
3086
3157
  * distube.previous(message);
3087
3158
  * });
3159
+ * ```ts
3160
+ *
3161
+ * @param guild - The type can be resolved to give a {@link Queue}
3162
+ *
3163
+ * @returns The new Song will be played
3088
3164
  */
3089
3165
  previous(guild) {
3090
3166
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).previous();
3091
3167
  }
3092
3168
  /**
3093
3169
  * Shuffle the guild queue songs
3094
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3095
- * @returns {Promise<Queue>} The guild queue
3170
+ *
3096
3171
  * @example
3172
+ * ```ts
3097
3173
  * client.on('message', (message) => {
3098
3174
  * if (!message.content.startsWith(config.prefix)) return;
3099
3175
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3101,19 +3177,21 @@ ${e.message}`;
3101
3177
  * if (command == "shuffle")
3102
3178
  * distube.shuffle(message);
3103
3179
  * });
3180
+ * ```ts
3181
+ *
3182
+ * @param guild - The type can be resolved to give a {@link Queue}
3183
+ *
3184
+ * @returns The guild queue
3104
3185
  */
3105
3186
  shuffle(guild) {
3106
3187
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).shuffle();
3107
3188
  }
3108
3189
  /**
3109
- * Jump to the song number in the queue.
3110
- * The next one is 1, 2,...
3111
- * The previous one is -1, -2,...
3112
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3113
- * @param {number} num The song number to play
3114
- * @returns {Promise<Song>} The new Song will be played
3115
- * @throws {Error} if `num` is invalid number (0 < num < {@link Queue#songs}.length)
3190
+ * Jump to the song number in the queue. The next one is 1, 2,... The previous one
3191
+ * is -1, -2,...
3192
+ *
3116
3193
  * @example
3194
+ * ```ts
3117
3195
  * client.on('message', (message) => {
3118
3196
  * if (!message.content.startsWith(config.prefix)) return;
3119
3197
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3122,17 +3200,22 @@ ${e.message}`;
3122
3200
  * distube.jump(message, parseInt(args[0]))
3123
3201
  * .catch(err => message.channel.send("Invalid song number."));
3124
3202
  * });
3203
+ * ```ts
3204
+ *
3205
+ * @param guild - The type can be resolved to give a {@link Queue}
3206
+ * @param num - The song number to play
3207
+ *
3208
+ * @returns The new Song will be played
3125
3209
  */
3126
3210
  jump(guild, num) {
3127
3211
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).jump(num);
3128
3212
  }
3129
3213
  /**
3130
- * Set the repeat mode of the guild queue.\
3214
+ * Set the repeat mode of the guild queue.
3131
3215
  * Toggle mode `(Disabled -> Song -> Queue -> Disabled ->...)` if `mode` is `undefined`
3132
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3133
- * @param {RepeatMode?} [mode] The repeat modes (toggle if `undefined`)
3134
- * @returns {RepeatMode} The new repeat mode
3216
+ *
3135
3217
  * @example
3218
+ * ```ts
3136
3219
  * client.on('message', (message) => {
3137
3220
  * if (!message.content.startsWith(config.prefix)) return;
3138
3221
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3143,7 +3226,9 @@ ${e.message}`;
3143
3226
  * message.channel.send("Set repeat mode to `" + mode + "`");
3144
3227
  * }
3145
3228
  * });
3229
+ * ```ts
3146
3230
  * @example
3231
+ * ```ts
3147
3232
  * const { RepeatMode } = require("distube");
3148
3233
  * let mode;
3149
3234
  * switch(distube.setRepeatMode(message, parseInt(args[0]))) {
@@ -3158,16 +3243,21 @@ ${e.message}`;
3158
3243
  * break;
3159
3244
  * }
3160
3245
  * message.channel.send("Set repeat mode to `" + mode + "`");
3246
+ * ```ts
3247
+ *
3248
+ * @param guild - The type can be resolved to give a {@link Queue}
3249
+ * @param mode - The repeat modes (toggle if `undefined`)
3250
+ *
3251
+ * @returns The new repeat mode
3161
3252
  */
3162
3253
  setRepeatMode(guild, mode) {
3163
3254
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).setRepeatMode(mode);
3164
3255
  }
3165
3256
  /**
3166
3257
  * Toggle autoplay mode
3167
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3168
- * @returns {boolean} Autoplay mode state
3169
- * @throws {Error}
3258
+ *
3170
3259
  * @example
3260
+ * ```ts
3171
3261
  * client.on('message', (message) => {
3172
3262
  * if (!message.content.startsWith(config.prefix)) return;
3173
3263
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3177,6 +3267,11 @@ ${e.message}`;
3177
3267
  * message.channel.send("Set autoplay mode to `" + (mode ? "On" : "Off") + "`");
3178
3268
  * }
3179
3269
  * });
3270
+ * ```ts
3271
+ *
3272
+ * @param guild - The type can be resolved to give a {@link Queue}
3273
+ *
3274
+ * @returns Autoplay mode state
3180
3275
  */
3181
3276
  toggleAutoplay(guild) {
3182
3277
  const queue = __privateMethod(this, _getQueue, getQueue_fn).call(this, guild);
@@ -3185,18 +3280,19 @@ ${e.message}`;
3185
3280
  }
3186
3281
  /**
3187
3282
  * Add related song to the queue
3188
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3189
- * @returns {Promise<Song>} The guild queue
3283
+ *
3284
+ * @param guild - The type can be resolved to give a {@link Queue}
3285
+ *
3286
+ * @returns The guild queue
3190
3287
  */
3191
3288
  addRelatedSong(guild) {
3192
3289
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).addRelatedSong();
3193
3290
  }
3194
3291
  /**
3195
3292
  * Set the playing time to another position
3196
- * @param {GuildIdResolvable} guild The type can be resolved to give a {@link Queue}
3197
- * @param {number} time Time in seconds
3198
- * @returns {Queue} Seeked queue
3293
+ *
3199
3294
  * @example
3295
+ * ```ts
3200
3296
  * client.on('message', message => {
3201
3297
  * if (!message.content.startsWith(config.prefix)) return;
3202
3298
  * const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
@@ -3204,24 +3300,30 @@ ${e.message}`;
3204
3300
  * if (command = 'seek')
3205
3301
  * distube.seek(message, Number(args[0]));
3206
3302
  * });
3303
+ * ```ts
3304
+ *
3305
+ * @param guild - The type can be resolved to give a {@link Queue}
3306
+ * @param time - Time in seconds
3307
+ *
3308
+ * @returns Seeked queue
3207
3309
  */
3208
3310
  seek(guild, time) {
3209
3311
  return __privateMethod(this, _getQueue, getQueue_fn).call(this, guild).seek(time);
3210
3312
  }
3211
3313
  /**
3212
3314
  * Emit error event
3213
- * @param {Error} error error
3214
- * @param {Discord.BaseGuildTextChannel} [channel] Text channel where the error is encountered.
3215
- * @private
3315
+ *
3316
+ * @param error - error
3317
+ * @param channel - Text channel where the error is encountered.
3216
3318
  */
3217
3319
  emitError(error, channel) {
3218
- if (this.listeners("error").length) {
3219
- this.emit("error", channel, error);
3320
+ if (this.listeners("error" /* ERROR */).length) {
3321
+ this.emit("error" /* ERROR */, channel, error);
3220
3322
  } else {
3221
3323
  console.error(error);
3222
3324
  console.warn("Unhandled 'error' event.");
3223
3325
  console.warn(
3224
- "See: https://distube.js.org/#/docs/DisTube/stable/class/DisTube?scrollTo=e-error and https://nodejs.org/api/events.html#events_error_events"
3326
+ "See: https://distube.js.org/classes/DisTube.html#error and https://nodejs.org/api/events.html#events_error_events"
3225
3327
  );
3226
3328
  }
3227
3329
  }
@@ -3264,6 +3366,7 @@ var DisTube = _DisTube;
3264
3366
  Song,
3265
3367
  StreamType,
3266
3368
  TaskQueue,
3369
+ checkFFmpeg,
3267
3370
  checkIntents,
3268
3371
  checkInvalidKey,
3269
3372
  chooseBestVideoFormat,