distube 4.2.0 → 4.2.2

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/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  # DisTube
21
21
 
22
- A Discord.js module to simplify your music commands and play songs with audio filters on Discord without any API key.
22
+ A powerful Discord.js module for simplifying music commands and effortless playback of various sources with integrated audio filters.
23
23
 
24
24
  [DisTube Support Server](https://discord.gg/feaDd9h) - [Frequently Asked Questions](https://discord.gg/feaDd9h)
25
25
 
package/dist/index.d.ts CHANGED
@@ -127,7 +127,7 @@ type DisTubeOptions = {
127
127
  searchCooldown?: number;
128
128
  /**
129
129
  * YouTube cookies. Guide: {@link
130
- * https://distube.js.org/#/docs/DisTube/main/general/cookie | YouTube Cookies}
130
+ * https://github.com/skick1234/DisTube/wiki/YouTube-Cookies | YouTube Cookies}
131
131
  */
132
132
  youtubeCookie?: Cookie[] | string;
133
133
  /**
@@ -167,12 +167,31 @@ type DisTubeOptions = {
167
167
  directLink?: boolean;
168
168
  /**
169
169
  * FFmpeg path
170
+ * @deprecated
170
171
  */
171
172
  ffmpegPath?: string;
172
173
  /**
173
174
  * FFmpeg default arguments
174
- */
175
- ffmpegDefaultArgs?: FFmpegOptions;
175
+ * @deprecated
176
+ */
177
+ ffmpegDefaultArgs?: FFmpegArgs;
178
+ /**
179
+ * FFmpeg options
180
+ */
181
+ ffmpeg?: {
182
+ /**
183
+ * FFmpeg path
184
+ */
185
+ path?: string;
186
+ /**
187
+ * FFmpeg default arguments
188
+ */
189
+ args?: {
190
+ global?: FFmpegArgs;
191
+ input?: FFmpegArgs;
192
+ output?: FFmpegArgs;
193
+ };
194
+ };
176
195
  };
177
196
  /**
178
197
  * Data that can be resolved to give a guild id string. This can be:
@@ -364,7 +383,15 @@ declare enum Events {
364
383
  SEARCH_RESULT = "searchResult",
365
384
  FFMPEG_DEBUG = "ffmpegDebug"
366
385
  }
367
- type FFmpegOptions = Record<string, string | number | boolean | Array<string | null | undefined> | null | undefined>;
386
+ type FFmpegArgs = Record<string, string | number | boolean | Array<string | null | undefined> | null | undefined>;
387
+ type FFmpegOptions = {
388
+ path: string;
389
+ args: {
390
+ global: FFmpegArgs;
391
+ input: FFmpegArgs;
392
+ output: FFmpegArgs;
393
+ };
394
+ };
368
395
 
369
396
  /**
370
397
  * Default DisTube audio filters.
@@ -387,11 +414,6 @@ declare const defaultOptions: {
387
414
  joinNewVoiceChannel: true;
388
415
  streamType: StreamType.OPUS;
389
416
  directLink: true;
390
- ffmpegPath: string;
391
- ffmpegDefaultArgs: {
392
- analyzeduration: number;
393
- hide_banner: true;
394
- };
395
417
  };
396
418
 
397
419
  declare const ERROR_MESSAGES: {
@@ -413,6 +435,8 @@ declare const ERROR_MESSAGES: {
413
435
  VOICE_RECONNECT_FAILED: string;
414
436
  VOICE_DIFFERENT_GUILD: string;
415
437
  VOICE_DIFFERENT_CLIENT: string;
438
+ FFMPEG_EXITED: (code: number) => string;
439
+ FFMPEG_NOT_INSTALLED: (path: string) => string;
416
440
  NO_QUEUE: string;
417
441
  QUEUE_EXIST: string;
418
442
  PAUSED: string;
@@ -759,19 +783,18 @@ declare class DisTubeVoice extends TypedEmitter<DisTubeVoiceEvents> {
759
783
  }
760
784
 
761
785
  interface StreamOptions {
762
- ffmpeg: {
763
- path: string;
764
- args: FFmpegOptions;
765
- };
786
+ ffmpeg: FFmpegOptions;
766
787
  seek?: number;
767
788
  type?: StreamType;
768
789
  }
769
790
  declare const chooseBestVideoFormat: ({ duration, formats, isLive }: Song) => ytdl.videoFormat | undefined;
791
+ declare const checkFFmpeg: (distube: DisTube) => void;
770
792
  /**
771
793
  * Create a stream to play with {@link DisTubeVoice}
772
794
  */
773
795
  declare class DisTubeStream extends TypedEmitter<{
774
796
  debug: (debug: string) => Awaitable;
797
+ error: (error: Error) => Awaitable;
775
798
  }> {
776
799
  private killed;
777
800
  process: ChildProcess;
@@ -899,8 +922,11 @@ declare class Options {
899
922
  joinNewVoiceChannel: boolean;
900
923
  streamType: StreamType;
901
924
  directLink: boolean;
902
- ffmpegPath: string;
903
- ffmpegDefaultArgs: FFmpegOptions;
925
+ /** @deprecated */
926
+ ffmpegPath: undefined;
927
+ /** @deprecated */
928
+ ffmpegDefaultArgs: undefined;
929
+ ffmpeg: FFmpegOptions;
904
930
  constructor(options: DisTubeOptions);
905
931
  }
906
932
 
@@ -1011,7 +1037,7 @@ declare class FilterManager extends BaseManager<Filter> {
1011
1037
  * Array of enabled filters
1012
1038
  */
1013
1039
  get values(): Filter[];
1014
- get ffmpegArgs(): FFmpegOptions;
1040
+ get ffmpegArgs(): FFmpegArgs;
1015
1041
  toString(): string;
1016
1042
  }
1017
1043
 
@@ -1616,9 +1642,9 @@ declare class DisTube extends TypedEmitter<TypedDisTubeEvents> {
1616
1642
  readonly filters: Filters;
1617
1643
  /**
1618
1644
  * @deprecated Use `youtubeCookie: Cookie[]` instead. Guide: {@link
1619
- * https://distube.js.org/#/docs/DisTube/main/general/cookie | YouTube Cookies}
1645
+ * https://github.com/skick1234/DisTube/wiki/YouTube-Cookies | YouTube Cookies}
1620
1646
  */
1621
- constructor(client: Client, otp: DisTubeOptions & {
1647
+ constructor(client: Client, opts: DisTubeOptions & {
1622
1648
  youtubeCookie: string;
1623
1649
  });
1624
1650
  /**
@@ -1638,9 +1664,9 @@ declare class DisTube extends TypedEmitter<TypedDisTubeEvents> {
1638
1664
  * @throws {@link DisTubeError}
1639
1665
  *
1640
1666
  * @param client - Discord.JS client
1641
- * @param otp - Custom DisTube options
1667
+ * @param opts - Custom DisTube options
1642
1668
  */
1643
- constructor(client: Client, otp?: DisTubeOptions);
1669
+ constructor(client: Client, opts?: DisTubeOptions);
1644
1670
  static get version(): string;
1645
1671
  /**
1646
1672
  * DisTube version
@@ -1965,4 +1991,4 @@ declare class DisTube extends TypedEmitter<TypedDisTubeEvents> {
1965
1991
  emitError(error: Error, channel?: GuildTextBasedChannel): void;
1966
1992
  }
1967
1993
 
1968
- export { type Awaitable, BaseManager, type Chapter, type CustomPlaylistOptions, CustomPlugin, DirectLinkPlugin, DisTube, DisTubeBase, DisTubeError, type DisTubeEvents, DisTubeHandler, type DisTubeOptions, DisTubeStream, DisTubeVoice, type DisTubeVoiceEvents, DisTubeVoiceManager, Events, ExtractorPlugin, type FFmpegOptions, type Filter, FilterManager, type FilterResolvable, type Filters, GuildIdManager, type GuildIdResolvable, Options, type OtherSongInfo, type PlayHandlerOptions, type PlayOptions, Playlist, type PlaylistInfo, Plugin, PluginType, Queue, QueueManager, type RelatedSong, RepeatMode, type ResolveOptions, type ResolvePlaylistOptions, type SearchResult, SearchResultPlaylist, SearchResultType, SearchResultVideo, Song, StreamType, TaskQueue, type TypedDisTubeEvents, checkIntents, checkInvalidKey, chooseBestVideoFormat, DisTube as default, defaultFilters, defaultOptions, formatDuration, isClientInstance, isGuildInstance, isMemberInstance, isMessageInstance, isNsfwChannel, isObject, isRecord, isSnowflake, isSupportedVoiceChannel, isTextChannelInstance, isTruthy, isURL, isVoiceChannelEmpty, objectKeys, parseNumber, resolveGuildId, toSecond, version };
1994
+ export { type Awaitable, BaseManager, type Chapter, type CustomPlaylistOptions, CustomPlugin, DirectLinkPlugin, DisTube, DisTubeBase, DisTubeError, type DisTubeEvents, DisTubeHandler, type DisTubeOptions, DisTubeStream, DisTubeVoice, type DisTubeVoiceEvents, DisTubeVoiceManager, Events, ExtractorPlugin, type FFmpegArgs, type FFmpegOptions, type Filter, FilterManager, type FilterResolvable, type Filters, GuildIdManager, type GuildIdResolvable, Options, type OtherSongInfo, type PlayHandlerOptions, type PlayOptions, Playlist, type PlaylistInfo, Plugin, PluginType, Queue, QueueManager, type RelatedSong, RepeatMode, type ResolveOptions, type ResolvePlaylistOptions, type SearchResult, SearchResultPlaylist, SearchResultType, SearchResultVideo, Song, StreamType, TaskQueue, type TypedDisTubeEvents, checkFFmpeg, checkIntents, checkInvalidKey, chooseBestVideoFormat, DisTube as default, defaultFilters, defaultOptions, formatDuration, isClientInstance, isGuildInstance, isMemberInstance, isMessageInstance, isNsfwChannel, isObject, isRecord, isSnowflake, isSupportedVoiceChannel, isTextChannelInstance, isTruthy, isURL, isVoiceChannelEmpty, objectKeys, parseNumber, resolveGuildId, toSecond, version };
package/dist/index.js CHANGED
@@ -63,7 +63,7 @@ var require_package = __commonJS({
63
63
  "package.json"(exports2, module2) {
64
64
  module2.exports = {
65
65
  name: "distube",
66
- version: "4.2.0",
66
+ version: "4.2.2",
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",
@@ -202,6 +202,7 @@ __export(src_exports, {
202
202
  Song: () => Song,
203
203
  StreamType: () => StreamType,
204
204
  TaskQueue: () => TaskQueue,
205
+ checkFFmpeg: () => checkFFmpeg,
205
206
  checkIntents: () => checkIntents,
206
207
  checkInvalidKey: () => checkInvalidKey,
207
208
  chooseBestVideoFormat: () => chooseBestVideoFormat,
@@ -307,12 +308,7 @@ var defaultOptions = {
307
308
  emitAddListWhenCreatingQueue: true,
308
309
  joinNewVoiceChannel: true,
309
310
  streamType: 0 /* OPUS */,
310
- directLink: true,
311
- ffmpegPath: "ffmpeg",
312
- ffmpegDefaultArgs: {
313
- analyzeduration: 0,
314
- hide_banner: true
315
- }
311
+ directLink: true
316
312
  };
317
313
 
318
314
  // src/struct/DisTubeError.ts
@@ -336,6 +332,8 @@ var ERROR_MESSAGES = {
336
332
  VOICE_RECONNECT_FAILED: "Cannot reconnect to the voice channel",
337
333
  VOICE_DIFFERENT_GUILD: "Cannot join a voice channel in a different guild",
338
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`,
339
337
  NO_QUEUE: "There is no playing queue in this guild",
340
338
  QUEUE_EXIST: "This guild has a Queue already",
341
339
  PAUSED: "The queue has been paused already",
@@ -962,7 +960,7 @@ var _DisTubeVoice = class _DisTubeVoice extends import_tiny_typed_emitter.TypedE
962
960
  */
963
961
  play(dtStream) {
964
962
  this.emittedError = false;
965
- dtStream.stream.on("error", (error) => {
963
+ dtStream.on("error", (error) => {
966
964
  if (this.emittedError || error.code === "ERR_STREAM_PREMATURE_CLOSE")
967
965
  return;
968
966
  this.emittedError = true;
@@ -1080,11 +1078,41 @@ __name(_DisTubeVoice, "DisTubeVoice");
1080
1078
  var DisTubeVoice = _DisTubeVoice;
1081
1079
 
1082
1080
  // src/core/DisTubeStream.ts
1083
- var import_child_process = require("child_process");
1084
1081
  var import_node_stream = require("stream");
1082
+ var import_child_process = require("child_process");
1085
1083
  var import_tiny_typed_emitter2 = require("tiny-typed-emitter");
1086
1084
  var import_voice2 = require("@discordjs/voice");
1087
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");
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");
1088
1116
  var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.TypedEmitter {
1089
1117
  /**
1090
1118
  * Create a DisTubeStream to play with {@link DisTubeVoice}
@@ -1103,13 +1131,16 @@ var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.Typ
1103
1131
  this.type = !type ? import_voice2.StreamType.OggOpus : import_voice2.StreamType.Raw;
1104
1132
  const opts = {
1105
1133
  reconnect: 1,
1106
- reconnect_on_network_error: 1,
1107
1134
  reconnect_streamed: 1,
1108
1135
  reconnect_delay_max: 5,
1136
+ analyzeduration: 0,
1137
+ hide_banner: true,
1138
+ ...ffmpeg.args.global,
1139
+ ...ffmpeg.args.input,
1109
1140
  i: url,
1110
1141
  ar: 48e3,
1111
1142
  ac: 2,
1112
- ...ffmpeg.args
1143
+ ...ffmpeg.args.output
1113
1144
  };
1114
1145
  if (!type) {
1115
1146
  opts.f = "opus";
@@ -1119,8 +1150,6 @@ var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.Typ
1119
1150
  }
1120
1151
  if (typeof seek === "number" && seek > 0)
1121
1152
  opts.ss = seek.toString();
1122
- if (typeof ffmpeg.args === "object")
1123
- Object.assign(opts, ffmpeg.args);
1124
1153
  this.process = (0, import_child_process.spawn)(
1125
1154
  ffmpeg.path,
1126
1155
  [
@@ -1129,19 +1158,26 @@ var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.Typ
1129
1158
  ).flat(),
1130
1159
  "pipe:1"
1131
1160
  ],
1132
- { stdio: ["ignore", "pipe", "pipe"] }
1133
- ).on("error", (err) => this.debug(`[process] error: ${err.message}`)).on("exit", (code, signal) => {
1161
+ { stdio: ["ignore", "pipe", "pipe"], shell: false, windowsHide: true }
1162
+ ).on("error", (err) => {
1163
+ this.debug(`[process] error: ${err.message}`);
1164
+ this.emit("error", err);
1165
+ }).on("exit", (code, signal) => {
1134
1166
  this.debug(`[process] exit: code=${code ?? "unknown"} signal=${signal ?? "unknown"}`);
1135
1167
  if (!code || [0, 255].includes(code))
1136
1168
  return;
1137
1169
  this.debug(`[process] error: ffmpeg exited with code ${code}`);
1170
+ this.emit("error", new DisTubeError("FFMPEG_EXITED", code));
1138
1171
  });
1139
1172
  if (!this.process.stdout || !this.process.stderr) {
1140
1173
  this.kill();
1141
1174
  throw new Error("Failed to create ffmpeg process");
1142
1175
  }
1143
1176
  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"));
1177
+ this.stream.on("close", () => this.kill()).on("error", (err) => {
1178
+ this.debug(`[stream] error: ${err.message}`);
1179
+ this.emit("error", err);
1180
+ }).on("finish", () => this.debug("[stream] log: stream finished"));
1145
1181
  this.process.stdout.pipe(this.stream);
1146
1182
  this.process.stderr.setEncoding("utf8")?.on("data", (data) => {
1147
1183
  const lines = data.split(/\r\n|\r|\n/u);
@@ -1247,7 +1283,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1247
1283
  const cookies = __privateSet(this, _cookie, this.options.youtubeCookie);
1248
1284
  if (typeof cookies === "string") {
1249
1285
  console.warn(
1250
- "\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)"
1286
+ "\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)"
1251
1287
  );
1252
1288
  options.agent = import_ytdl_core.default.createAgent(
1253
1289
  cookies.split(";").map((c) => import_tough_cookie.Cookie.parse(c)).filter(isTruthy)
@@ -1544,10 +1580,11 @@ __name(_DisTubeHandler, "DisTubeHandler");
1544
1580
  var DisTubeHandler = _DisTubeHandler;
1545
1581
 
1546
1582
  // src/core/DisTubeOptions.ts
1547
- var _validateOptions, validateOptions_fn;
1583
+ var _validateOptions, validateOptions_fn, _ffmpegOption, ffmpegOption_fn;
1548
1584
  var _Options = class _Options {
1549
1585
  constructor(options) {
1550
1586
  __privateAdd(this, _validateOptions);
1587
+ __privateAdd(this, _ffmpegOption);
1551
1588
  __publicField(this, "plugins");
1552
1589
  __publicField(this, "emitNewSongOnly");
1553
1590
  __publicField(this, "leaveOnFinish");
@@ -1566,8 +1603,11 @@ var _Options = class _Options {
1566
1603
  __publicField(this, "joinNewVoiceChannel");
1567
1604
  __publicField(this, "streamType");
1568
1605
  __publicField(this, "directLink");
1606
+ /** @deprecated */
1569
1607
  __publicField(this, "ffmpegPath");
1608
+ /** @deprecated */
1570
1609
  __publicField(this, "ffmpegDefaultArgs");
1610
+ __publicField(this, "ffmpeg");
1571
1611
  if (typeof options !== "object" || Array.isArray(options)) {
1572
1612
  throw new DisTubeError("INVALID_TYPE", "object", options, "DisTubeOptions");
1573
1613
  }
@@ -1590,8 +1630,7 @@ var _Options = class _Options {
1590
1630
  this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
1591
1631
  this.streamType = opts.streamType;
1592
1632
  this.directLink = opts.directLink;
1593
- this.ffmpegPath = opts.ffmpegPath;
1594
- this.ffmpegDefaultArgs = opts.ffmpegDefaultArgs;
1633
+ this.ffmpeg = __privateMethod(this, _ffmpegOption, ffmpegOption_fn).call(this, options);
1595
1634
  checkInvalidKey(opts, this, "DisTubeOptions");
1596
1635
  __privateMethod(this, _validateOptions, validateOptions_fn).call(this);
1597
1636
  }
@@ -1611,8 +1650,8 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1611
1650
  "directLink"
1612
1651
  ]);
1613
1652
  const numberOptions = /* @__PURE__ */ new Set(["searchCooldown", "emptyCooldown", "searchSongs"]);
1614
- const stringOptions = /* @__PURE__ */ new Set(["ffmpegPath"]);
1615
- const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpegDefaultArgs"]);
1653
+ const stringOptions = /* @__PURE__ */ new Set();
1654
+ const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpeg"]);
1616
1655
  const optionalOptions = /* @__PURE__ */ new Set(["youtubeCookie", "customFilters"]);
1617
1656
  for (const [key, value] of Object.entries(options)) {
1618
1657
  if (value === void 0 && optionalOptions.has(key))
@@ -1642,6 +1681,29 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1642
1681
  }
1643
1682
  }
1644
1683
  }, "#validateOptions");
1684
+ _ffmpegOption = new WeakSet();
1685
+ ffmpegOption_fn = /* @__PURE__ */ __name(function(opts) {
1686
+ let path;
1687
+ const args = { global: {}, input: {}, output: {} };
1688
+ if (opts.ffmpegPath) {
1689
+ console.warn("`DisTubeOptions.ffmpegPath` is deprecated. Use `ffmpeg.path` instead.");
1690
+ path = opts.ffmpegPath;
1691
+ }
1692
+ if (opts.ffmpegDefaultArgs) {
1693
+ console.warn("`DisTubeOptions.ffmpegDefaultArgs` is deprecated. Use `ffmpeg.args` instead.");
1694
+ args.global = opts.ffmpegDefaultArgs;
1695
+ }
1696
+ path ??= opts.ffmpeg?.path ?? "ffmpeg";
1697
+ if (opts.ffmpeg?.args) {
1698
+ if (opts.ffmpeg.args.global)
1699
+ args.global = opts.ffmpeg.args.global;
1700
+ if (opts.ffmpeg.args.input)
1701
+ args.input = opts.ffmpeg.args.input;
1702
+ if (opts.ffmpeg.args.output)
1703
+ args.output = opts.ffmpeg.args.output;
1704
+ }
1705
+ return { path, args };
1706
+ }, "#ffmpegOption");
1645
1707
  __name(_Options, "Options");
1646
1708
  var Options = _Options;
1647
1709
 
@@ -1920,6 +1982,7 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1920
1982
  const queue = new Queue(this.distube, voice, song, textChannel);
1921
1983
  await queue._taskQueue.queuing();
1922
1984
  try {
1985
+ checkFFmpeg(this.distube);
1923
1986
  await voice.join();
1924
1987
  __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1925
1988
  this.add(queue.id, queue);
@@ -1940,10 +2003,11 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1940
2003
  const { duration, source, streamURL } = song;
1941
2004
  const streamOptions = {
1942
2005
  ffmpeg: {
1943
- path: this.options.ffmpegPath,
2006
+ path: this.options.ffmpeg.path,
1944
2007
  args: {
1945
- ...this.options.ffmpegDefaultArgs,
1946
- ...queue.filters.ffmpegArgs
2008
+ global: { ...this.options.ffmpeg.args.global },
2009
+ input: { ...this.options.ffmpeg.args.input },
2010
+ output: { ...this.options.ffmpeg.args.output, ...queue.filters.ffmpegArgs }
1947
2011
  }
1948
2012
  },
1949
2013
  seek: duration ? queue.beginTime : void 0,
@@ -1951,6 +2015,8 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1951
2015
  };
1952
2016
  if (source === "youtube")
1953
2017
  return DisTubeStream.YouTube(song, streamOptions);
2018
+ if (!streamURL)
2019
+ throw new Error("No streamURL, something went wrong");
1954
2020
  return DisTubeStream.DirectLink(streamURL, streamOptions);
1955
2021
  }
1956
2022
  /**
@@ -2737,15 +2803,9 @@ var _DirectLinkPlugin = class _DirectLinkPlugin extends ExtractorPlugin {
2737
2803
  return false;
2738
2804
  }
2739
2805
  resolve(url, options = {}) {
2740
- url = url.replace(/\/+$/, "");
2741
- return new Song(
2742
- {
2743
- name: url.substring(url.lastIndexOf("/") + 1).replace(/((\?|#).*)?$/, "") || url,
2744
- url,
2745
- src: "direct_link"
2746
- },
2747
- options
2748
- );
2806
+ const u = new URL(url);
2807
+ const name = u.pathname.split("/").pop() || u.href;
2808
+ return new Song({ name, url, src: "direct_link" }, options);
2749
2809
  }
2750
2810
  };
2751
2811
  __name(_DirectLinkPlugin, "DirectLinkPlugin");
@@ -2757,7 +2817,7 @@ var import_tiny_typed_emitter3 = require("tiny-typed-emitter");
2757
2817
  var { version } = require_package();
2758
2818
  var _getQueue, getQueue_fn;
2759
2819
  var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
2760
- constructor(client, otp = {}) {
2820
+ constructor(client, opts = {}) {
2761
2821
  super();
2762
2822
  __privateAdd(this, _getQueue);
2763
2823
  __publicField(this, "handler");
@@ -2773,7 +2833,7 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
2773
2833
  throw new DisTubeError("INVALID_TYPE", "Discord.Client", client, "client");
2774
2834
  this.client = client;
2775
2835
  checkIntents(client.options);
2776
- this.options = new Options(otp);
2836
+ this.options = new Options(opts);
2777
2837
  this.voices = new DisTubeVoiceManager(this);
2778
2838
  this.handler = new DisTubeHandler(this);
2779
2839
  this.queues = new QueueManager(this);
@@ -3261,7 +3321,7 @@ ${e.message}`;
3261
3321
  console.error(error);
3262
3322
  console.warn("Unhandled 'error' event.");
3263
3323
  console.warn(
3264
- "See: https://distube.js.org/#/docs/DisTube/stable/class/DisTube?scrollTo=e-error and https://nodejs.org/api/events.html#events_error_events"
3324
+ "See: https://distube.js.org/classes/DisTube.html#error and https://nodejs.org/api/events.html#events_error_events"
3265
3325
  );
3266
3326
  }
3267
3327
  }
@@ -3304,6 +3364,7 @@ var DisTube = _DisTube;
3304
3364
  Song,
3305
3365
  StreamType,
3306
3366
  TaskQueue,
3367
+ checkFFmpeg,
3307
3368
  checkIntents,
3308
3369
  checkInvalidKey,
3309
3370
  chooseBestVideoFormat,