distube 5.1.2 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,7 +2,15 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/constant.ts
5
- var version = "5.1.2";
5
+ var version = "5.2.0";
6
+ var AUDIO_SAMPLE_RATE = 48e3;
7
+ var AUDIO_CHANNELS = 2;
8
+ var DEFAULT_VOLUME = 50;
9
+ var JOIN_TIMEOUT_MS = 3e4;
10
+ var RECONNECT_TIMEOUT_MS = 5e3;
11
+ var RECONNECT_MAX_ATTEMPTS = 5;
12
+ var HTTP_REDIRECT_CODES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
13
+ var MAX_REDIRECT_DEPTH = 5;
6
14
  var defaultFilters = {
7
15
  "3d": "apulsator=hz=0.125",
8
16
  bassboost: "bass=g=10",
@@ -222,17 +230,17 @@ var DisTubeVoice = class extends TypedEmitter {
222
230
  if (newState.reason === VoiceConnectionDisconnectReason.Manual) {
223
231
  this.leave();
224
232
  } else if (newState.reason === VoiceConnectionDisconnectReason.WebSocketClose && newState.closeCode === 4014) {
225
- entersState(this.connection, VoiceConnectionStatus.Connecting, 5e3).catch(() => {
233
+ entersState(this.connection, VoiceConnectionStatus.Connecting, RECONNECT_TIMEOUT_MS).catch(() => {
226
234
  if (![VoiceConnectionStatus.Ready, VoiceConnectionStatus.Connecting].includes(this.connection.state.status)) {
227
235
  this.leave();
228
236
  }
229
237
  });
230
- } else if (this.connection.rejoinAttempts < 5) {
238
+ } else if (this.connection.rejoinAttempts < RECONNECT_MAX_ATTEMPTS) {
231
239
  setTimeout(
232
240
  () => {
233
241
  this.connection.rejoin();
234
242
  },
235
- (this.connection.rejoinAttempts + 1) * 5e3
243
+ (this.connection.rejoinAttempts + 1) * RECONNECT_TIMEOUT_MS
236
244
  ).unref();
237
245
  } else if (this.connection.state.status !== VoiceConnectionStatus.Destroyed) {
238
246
  this.leave(new DisTubeError("VOICE_RECONNECT_FAILED"));
@@ -288,15 +296,14 @@ var DisTubeVoice = class extends TypedEmitter {
288
296
  * @param channel - A voice channel
289
297
  */
290
298
  async join(channel) {
291
- const TIMEOUT = 3e4;
292
299
  if (channel) this.channel = channel;
293
300
  try {
294
- await entersState(this.connection, VoiceConnectionStatus.Ready, TIMEOUT);
301
+ await entersState(this.connection, VoiceConnectionStatus.Ready, JOIN_TIMEOUT_MS);
295
302
  } catch {
296
303
  if (this.connection.state.status === VoiceConnectionStatus.Ready) return this;
297
304
  if (this.connection.state.status !== VoiceConnectionStatus.Destroyed) this.connection.destroy();
298
305
  this.voices.remove(this.id);
299
- throw new DisTubeError("VOICE_CONNECT_FAILED", TIMEOUT / 1e3);
306
+ throw new DisTubeError("VOICE_CONNECT_FAILED", JOIN_TIMEOUT_MS / 1e3);
300
307
  }
301
308
  return this;
302
309
  }
@@ -321,6 +328,7 @@ var DisTubeVoice = class extends TypedEmitter {
321
328
  stop(force = false) {
322
329
  this.audioPlayer.stop(force);
323
330
  }
331
+ #streamErrorHandler;
324
332
  /**
325
333
  * Play a {@link DisTubeStream}
326
334
  * @param dtStream - DisTubeStream
@@ -331,11 +339,15 @@ var DisTubeVoice = class extends TypedEmitter {
331
339
  throw new DisTubeError("ENCRYPTION_LIBRARIES_MISSING");
332
340
  }
333
341
  this.emittedError = false;
334
- dtStream.on("error", (error) => {
342
+ if (this.stream && this.#streamErrorHandler) {
343
+ this.stream.off("error", this.#streamErrorHandler);
344
+ }
345
+ this.#streamErrorHandler = (error) => {
335
346
  if (this.emittedError || error.code === "ERR_STREAM_PREMATURE_CLOSE") return;
336
347
  this.emittedError = true;
337
348
  this.emit("error", error);
338
- });
349
+ };
350
+ dtStream.on("error", this.#streamErrorHandler);
339
351
  if (this.audioPlayer.state.status !== AudioPlayerStatus.Paused) {
340
352
  this.audioPlayer.play(dtStream.audioResource);
341
353
  this.stream?.kill();
@@ -363,11 +375,17 @@ var DisTubeVoice = class extends TypedEmitter {
363
375
  return this.#volume;
364
376
  }
365
377
  /**
366
- * Playback duration of the audio resource in seconds
378
+ * Playback duration of the audio resource in seconds (time since playback started)
367
379
  */
368
380
  get playbackDuration() {
369
381
  return (this.stream?.audioResource?.playbackDuration ?? 0) / 1e3;
370
382
  }
383
+ /**
384
+ * Current playback time in seconds, accounting for seek offset
385
+ */
386
+ get playbackTime() {
387
+ return this.playbackDuration + (this.stream?.seekTime ?? 0);
388
+ }
371
389
  pause() {
372
390
  this.audioPlayer.pause();
373
391
  }
@@ -663,7 +681,10 @@ var Queue = class extends DisTubeBase {
663
681
  */
664
682
  stopped;
665
683
  /**
666
- * Whether or not the stream is currently playing.
684
+ * Whether or not the queue is active.
685
+ *
686
+ * Note: This remains `true` when paused. It only becomes `false` when stopped.
687
+ * @deprecated Use `!queue.paused` to check if audio is playing. Will be removed in v6.0.
667
688
  */
668
689
  playing;
669
690
  /**
@@ -690,19 +711,23 @@ var Queue = class extends DisTubeBase {
690
711
  textChannel;
691
712
  /**
692
713
  * What time in the song to begin (in seconds).
714
+ * @internal
693
715
  */
694
716
  _beginTime;
695
717
  #filters;
696
718
  /**
697
719
  * Whether or not the queue is being updated manually (skip, jump, previous)
720
+ * @internal
698
721
  */
699
722
  _manualUpdate;
700
723
  /**
701
724
  * Task queuing system
725
+ * @internal
702
726
  */
703
727
  _taskQueue;
704
728
  /**
705
729
  * {@link DisTubeVoice} listener
730
+ * @internal
706
731
  */
707
732
  _listeners;
708
733
  /**
@@ -715,7 +740,7 @@ var Queue = class extends DisTubeBase {
715
740
  super(distube);
716
741
  this.voice = voice;
717
742
  this.id = voice.id;
718
- this.volume = 50;
743
+ this.volume = DEFAULT_VOLUME;
719
744
  this.songs = [];
720
745
  this.previousSongs = [];
721
746
  this.stopped = false;
@@ -780,7 +805,7 @@ var Queue = class extends DisTubeBase {
780
805
  * What time in the song is playing (in seconds).
781
806
  */
782
807
  get currentTime() {
783
- return this.voice.playbackDuration + this._beginTime;
808
+ return this.voice.playbackTime;
784
809
  }
785
810
  /**
786
811
  * Formatted {@link Queue#currentTime} string.
@@ -828,13 +853,15 @@ var Queue = class extends DisTubeBase {
828
853
  return this;
829
854
  }
830
855
  /**
831
- * @returns `true` if the queue is playing
856
+ * @returns `true` if the queue is active (not stopped)
857
+ * @deprecated Use `!queue.paused` to check if audio is playing. Will be removed in v6.0.
832
858
  */
833
859
  isPlaying() {
834
860
  return this.playing;
835
861
  }
836
862
  /**
837
863
  * @returns `true` if the queue is paused
864
+ * @deprecated Use `queue.paused` property instead. Will be removed in v6.0.
838
865
  */
839
866
  isPaused() {
840
867
  return this.paused;
@@ -1039,7 +1066,7 @@ var Queue = class extends DisTubeBase {
1039
1066
  */
1040
1067
  remove() {
1041
1068
  this.playing = false;
1042
- this.paused = false;
1069
+ this.paused = true;
1043
1070
  this.stopped = true;
1044
1071
  this.songs = [];
1045
1072
  this.previousSongs = [];
@@ -1431,7 +1458,6 @@ var Song = class {
1431
1458
  };
1432
1459
 
1433
1460
  // src/core/DisTubeHandler.ts
1434
- var REDIRECT_CODES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
1435
1461
  var DisTubeHandler = class extends DisTubeBase {
1436
1462
  static {
1437
1463
  __name(this, "DisTubeHandler");
@@ -1518,7 +1544,7 @@ var DisTubeHandler = class extends DisTubeBase {
1518
1544
  song.stream.song = altSong;
1519
1545
  }
1520
1546
  }
1521
- async followRedirectLink(url, maxRedirect = 5) {
1547
+ async followRedirectLink(url, maxRedirect = MAX_REDIRECT_DEPTH) {
1522
1548
  if (maxRedirect === 0) return url;
1523
1549
  const res = await request(url, {
1524
1550
  method: "HEAD",
@@ -1526,7 +1552,7 @@ var DisTubeHandler = class extends DisTubeBase {
1526
1552
  "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.3"
1527
1553
  }
1528
1554
  });
1529
- if (REDIRECT_CODES.has(res.statusCode ?? 200)) {
1555
+ if (HTTP_REDIRECT_CODES.has(res.statusCode ?? 200)) {
1530
1556
  let location = res.headers.location;
1531
1557
  if (typeof location !== "string") location = location?.[0] ?? url;
1532
1558
  return this.followRedirectLink(location, --maxRedirect);
@@ -1655,7 +1681,8 @@ var checkFFmpeg = /* @__PURE__ */ __name((distube) => {
1655
1681
  if (!version2) throw new Error("Invalid FFmpeg version");
1656
1682
  debug(`[test] ffmpeg version: ${version2}`);
1657
1683
  } catch (e) {
1658
- debug(`[test] failed to spawn ffmpeg at '${path}': ${e?.stack ?? e}`);
1684
+ const errorMessage = e instanceof Error ? e.stack ?? e.message : String(e);
1685
+ debug(`[test] failed to spawn ffmpeg at '${path}': ${errorMessage}`);
1659
1686
  throw new DisTubeError("FFMPEG_NOT_INSTALLED", path);
1660
1687
  }
1661
1688
  checked = true;
@@ -1669,6 +1696,10 @@ var DisTubeStream = class extends TypedEmitter2 {
1669
1696
  process;
1670
1697
  stream;
1671
1698
  audioResource;
1699
+ /**
1700
+ * The seek time in seconds that this stream started from
1701
+ */
1702
+ seekTime;
1672
1703
  /**
1673
1704
  * Create a DisTubeStream to play with {@link DisTubeVoice}
1674
1705
  * @param url - Stream URL
@@ -1677,6 +1708,7 @@ var DisTubeStream = class extends TypedEmitter2 {
1677
1708
  constructor(url, options) {
1678
1709
  super();
1679
1710
  const { ffmpeg, seek } = options;
1711
+ this.seekTime = typeof seek === "number" && seek > 0 ? seek : 0;
1680
1712
  const opts = {
1681
1713
  reconnect: 1,
1682
1714
  reconnect_streamed: 1,
@@ -1686,8 +1718,8 @@ var DisTubeStream = class extends TypedEmitter2 {
1686
1718
  ...ffmpeg.args.global,
1687
1719
  ...ffmpeg.args.input,
1688
1720
  i: url,
1689
- ar: 48e3,
1690
- ac: 2,
1721
+ ar: AUDIO_SAMPLE_RATE,
1722
+ ac: AUDIO_CHANNELS,
1691
1723
  ...ffmpeg.args.output,
1692
1724
  f: "s16le"
1693
1725
  };
@@ -1895,8 +1927,8 @@ var QueueManager = class extends GuildIdManager {
1895
1927
  this.emit("disconnect" /* DISCONNECT */, queue);
1896
1928
  if (error) this.emitError(error, queue, queue.songs?.[0]);
1897
1929
  }, "disconnect"),
1898
- error: /* @__PURE__ */ __name((error) => this.#handlePlayingError(queue, error), "error"),
1899
- finish: /* @__PURE__ */ __name(() => this.handleSongFinish(queue), "finish")
1930
+ error: /* @__PURE__ */ __name((error) => void this.#handlePlayingError(queue, error), "error"),
1931
+ finish: /* @__PURE__ */ __name(() => void this.handleSongFinish(queue), "finish")
1900
1932
  };
1901
1933
  for (const event of objectKeys(queue._listeners)) {
1902
1934
  queue.voice.on(event, queue._listeners[event]);
@@ -1929,8 +1961,13 @@ var QueueManager = class extends GuildIdManager {
1929
1961
  this.debug(`[QueueManager] Adding related song: ${queue.id}`);
1930
1962
  await queue.addRelatedSong(song);
1931
1963
  } catch (e) {
1932
- this.debug(`[${queue.id}] Add related song error: ${e.message}`);
1933
- this.emit("noRelated" /* NO_RELATED */, queue, e);
1964
+ const errorMessage = e instanceof Error ? e.message : String(e);
1965
+ this.debug(`[${queue.id}] Add related song error: ${errorMessage}`);
1966
+ if (e instanceof DisTubeError) {
1967
+ this.emit("noRelated" /* NO_RELATED */, queue, e);
1968
+ } else {
1969
+ this.emit("noRelated" /* NO_RELATED */, queue, new DisTubeError("NO_RELATED"));
1970
+ }
1934
1971
  }
1935
1972
  }
1936
1973
  if (queue.songs.length === 0) {
@@ -1953,7 +1990,7 @@ var QueueManager = class extends GuildIdManager {
1953
1990
  * @param queue - queue
1954
1991
  * @param error - error
1955
1992
  */
1956
- #handlePlayingError(queue, error) {
1993
+ async #handlePlayingError(queue, error) {
1957
1994
  const song = queue.songs.shift();
1958
1995
  try {
1959
1996
  error.name = "PlayingError";
@@ -1963,10 +2000,10 @@ var QueueManager = class extends GuildIdManager {
1963
2000
  this.emitError(error, queue, song);
1964
2001
  if (queue.songs.length > 0) {
1965
2002
  this.debug(`[${queue.id}] Playing next song: ${queue.songs[0]}`);
1966
- this.playSong(queue);
2003
+ await this.playSong(queue);
1967
2004
  } else {
1968
2005
  this.debug(`[${queue.id}] Queue is empty, stopping...`);
1969
- queue.stop();
2006
+ await queue.stop();
1970
2007
  }
1971
2008
  }
1972
2009
  /**
@@ -1977,7 +2014,7 @@ var QueueManager = class extends GuildIdManager {
1977
2014
  async playSong(queue, emitPlaySong = true) {
1978
2015
  if (!queue) return;
1979
2016
  if (queue.stopped || !queue.songs.length) {
1980
- queue.stop();
2017
+ await queue.stop();
1981
2018
  return;
1982
2019
  }
1983
2020
  try {
@@ -2006,7 +2043,8 @@ var QueueManager = class extends GuildIdManager {
2006
2043
  await queue.voice.play(dtStream);
2007
2044
  if (emitPlaySong) this.emit("playSong" /* PLAY_SONG */, queue, song);
2008
2045
  } catch (e) {
2009
- this.#handlePlayingError(queue, e);
2046
+ const error = e instanceof Error ? e : new Error(String(e));
2047
+ this.#handlePlayingError(queue, error);
2010
2048
  }
2011
2049
  }
2012
2050
  };
@@ -2214,12 +2252,15 @@ var DisTube = class extends TypedEmitter3 {
2214
2252
  else if (skip) await queue.skip();
2215
2253
  } catch (e) {
2216
2254
  if (!(e instanceof DisTubeError)) {
2217
- this.debug(`[${queue.id}] Unexpected error while playing song: ${e.stack || e.message}`);
2218
- try {
2219
- e.name = "PlayError";
2220
- e.message = `${typeof song === "string" ? song : song.url}
2255
+ const errorMessage = e instanceof Error ? e.stack ?? e.message : String(e);
2256
+ this.debug(`[${queue.id}] Unexpected error while playing song: ${errorMessage}`);
2257
+ if (e instanceof Error) {
2258
+ try {
2259
+ e.name = "PlayError";
2260
+ e.message = `${typeof song === "string" ? song : song.url}
2221
2261
  ${e.message}`;
2222
- } catch {
2262
+ } catch {
2263
+ }
2223
2264
  }
2224
2265
  }
2225
2266
  throw e;
@@ -2281,6 +2322,7 @@ ${e.message}`;
2281
2322
  * Pause the guild stream
2282
2323
  * @param guild - The type can be resolved to give a {@link Queue}
2283
2324
  * @returns The guild queue
2325
+ * @deprecated Use `distube.getQueue(guild).pause()` instead. Will be removed in v6.0.
2284
2326
  */
2285
2327
  pause(guild) {
2286
2328
  return this.#getQueue(guild).pause();
@@ -2289,6 +2331,7 @@ ${e.message}`;
2289
2331
  * Resume the guild stream
2290
2332
  * @param guild - The type can be resolved to give a {@link Queue}
2291
2333
  * @returns The guild queue
2334
+ * @deprecated Use `distube.getQueue(guild).resume()` instead. Will be removed in v6.0.
2292
2335
  */
2293
2336
  resume(guild) {
2294
2337
  return this.#getQueue(guild).resume();
@@ -2296,6 +2339,7 @@ ${e.message}`;
2296
2339
  /**
2297
2340
  * Stop the guild stream
2298
2341
  * @param guild - The type can be resolved to give a {@link Queue}
2342
+ * @deprecated Use `distube.getQueue(guild).stop()` instead. Will be removed in v6.0.
2299
2343
  */
2300
2344
  stop(guild) {
2301
2345
  return this.#getQueue(guild).stop();
@@ -2305,6 +2349,7 @@ ${e.message}`;
2305
2349
  * @param guild - The type can be resolved to give a {@link Queue}
2306
2350
  * @param percent - The percentage of volume you want to set
2307
2351
  * @returns The guild queue
2352
+ * @deprecated Use `distube.getQueue(guild).setVolume(percent)` instead. Will be removed in v6.0.
2308
2353
  */
2309
2354
  setVolume(guild, percent) {
2310
2355
  return this.#getQueue(guild).setVolume(percent);
@@ -2315,6 +2360,7 @@ ${e.message}`;
2315
2360
  * play a related song.</info>
2316
2361
  * @param guild - The type can be resolved to give a {@link Queue}
2317
2362
  * @returns The new Song will be played
2363
+ * @deprecated Use `distube.getQueue(guild).skip(options)` instead. Will be removed in v6.0.
2318
2364
  */
2319
2365
  skip(guild, options) {
2320
2366
  return this.#getQueue(guild).skip(options);
@@ -2323,6 +2369,7 @@ ${e.message}`;
2323
2369
  * Play the previous song
2324
2370
  * @param guild - The type can be resolved to give a {@link Queue}
2325
2371
  * @returns The new Song will be played
2372
+ * @deprecated Use `distube.getQueue(guild).previous()` instead. Will be removed in v6.0.
2326
2373
  */
2327
2374
  previous(guild) {
2328
2375
  return this.#getQueue(guild).previous();
@@ -2331,6 +2378,7 @@ ${e.message}`;
2331
2378
  * Shuffle the guild queue songs
2332
2379
  * @param guild - The type can be resolved to give a {@link Queue}
2333
2380
  * @returns The guild queue
2381
+ * @deprecated Use `distube.getQueue(guild).shuffle()` instead. Will be removed in v6.0.
2334
2382
  */
2335
2383
  shuffle(guild) {
2336
2384
  return this.#getQueue(guild).shuffle();
@@ -2341,6 +2389,7 @@ ${e.message}`;
2341
2389
  * @param guild - The type can be resolved to give a {@link Queue}
2342
2390
  * @param num - The song number to play
2343
2391
  * @returns The new Song will be played
2392
+ * @deprecated Use `distube.getQueue(guild).jump(num, options)` instead. Will be removed in v6.0.
2344
2393
  */
2345
2394
  jump(guild, num, options) {
2346
2395
  return this.#getQueue(guild).jump(num, options);
@@ -2351,6 +2400,7 @@ ${e.message}`;
2351
2400
  * @param guild - The type can be resolved to give a {@link Queue}
2352
2401
  * @param mode - The repeat modes (toggle if `undefined`)
2353
2402
  * @returns The new repeat mode
2403
+ * @deprecated Use `distube.getQueue(guild).setRepeatMode(mode)` instead. Will be removed in v6.0.
2354
2404
  */
2355
2405
  setRepeatMode(guild, mode) {
2356
2406
  return this.#getQueue(guild).setRepeatMode(mode);
@@ -2359,6 +2409,7 @@ ${e.message}`;
2359
2409
  * Toggle autoplay mode
2360
2410
  * @param guild - The type can be resolved to give a {@link Queue}
2361
2411
  * @returns Autoplay mode state
2412
+ * @deprecated Use `distube.getQueue(guild).toggleAutoplay()` instead. Will be removed in v6.0.
2362
2413
  */
2363
2414
  toggleAutoplay(guild) {
2364
2415
  const queue = this.#getQueue(guild);
@@ -2369,6 +2420,7 @@ ${e.message}`;
2369
2420
  * Add related song to the queue
2370
2421
  * @param guild - The type can be resolved to give a {@link Queue}
2371
2422
  * @returns The guild queue
2423
+ * @deprecated Use `distube.getQueue(guild).addRelatedSong()` instead. Will be removed in v6.0.
2372
2424
  */
2373
2425
  addRelatedSong(guild) {
2374
2426
  return this.#getQueue(guild).addRelatedSong();
@@ -2378,6 +2430,7 @@ ${e.message}`;
2378
2430
  * @param guild - The type can be resolved to give a {@link Queue}
2379
2431
  * @param time - Time in seconds
2380
2432
  * @returns Seeked queue
2433
+ * @deprecated Use `distube.getQueue(guild).seek(time)` instead. Will be removed in v6.0.
2381
2434
  */
2382
2435
  seek(guild, time) {
2383
2436
  return this.#getQueue(guild).seek(time);
@@ -2422,7 +2475,7 @@ var ExtractorPlugin = class extends Plugin {
2422
2475
  type = "extractor" /* EXTRACTOR */;
2423
2476
  };
2424
2477
 
2425
- // src/struct/InfoExtratorPlugin.ts
2478
+ // src/struct/InfoExtractorPlugin.ts
2426
2479
  var InfoExtractorPlugin = class extends Plugin {
2427
2480
  static {
2428
2481
  __name(this, "InfoExtractorPlugin");
@@ -2430,7 +2483,7 @@ var InfoExtractorPlugin = class extends Plugin {
2430
2483
  type = "info-extractor" /* INFO_EXTRACTOR */;
2431
2484
  };
2432
2485
 
2433
- // src/struct/PlayableExtratorPlugin.ts
2486
+ // src/struct/PlayableExtractorPlugin.ts
2434
2487
  var PlayableExtractorPlugin = class extends Plugin {
2435
2488
  static {
2436
2489
  __name(this, "PlayableExtractorPlugin");
@@ -2438,7 +2491,10 @@ var PlayableExtractorPlugin = class extends Plugin {
2438
2491
  type = "playable-extractor" /* PLAYABLE_EXTRACTOR */;
2439
2492
  };
2440
2493
  export {
2494
+ AUDIO_CHANNELS,
2495
+ AUDIO_SAMPLE_RATE,
2441
2496
  BaseManager,
2497
+ DEFAULT_VOLUME,
2442
2498
  DisTube,
2443
2499
  DisTubeBase,
2444
2500
  DisTubeError,
@@ -2450,14 +2506,21 @@ export {
2450
2506
  ExtractorPlugin,
2451
2507
  FilterManager,
2452
2508
  GuildIdManager,
2509
+ HTTP_REDIRECT_CODES,
2453
2510
  InfoExtractorPlugin,
2511
+ InfoExtractorPlugin as InfoExtratorPlugin,
2512
+ JOIN_TIMEOUT_MS,
2513
+ MAX_REDIRECT_DEPTH,
2454
2514
  Options,
2455
2515
  PlayableExtractorPlugin,
2516
+ PlayableExtractorPlugin as PlayableExtratorPlugin,
2456
2517
  Playlist,
2457
2518
  Plugin,
2458
2519
  PluginType,
2459
2520
  Queue,
2460
2521
  QueueManager,
2522
+ RECONNECT_MAX_ATTEMPTS,
2523
+ RECONNECT_TIMEOUT_MS,
2461
2524
  RepeatMode,
2462
2525
  Song,
2463
2526
  TaskQueue,