distube 4.2.0 → 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/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.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",
@@ -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";
@@ -1129,19 +1160,26 @@ var _DisTubeStream = class _DisTubeStream extends import_tiny_typed_emitter2.Typ
1129
1160
  ).flat(),
1130
1161
  "pipe:1"
1131
1162
  ],
1132
- { stdio: ["ignore", "pipe", "pipe"] }
1133
- ).on("error", (err) => this.debug(`[process] error: ${err.message}`)).on("exit", (code, signal) => {
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) => {
1134
1168
  this.debug(`[process] exit: code=${code ?? "unknown"} signal=${signal ?? "unknown"}`);
1135
1169
  if (!code || [0, 255].includes(code))
1136
1170
  return;
1137
1171
  this.debug(`[process] error: ffmpeg exited with code ${code}`);
1172
+ this.emit("error", new DisTubeError("FFMPEG_EXITED", code));
1138
1173
  });
1139
1174
  if (!this.process.stdout || !this.process.stderr) {
1140
1175
  this.kill();
1141
1176
  throw new Error("Failed to create ffmpeg process");
1142
1177
  }
1143
1178
  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"));
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"));
1145
1183
  this.process.stdout.pipe(this.stream);
1146
1184
  this.process.stderr.setEncoding("utf8")?.on("data", (data) => {
1147
1185
  const lines = data.split(/\r\n|\r|\n/u);
@@ -1247,7 +1285,7 @@ var _DisTubeHandler = class _DisTubeHandler extends DisTubeBase {
1247
1285
  const cookies = __privateSet(this, _cookie, this.options.youtubeCookie);
1248
1286
  if (typeof cookies === "string") {
1249
1287
  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)"
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)"
1251
1289
  );
1252
1290
  options.agent = import_ytdl_core.default.createAgent(
1253
1291
  cookies.split(";").map((c) => import_tough_cookie.Cookie.parse(c)).filter(isTruthy)
@@ -1544,10 +1582,11 @@ __name(_DisTubeHandler, "DisTubeHandler");
1544
1582
  var DisTubeHandler = _DisTubeHandler;
1545
1583
 
1546
1584
  // src/core/DisTubeOptions.ts
1547
- var _validateOptions, validateOptions_fn;
1585
+ var _validateOptions, validateOptions_fn, _ffmpegOption, ffmpegOption_fn;
1548
1586
  var _Options = class _Options {
1549
1587
  constructor(options) {
1550
1588
  __privateAdd(this, _validateOptions);
1589
+ __privateAdd(this, _ffmpegOption);
1551
1590
  __publicField(this, "plugins");
1552
1591
  __publicField(this, "emitNewSongOnly");
1553
1592
  __publicField(this, "leaveOnFinish");
@@ -1566,8 +1605,11 @@ var _Options = class _Options {
1566
1605
  __publicField(this, "joinNewVoiceChannel");
1567
1606
  __publicField(this, "streamType");
1568
1607
  __publicField(this, "directLink");
1608
+ /** @deprecated */
1569
1609
  __publicField(this, "ffmpegPath");
1610
+ /** @deprecated */
1570
1611
  __publicField(this, "ffmpegDefaultArgs");
1612
+ __publicField(this, "ffmpeg");
1571
1613
  if (typeof options !== "object" || Array.isArray(options)) {
1572
1614
  throw new DisTubeError("INVALID_TYPE", "object", options, "DisTubeOptions");
1573
1615
  }
@@ -1590,8 +1632,7 @@ var _Options = class _Options {
1590
1632
  this.joinNewVoiceChannel = opts.joinNewVoiceChannel;
1591
1633
  this.streamType = opts.streamType;
1592
1634
  this.directLink = opts.directLink;
1593
- this.ffmpegPath = opts.ffmpegPath;
1594
- this.ffmpegDefaultArgs = opts.ffmpegDefaultArgs;
1635
+ this.ffmpeg = __privateMethod(this, _ffmpegOption, ffmpegOption_fn).call(this, options);
1595
1636
  checkInvalidKey(opts, this, "DisTubeOptions");
1596
1637
  __privateMethod(this, _validateOptions, validateOptions_fn).call(this);
1597
1638
  }
@@ -1611,8 +1652,8 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1611
1652
  "directLink"
1612
1653
  ]);
1613
1654
  const numberOptions = /* @__PURE__ */ new Set(["searchCooldown", "emptyCooldown", "searchSongs"]);
1614
- const stringOptions = /* @__PURE__ */ new Set(["ffmpegPath"]);
1615
- const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpegDefaultArgs"]);
1655
+ const stringOptions = /* @__PURE__ */ new Set();
1656
+ const objectOptions = /* @__PURE__ */ new Set(["customFilters", "ytdlOptions", "ffmpeg"]);
1616
1657
  const optionalOptions = /* @__PURE__ */ new Set(["youtubeCookie", "customFilters"]);
1617
1658
  for (const [key, value] of Object.entries(options)) {
1618
1659
  if (value === void 0 && optionalOptions.has(key))
@@ -1642,6 +1683,29 @@ validateOptions_fn = /* @__PURE__ */ __name(function(options = this) {
1642
1683
  }
1643
1684
  }
1644
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");
1645
1709
  __name(_Options, "Options");
1646
1710
  var Options = _Options;
1647
1711
 
@@ -1920,6 +1984,7 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1920
1984
  const queue = new Queue(this.distube, voice, song, textChannel);
1921
1985
  await queue._taskQueue.queuing();
1922
1986
  try {
1987
+ checkFFmpeg(this.distube);
1923
1988
  await voice.join();
1924
1989
  __privateMethod(this, _voiceEventHandler, voiceEventHandler_fn).call(this, queue);
1925
1990
  this.add(queue.id, queue);
@@ -1940,10 +2005,11 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
1940
2005
  const { duration, source, streamURL } = song;
1941
2006
  const streamOptions = {
1942
2007
  ffmpeg: {
1943
- path: this.options.ffmpegPath,
2008
+ path: this.options.ffmpeg.path,
1944
2009
  args: {
1945
- ...this.options.ffmpegDefaultArgs,
1946
- ...queue.filters.ffmpegArgs
2010
+ global: { ...this.options.ffmpeg.args.global },
2011
+ input: { ...this.options.ffmpeg.args.input },
2012
+ output: { ...this.options.ffmpeg.args.output, ...queue.filters.ffmpegArgs }
1947
2013
  }
1948
2014
  },
1949
2015
  seek: duration ? queue.beginTime : void 0,
@@ -1951,6 +2017,8 @@ var _QueueManager = class _QueueManager extends GuildIdManager {
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
  /**
@@ -2737,15 +2805,9 @@ var _DirectLinkPlugin = class _DirectLinkPlugin extends ExtractorPlugin {
2737
2805
  return false;
2738
2806
  }
2739
2807
  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
- );
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);
2749
2811
  }
2750
2812
  };
2751
2813
  __name(_DirectLinkPlugin, "DirectLinkPlugin");
@@ -2757,7 +2819,7 @@ var import_tiny_typed_emitter3 = require("tiny-typed-emitter");
2757
2819
  var { version } = require_package();
2758
2820
  var _getQueue, getQueue_fn;
2759
2821
  var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
2760
- constructor(client, otp = {}) {
2822
+ constructor(client, opts = {}) {
2761
2823
  super();
2762
2824
  __privateAdd(this, _getQueue);
2763
2825
  __publicField(this, "handler");
@@ -2773,7 +2835,7 @@ var _DisTube = class _DisTube extends import_tiny_typed_emitter3.TypedEmitter {
2773
2835
  throw new DisTubeError("INVALID_TYPE", "Discord.Client", client, "client");
2774
2836
  this.client = client;
2775
2837
  checkIntents(client.options);
2776
- this.options = new Options(otp);
2838
+ this.options = new Options(opts);
2777
2839
  this.voices = new DisTubeVoiceManager(this);
2778
2840
  this.handler = new DisTubeHandler(this);
2779
2841
  this.queues = new QueueManager(this);
@@ -3261,7 +3323,7 @@ ${e.message}`;
3261
3323
  console.error(error);
3262
3324
  console.warn("Unhandled 'error' event.");
3263
3325
  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"
3326
+ "See: https://distube.js.org/classes/DisTube.html#error and https://nodejs.org/api/events.html#events_error_events"
3265
3327
  );
3266
3328
  }
3267
3329
  }
@@ -3304,6 +3366,7 @@ var DisTube = _DisTube;
3304
3366
  Song,
3305
3367
  StreamType,
3306
3368
  TaskQueue,
3369
+ checkFFmpeg,
3307
3370
  checkIntents,
3308
3371
  checkInvalidKey,
3309
3372
  chooseBestVideoFormat,