lavalink-client 1.0.5 → 1.0.7

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.
Files changed (33) hide show
  1. package/README.md +23 -1
  2. package/dist/cjs/structures/Filters.d.ts +5 -3
  3. package/dist/cjs/structures/LavalinkManager.d.ts +18 -10
  4. package/dist/cjs/structures/LavalinkManager.js +1 -1
  5. package/dist/cjs/structures/Node.d.ts +3 -3
  6. package/dist/cjs/structures/Node.js +13 -8
  7. package/dist/cjs/structures/Player.d.ts +23 -18
  8. package/dist/cjs/structures/Player.js +1 -0
  9. package/dist/cjs/structures/Queue.d.ts +23 -18
  10. package/dist/cjs/structures/Queue.js +6 -21
  11. package/dist/cjs/structures/Utils.d.ts +2 -2
  12. package/dist/cjs/structures/Utils.js +58 -6
  13. package/dist/esm/structures/Filters.d.ts +5 -3
  14. package/dist/esm/structures/LavalinkManager.d.ts +18 -10
  15. package/dist/esm/structures/LavalinkManager.js +1 -1
  16. package/dist/esm/structures/Node.d.ts +3 -3
  17. package/dist/esm/structures/Node.js +13 -8
  18. package/dist/esm/structures/Player.d.ts +23 -18
  19. package/dist/esm/structures/Player.js +1 -0
  20. package/dist/esm/structures/Queue.d.ts +23 -18
  21. package/dist/esm/structures/Queue.js +6 -20
  22. package/dist/esm/structures/Utils.d.ts +2 -2
  23. package/dist/esm/structures/Utils.js +58 -6
  24. package/dist/structures/LavalinkManager.d.ts +5 -5
  25. package/dist/structures/Queue.d.ts +7 -7
  26. package/dist/structures/QueueManager.d.ts +6 -6
  27. package/dist/types/structures/Filters.d.ts +5 -3
  28. package/dist/types/structures/LavalinkManager.d.ts +18 -10
  29. package/dist/types/structures/Node.d.ts +3 -3
  30. package/dist/types/structures/Player.d.ts +23 -18
  31. package/dist/types/structures/Queue.d.ts +23 -18
  32. package/dist/types/structures/Utils.d.ts +2 -2
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Lavalink Client
2
- Easy and advanced lavalink client. Use it with lavalink plugins as well as latest lavalink versions.
2
+ Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.
3
+
4
+ <div align="center">
5
+ <p>
6
+ <img src="https://madewithlove.now.sh/at?heart=true&template=for-the-badge" alt="Made with love in Austria">
7
+ <img alt="Made with TypeScript" src="https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white">
8
+ </p>
9
+ <p>
10
+ <a href="https://www.npmjs.com/package/lavalink-client">
11
+ <img src="https://img.shields.io/npm/v/lavalink-client.svg?maxAge=3600&style=for-the-badge&logo=npm&logoColor=red" alt="NPM version" />
12
+ </a>
13
+ <a href="https://www.npmjs.com/package/lavalink-client">
14
+ <img src="https://img.shields.io/npm/dt/lavalink-client.svg?maxAge=3600&style=for-the-badge&logo=npm&logoColor=red" alt="NPM downloads" />
15
+ </a>
16
+ <a href="https://lc4.gitbook.io/lavalink-client/">
17
+ <img src="https://img.shields.io/badge/Documation-%230288D1.svg?style=for-the-badge&logo=gitbook&logoColor=white" alt="Get Started Now">
18
+ </a>
19
+ </p>
20
+ <p>
21
+ <a href="https://www.npmjs.com/package/lavalink-client"><img src="https://nodei.co/npm/lavalink-client.png?downloads=true&stars=true" alt="npm install lavalink-client" /></a>
22
+ </p>
23
+ </div>
3
24
 
4
25
  # Install
5
26
 
@@ -11,6 +32,7 @@ Latest stable Version: (currently, unreleased)
11
32
  npm install --save lavalink-client
12
33
  ```
13
34
 
35
+
14
36
  Dev Version: (Current)
15
37
 
16
38
  ```bash
@@ -7,7 +7,7 @@ export declare class FilterManager {
7
7
  equalizerBands: EQBand[];
8
8
  filterUpdatedState: number;
9
9
  filters: PlayerFilters;
10
- data: LavalinkFilterData;
10
+ data: FilterData;
11
11
  constructor(player: Player);
12
12
  applyPlayerFilters(): Promise<void>;
13
13
  /**
@@ -215,9 +215,8 @@ export interface ReverbFilter {
215
215
  delay: number;
216
216
  decay: number;
217
217
  }
218
- export interface LavalinkFilterData {
218
+ export interface FilterData {
219
219
  volume?: number;
220
- equalizer?: EQBand[];
221
220
  karaoke?: KaraokeFilter;
222
221
  timescale?: TimescaleFilter;
223
222
  tremolo?: FreqFilter;
@@ -229,3 +228,6 @@ export interface LavalinkFilterData {
229
228
  echo: EchoFilter;
230
229
  reverb: ReverbFilter;
231
230
  }
231
+ export interface LavalinkFilterData extends FilterData {
232
+ equalizer?: EQBand[];
233
+ }
@@ -1,22 +1,24 @@
1
1
  /// <reference types="node" />
2
2
  import { EventEmitter } from "events";
3
3
  import { NodeManager } from "./NodeManager";
4
- import { QueueSaverOptions } from "./Queue";
5
- import { GuildShardPayload, LavalinkSearchPlatform, ManagerUitls, MiniMap, SearchPlatform, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, VoicePacket, VoiceServer, VoiceState, WebSocketClosedEvent } from "./Utils";
4
+ import { ManagerQueueOptions } from "./Queue";
5
+ import { GuildShardPayload, ManagerUitls, MiniMap, SearchPlatform, TrackEndEvent, TrackExceptionEvent, TrackStartEvent, TrackStuckEvent, VoicePacket, VoiceServer, VoiceState, WebSocketClosedEvent } from "./Utils";
6
6
  import { LavalinkNodeOptions } from "./Node";
7
- import { DestroyReasonsType, Player, PlayerOptions } from "./Player";
7
+ import { DestroyReasonsType, Player, PlayerJson, PlayerOptions } from "./Player";
8
8
  import { Track, UnresolvedTrack } from "./Track";
9
9
  export interface LavalinkManager {
10
10
  nodeManager: NodeManager;
11
11
  utils: ManagerUitls;
12
12
  }
13
13
  export interface BotClientOptions {
14
+ /** Bot Client Id */
14
15
  id: string;
16
+ /** Bot Client Username */
15
17
  username?: string;
16
18
  /** So users can pass entire objects / classes */
17
19
  [x: string | number | symbol | undefined]: any;
18
20
  }
19
- export interface LavalinkPlayerOptions {
21
+ export interface ManagerPlayerOptions {
20
22
  /** If the Lavalink Volume should be decremented by x number */
21
23
  volumeDecrementer?: number;
22
24
  /** How often it should update the the player Position */
@@ -39,15 +41,21 @@ export interface LavalinkPlayerOptions {
39
41
  autoPlayFunction?: (player: Player, lastPlayedTrack: Track) => Promise<void>;
40
42
  destroyAfterMs?: number;
41
43
  };
44
+ useUnresolvedData?: boolean;
42
45
  }
43
46
  export interface ManagerOptions {
47
+ /** The Node Options, for all Nodes! (on init) */
44
48
  nodes: LavalinkNodeOptions[];
45
- queueOptions?: QueueSaverOptions;
49
+ /** @async The Function to send the voice connection changes from Lavalink to Discord */
50
+ sendToShard: (guildId: string, payload: GuildShardPayload) => void;
51
+ /** The Bot Client's Data for Authorization */
46
52
  client?: BotClientOptions;
47
- playerOptions?: LavalinkPlayerOptions;
53
+ /** QueueOptions for all Queues */
54
+ queueOptions?: ManagerQueueOptions;
55
+ /** PlayerOptions for all Players */
56
+ playerOptions?: ManagerPlayerOptions;
57
+ /** If it should skip to the next Track on TrackEnd / TrackError etc. events */
48
58
  autoSkip?: boolean;
49
- /** @async */
50
- sendToShard: (guildId: string, payload: GuildShardPayload) => void;
51
59
  }
52
60
  interface LavalinkManagerEvents {
53
61
  /**
@@ -104,7 +112,7 @@ interface LavalinkManagerEvents {
104
112
  * Always emits when the player (on lavalink side) got updated
105
113
  * @event Manager#playerUpdate
106
114
  */
107
- "playerUpdate": (player: Player) => void;
115
+ "playerUpdate": (oldPlayerJson: PlayerJson, newPlayer: Player) => void;
108
116
  }
109
117
  export interface LavalinkManager {
110
118
  options: ManagerOptions;
@@ -112,7 +120,7 @@ export interface LavalinkManager {
112
120
  emit<U extends keyof LavalinkManagerEvents>(event: U, ...args: Parameters<LavalinkManagerEvents[U]>): boolean;
113
121
  }
114
122
  export declare class LavalinkManager extends EventEmitter {
115
- static DefaultSources: Record<SearchPlatform, LavalinkSearchPlatform>;
123
+ static DefaultSources: Record<SearchPlatform, import("./Utils").LavalinkSearchPlatform>;
116
124
  static SourceLinksRegexes: Record<import("./Utils").SourcesRegex, RegExp>;
117
125
  initiated: boolean;
118
126
  readonly players: MiniMap<string, Player>;
@@ -81,7 +81,7 @@ class LavalinkManager extends events_1.EventEmitter {
81
81
  const keys = Object.getOwnPropertyNames(Object.getPrototypeOf(options.queueOptions?.queueChangesWatcher));
82
82
  const requiredKeys = ["tracksAdd", "tracksRemoved", "shuffled"];
83
83
  if (!requiredKeys.every(v => keys.includes(v)) || !requiredKeys.every(v => typeof options.queueOptions?.queueChangesWatcher[v] === "function"))
84
- throw new SyntaxError(`The provided ManagerOption.QueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`);
84
+ throw new SyntaxError(`The provided ManagerOption.DefaultQueueChangesWatcher, does not have all required functions: ${requiredKeys.join(", ")}`);
85
85
  }
86
86
  }
87
87
  constructor(options) {
@@ -4,7 +4,7 @@ import { NodeManager } from "./NodeManager";
4
4
  import internal from "stream";
5
5
  import { InvalidLavalinkRestRequest, LavalinkPlayer, PlayerUpdateInfo, RoutePlanner, Session, Base64 } from "./Utils";
6
6
  import { DestroyReasonsType } from "./Player";
7
- import { TrackInfo } from "./Track";
7
+ import { Track } from "./Track";
8
8
  /** Modifies any outgoing REST requests. */
9
9
  export type ModifyRequest = (options: Dispatcher.RequestOptions) => void;
10
10
  export interface LavalinkNodeOptions {
@@ -185,13 +185,13 @@ export declare class LavalinkNode {
185
185
  * @param encoded
186
186
  * @returns
187
187
  */
188
- singleTrack: (encoded: Base64) => Promise<TrackInfo>;
188
+ singleTrack: (encoded: Base64, requester: unknown) => Promise<Track>;
189
189
  /**
190
190
  *
191
191
  * @param encodeds Decodes multiple tracks into their info
192
192
  * @returns
193
193
  */
194
- multipleTracks: (encodeds: Base64[]) => Promise<TrackInfo[]>;
194
+ multipleTracks: (encodeds: Base64[], requester: unknown) => Promise<Track[]>;
195
195
  };
196
196
  /**
197
197
  * Request Lavalink statistics.
@@ -227,25 +227,27 @@ class LavalinkNode {
227
227
  * @param encoded
228
228
  * @returns
229
229
  */
230
- singleTrack: async (encoded) => {
230
+ singleTrack: async (encoded, requester) => {
231
231
  if (!encoded)
232
232
  throw new SyntaxError("No encoded (Base64 string) was provided");
233
- return await this.request(`/decodetrack?encodedTrack=${encoded}`);
233
+ // return the decoded + builded track
234
+ return this.NodeManager.LavalinkManager.utils.buildTrack(await this.request(`/decodetrack?encodedTrack=${encoded}`), requester);
234
235
  },
235
236
  /**
236
237
  *
237
238
  * @param encodeds Decodes multiple tracks into their info
238
239
  * @returns
239
240
  */
240
- multipleTracks: async (encodeds) => {
241
+ multipleTracks: async (encodeds, requester) => {
241
242
  if (!Array.isArray(encodeds) || !encodeds.every(v => typeof v === "string" && v.length > 1))
242
243
  throw new SyntaxError("You need to provide encodeds, which is an array of base64 strings");
244
+ // return the decoded + builded tracks
243
245
  return await this.request(`/decodetracks`, r => {
244
246
  r.method = "POST";
245
247
  r.body = JSON.stringify(encodeds);
246
248
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
247
249
  r.headers["Content-Type"] = "application/json";
248
- });
250
+ }).then((r) => r.map(track => this.NodeManager.LavalinkManager.utils.buildTrack(track, requester)));
249
251
  }
250
252
  };
251
253
  /**
@@ -356,7 +358,7 @@ class LavalinkNode {
356
358
  if (data.playerOptions.filters.volume)
357
359
  player.filterManager.data.volume = data.playerOptions.filters.volume;
358
360
  if (data.playerOptions.filters.equalizer)
359
- player.filterManager.data.equalizer = data.playerOptions.filters.equalizer;
361
+ player.filterManager.equalizerBands = data.playerOptions.filters.equalizer;
360
362
  if (data.playerOptions.filters.karaoke)
361
363
  player.filterManager.data.karaoke = data.playerOptions.filters.karaoke;
362
364
  if (data.playerOptions.filters.lowPass)
@@ -439,6 +441,7 @@ class LavalinkNode {
439
441
  const player = this.NodeManager.LavalinkManager.getPlayer(payload.guildId);
440
442
  if (!player)
441
443
  return;
444
+ const oldPlayer = player?.toJSON();
442
445
  if (player.get("internal_updateInterval"))
443
446
  clearInterval(player.get("internal_updateInterval"));
444
447
  // override the position
@@ -476,7 +479,7 @@ class LavalinkNode {
476
479
  player.filterManager.filterUpdatedState = 0;
477
480
  }
478
481
  }
479
- this.NodeManager.LavalinkManager.emit("playerUpdate", player);
482
+ this.NodeManager.LavalinkManager.emit("playerUpdate", oldPlayer, player);
480
483
  break;
481
484
  case "event":
482
485
  this.handleEvent(payload);
@@ -523,7 +526,6 @@ class LavalinkNode {
523
526
  return this.NodeManager.LavalinkManager.emit("trackStart", player, track, payload);
524
527
  }
525
528
  async trackEnd(player, track, payload) {
526
- console.log(payload.reason);
527
529
  // If there are no songs in the queue
528
530
  if (!player.queue.tracks.length && player.repeatMode === "off")
529
531
  return this.queueEnd(player, track, payload);
@@ -560,8 +562,11 @@ class LavalinkNode {
560
562
  await this.NodeManager.LavalinkManager.options?.playerOptions?.onEmptyQueue?.autoPlayFunction(player, track);
561
563
  if (player.queue.tracks.length > 0)
562
564
  await (0, Utils_1.queueTrackEnd)(player);
563
- if (player.queue.current)
565
+ if (player.queue.current) {
566
+ if (payload.type === "TrackEndEvent")
567
+ this.NodeManager.LavalinkManager.emit("trackEnd", player, track, payload);
564
568
  return player.play({ noReplace: true, paused: false });
569
+ }
565
570
  }
566
571
  player.queue.previous.unshift(track);
567
572
  if (payload?.reason !== "stopped") {
@@ -1,4 +1,4 @@
1
- import { FilterManager, LavalinkFilterData } from "./Filters";
1
+ import { EQBand, FilterData, FilterManager, LavalinkFilterData } from "./Filters";
2
2
  import { LavalinkManager } from "./LavalinkManager";
3
3
  import { LavalinkNode } from "./Node";
4
4
  import { Queue } from "./Queue";
@@ -7,6 +7,27 @@ import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult } from "./Util
7
7
  type PlayerDestroyReasons = "QueueEmpty" | "NodeDestroy" | "NodeDeleted" | "LavalinkNoVoice" | "NodeReconnectFail" | "PlayerReconnectFail" | "Disconnected" | "ChannelDeleted";
8
8
  export type DestroyReasonsType = PlayerDestroyReasons | string;
9
9
  export declare const DestroyReasons: Record<PlayerDestroyReasons, PlayerDestroyReasons>;
10
+ export interface PlayerJson {
11
+ guildId: string;
12
+ options: PlayerOptions;
13
+ voiceChannelId: string;
14
+ textChannelId?: string;
15
+ position: number;
16
+ lastPosition: number;
17
+ volume: number;
18
+ lavalinkVolume: number;
19
+ repeatMode: RepeatMode;
20
+ paused: boolean;
21
+ playing: boolean;
22
+ createdTimeStamp?: number;
23
+ filters: FilterData;
24
+ ping: {
25
+ ws: number;
26
+ lavalink: number;
27
+ };
28
+ equalizer: EQBand[];
29
+ nodeId?: string;
30
+ }
10
31
  export type RepeatMode = "queue" | "track" | "off";
11
32
  export interface PlayerOptions {
12
33
  guildId: string;
@@ -170,22 +191,6 @@ export declare class Player {
170
191
  */
171
192
  changeNode(newNode: LavalinkNode | string): Promise<string>;
172
193
  /** Converts the Player including Queue to a Json state */
173
- toJSON(): {
174
- guildId: string;
175
- options: PlayerOptions;
176
- voiceChannelId: string;
177
- textChannelId: string;
178
- position: number;
179
- lastPosition: number;
180
- volume: number;
181
- lavalinkVolume: number;
182
- repeatMode: RepeatMode;
183
- paused: boolean;
184
- playing: boolean;
185
- createdTimeStamp: number;
186
- filters: {};
187
- equalizer: import("./Filters").EQBand[];
188
- nodeId: string;
189
- };
194
+ toJSON(): PlayerJson;
190
195
  }
191
196
  export {};
@@ -405,6 +405,7 @@ class Player {
405
405
  filters: this.filterManager?.data || {},
406
406
  equalizer: this.filterManager?.equalizerBands || [],
407
407
  nodeId: this.node?.id,
408
+ ping: this.ping,
408
409
  };
409
410
  }
410
411
  }
@@ -1,53 +1,58 @@
1
1
  import { Track, UnresolvedTrack } from "./Track";
2
+ import { MiniMap } from "./Utils";
2
3
  export interface StoredQueue {
3
4
  current: Track | null;
4
5
  previous: Track[];
5
6
  tracks: Track[];
6
7
  }
7
- export interface StoreManager extends Record<any, any> {
8
+ export interface QueueStoreManager extends Record<string, any> {
8
9
  /** @async get a Value (MUST RETURN UNPARSED!) */
9
10
  get: (guildId: unknown) => Promise<any>;
10
11
  /** @async Set a value inside a guildId (MUST BE UNPARSED) */
11
12
  set: (guildId: unknown, value: unknown) => Promise<any>;
12
13
  /** @async Delete a Database Value based of it's guildId */
13
14
  delete: (guildId: unknown) => Promise<any>;
14
- /** @async Transform the value(s) inside of the StoreManager (IF YOU DON'T NEED PARSING/STRINGIFY, then just return the value) */
15
+ /** @async Transform the value(s) inside of the QueueStoreManager (IF YOU DON'T NEED PARSING/STRINGIFY, then just return the value) */
15
16
  stringify: (value: unknown) => Promise<any>;
16
17
  /** @async Parse the saved value back to the Queue (IF YOU DON'T NEED PARSING/STRINGIFY, then just return the value) */
17
18
  parse: (value: unknown) => Promise<Partial<StoredQueue>>;
18
19
  }
19
- export interface QueueSaverOptions {
20
- maxPreviousTracks: number;
21
- queueStore?: StoreManager;
20
+ export interface ManagerQueueOptions {
21
+ maxPreviousTracks?: number;
22
+ queueStore?: QueueStoreManager;
22
23
  queueChangesWatcher?: QueueChangesWatcher;
23
24
  }
24
25
  export interface QueueSaver {
25
26
  /** @private */
26
- _: StoreManager;
27
+ _: QueueStoreManager;
27
28
  /** @private */
28
- options: QueueSaverOptions;
29
+ options: {
30
+ maxPreviousTracks: number;
31
+ };
29
32
  }
30
33
  export declare class QueueSaver {
31
- constructor(options: QueueSaverOptions);
34
+ constructor(options: ManagerQueueOptions);
32
35
  get(guildId: string): Promise<Partial<StoredQueue>>;
33
36
  delete(guildId: string): Promise<any>;
34
37
  set(guildId: string, value: any): Promise<any>;
35
38
  sync(guildId: string): Promise<Partial<StoredQueue>>;
36
39
  }
37
- export declare class DefaultQueueStore {
40
+ export declare class DefaultQueueStore implements QueueStoreManager {
38
41
  private data;
39
42
  constructor();
40
- get(guildId: any): Promise<any>;
41
- set(guildId: any, stringifiedValue: any): Promise<Map<any, any>>;
43
+ get(guildId: any): Promise<unknown>;
44
+ set(guildId: any, stringifiedValue: any): Promise<MiniMap<unknown, unknown>>;
42
45
  delete(guildId: any): Promise<boolean>;
43
46
  stringify(value: any): Promise<any>;
44
47
  parse(value: any): Promise<Partial<StoredQueue>>;
45
48
  }
46
- export declare class QueueChangesWatcher {
47
- constructor();
48
- tracksAdd(guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue): void;
49
- tracksRemoved(guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue): void;
50
- shuffled(guildId: string, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue): void;
49
+ export interface QueueChangesWatcher {
50
+ /** get a Value (MUST RETURN UNPARSED!) */
51
+ tracksAdd: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => any;
52
+ /** Set a value inside a guildId (MUST BE UNPARSED) */
53
+ tracksRemoved: (guildId: string, tracks: (Track | UnresolvedTrack)[], position: number, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => any;
54
+ /** Set a value inside a guildId (MUST BE UNPARSED) */
55
+ shuffled: (guildId: string, oldStoredQueue: StoredQueue, newStoredQueue: StoredQueue) => any;
51
56
  }
52
57
  export declare class Queue {
53
58
  readonly tracks: (Track | UnresolvedTrack)[];
@@ -60,7 +65,7 @@ export declare class Queue {
60
65
  private readonly QueueSaver;
61
66
  private managerUtils;
62
67
  private queueChanges;
63
- constructor(guildId: string, data?: Partial<StoredQueue>, QueueSaver?: QueueSaver, queueOptions?: QueueSaverOptions);
68
+ constructor(guildId: string, data?: Partial<StoredQueue>, QueueSaver?: QueueSaver, queueOptions?: ManagerQueueOptions);
64
69
  private applyData;
65
70
  /**
66
71
  * Utils for a Queue
@@ -77,7 +82,7 @@ export declare class Queue {
77
82
  sync: (override?: boolean, dontSyncCurrent?: boolean) => Promise<void>;
78
83
  destroy: () => Promise<any>;
79
84
  /**
80
- * @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the storeManager
85
+ * @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the QueueStoreManager
81
86
  */
82
87
  toJSON: () => StoredQueue;
83
88
  /**
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Queue = exports.QueueChangesWatcher = exports.DefaultQueueStore = exports.QueueSaver = void 0;
3
+ exports.Queue = exports.DefaultQueueStore = exports.QueueSaver = void 0;
4
4
  const Utils_1 = require("./Utils");
5
5
  class QueueSaver {
6
6
  constructor(options) {
7
- this._ = options.queueStore || new DefaultQueueStore();
7
+ this._ = options?.queueStore || new DefaultQueueStore();
8
8
  this.options = {
9
- maxPreviousTracks: options.maxPreviousTracks
9
+ maxPreviousTracks: options?.maxPreviousTracks || 25,
10
10
  };
11
11
  }
12
12
  async get(guildId) {
@@ -24,9 +24,8 @@ class QueueSaver {
24
24
  }
25
25
  exports.QueueSaver = QueueSaver;
26
26
  class DefaultQueueStore {
27
- data = new Map();
28
- constructor() {
29
- }
27
+ data = new Utils_1.MiniMap();
28
+ constructor() { }
30
29
  async get(guildId) {
31
30
  return await this.data.get(guildId);
32
31
  }
@@ -44,20 +43,6 @@ class DefaultQueueStore {
44
43
  }
45
44
  }
46
45
  exports.DefaultQueueStore = DefaultQueueStore;
47
- class QueueChangesWatcher {
48
- constructor() {
49
- }
50
- tracksAdd(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
51
- return;
52
- }
53
- tracksRemoved(guildId, tracks, position, oldStoredQueue, newStoredQueue) {
54
- return;
55
- }
56
- shuffled(guildId, oldStoredQueue, newStoredQueue) {
57
- return;
58
- }
59
- }
60
- exports.QueueChangesWatcher = QueueChangesWatcher;
61
46
  class Queue {
62
47
  tracks = [];
63
48
  previous = [];
@@ -111,7 +96,7 @@ class Queue {
111
96
  return await this.QueueSaver.delete(this.guildId);
112
97
  },
113
98
  /**
114
- * @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the storeManager
99
+ * @returns {{current:Track|null, previous:Track[], tracks:Track[]}}The Queue, but in a raw State, which allows easier handling for the QueueStoreManager
115
100
  */
116
101
  toJSON: () => {
117
102
  if (this.previous.length > this.options.maxPreviousTracks)
@@ -7,8 +7,8 @@ export declare const TrackSymbol: unique symbol;
7
7
  export declare const UnresolvedTrackSymbol: unique symbol;
8
8
  export declare const QueueSymbol: unique symbol;
9
9
  export declare const NodeSymbol: unique symbol;
10
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "sprec" | "ymsearch" | "speak" | "tts" | "ftts";
11
- export type ClientSearchPlatform = "youtube" | "yt" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "yandex music" | "sp" | "sprec" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandexmusic";
10
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch" | "speak" | "tts" | "ftts";
11
+ export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
12
12
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
13
13
  export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
14
14
  export interface PlaylistInfo {
@@ -6,6 +6,8 @@ exports.TrackSymbol = Symbol("LC-Track");
6
6
  exports.UnresolvedTrackSymbol = Symbol("LC-Track-Unresolved");
7
7
  exports.QueueSymbol = Symbol("LC-Queue");
8
8
  exports.NodeSymbol = Symbol("LC-Node");
9
+ /** @hidden */
10
+ const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9
11
  class ManagerUitls {
10
12
  constructor(LavalinkManager) {
11
13
  this.manager = LavalinkManager;
@@ -23,7 +25,7 @@ class ManagerUitls {
23
25
  identifier: data.info?.identifier,
24
26
  title: data.info?.title,
25
27
  author: data.info?.author,
26
- duration: data.info?.length,
28
+ duration: data.info?.length || data.info?.duration,
27
29
  artworkUrl: data.info?.artworkUrl || data.pluginInfo?.artworkUrl || data.plugin?.artworkUrl,
28
30
  uri: data.info?.uri,
29
31
  sourceName: data.info?.sourceName,
@@ -95,13 +97,21 @@ class ManagerUitls {
95
97
  * @returns
96
98
  */
97
99
  isTrack(data) {
98
- return typeof data?.encoded === "string" && typeof data?.info === "object";
100
+ if (!data)
101
+ return false;
102
+ if (data[exports.TrackSymbol] === true)
103
+ return true;
104
+ return typeof data?.encoded === "string" && typeof data?.info === "object" && !("resolve" in data);
99
105
  }
100
106
  /**
101
107
  * Checks if the provided argument is a valid UnresolvedTrack.
102
108
  * @param track
103
109
  */
104
110
  isUnresolvedTrack(data) {
111
+ if (!data)
112
+ return false;
113
+ if (data[exports.UnresolvedTrackSymbol] === true)
114
+ return true;
105
115
  return typeof data === "object" && "info" in data && typeof data.info.title === "string" && typeof data.resolve === "function";
106
116
  }
107
117
  /**
@@ -277,6 +287,32 @@ async function queueTrackEnd(player) {
277
287
  return player.queue.current;
278
288
  }
279
289
  exports.queueTrackEnd = queueTrackEnd;
290
+ async function applyUnresolvedData(resTrack, data, utils) {
291
+ if (!resTrack?.info || !data?.info)
292
+ return;
293
+ if (utils.manager.options.playerOptions?.useUnresolvedData === true) { // overwrite values
294
+ if (data.info.uri)
295
+ resTrack.info.uri = data.info.uri;
296
+ if (data.info.artworkUrl?.length)
297
+ resTrack.info.artworkUrl = data.info.artworkUrl;
298
+ if (data.info.title?.length)
299
+ resTrack.info.title = data.info.title;
300
+ if (data.info.author?.length)
301
+ resTrack.info.author = data.info.author;
302
+ }
303
+ else { // only overwrite if undefined / invalid
304
+ if ((resTrack.info.title == 'Unknown title' || resTrack.info.title == "Unspecified description") && resTrack.info.title != data.info.title)
305
+ resTrack.info.title = data.info.title;
306
+ if (resTrack.info.author != data.info.author)
307
+ resTrack.info.author = data.info.author;
308
+ if (resTrack.info.artworkUrl != data.info.artworkUrl)
309
+ resTrack.info.artworkUrl = data.info.artworkUrl;
310
+ }
311
+ for (const key of Object.keys(data.info))
312
+ if (typeof resTrack.info[key] === "undefined" && key !== "resolve" && data.info[key])
313
+ resTrack.info[key] = data.info[key]; // add non-existing values
314
+ return resTrack;
315
+ }
280
316
  async function getClosestTrack(data, player, utils) {
281
317
  if (!player || !player.node)
282
318
  throw new RangeError("No player with a lavalink node was provided");
@@ -288,19 +324,35 @@ async function getClosestTrack(data, player, utils) {
288
324
  throw new SyntaxError("the track title is required for unresolved tracks");
289
325
  if (!data.requester)
290
326
  throw new SyntaxError("The requester is required");
327
+ // try to decode the track, if possible
291
328
  if (typeof data.encoded === "string") {
292
- const r = await player.node.decode.singleTrack(data.encoded);
329
+ const r = await player.node.decode.singleTrack(data.encoded, data.requester);
293
330
  if (r)
294
- return utils.buildTrack(r, data.requester);
331
+ return applyUnresolvedData(r, data, utils);
295
332
  }
333
+ // try to fetch the track via a uri if possible
296
334
  if (typeof data.info.uri === "string") {
297
335
  const r = await player.search({ query: data?.info?.uri }, data.requester).then(v => v.tracks[0]);
298
336
  if (r)
299
- return r;
337
+ return applyUnresolvedData(r, data, utils);
300
338
  }
339
+ // search the track as closely as possible
301
340
  const query = [data.info?.title, data.info?.author].filter(str => !!str).join(" by ");
302
341
  const sourceName = data.info?.sourceName;
303
342
  return await player.search({
304
343
  query, source: sourceName !== "bandcamp" && sourceName !== "twitch" && sourceName !== "flowery-tts" ? sourceName : player.LavalinkManager.options?.playerOptions?.defaultSearchPlatform,
305
- }, data.requester).then(v => v.tracks[0]);
344
+ }, data.requester).then(res => {
345
+ let trackToUse = null;
346
+ // try to find via author name
347
+ if (data.info.author && !trackToUse)
348
+ trackToUse = res.tracks.find(track => [data.info.author, `${data.info.author} - Topic`].some(name => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info.author)) || new RegExp(`^${escapeRegExp(data.info.title)}$`, "i").test(track.info.title));
349
+ // try to find via duration
350
+ if (data.info.duration && !trackToUse)
351
+ trackToUse = res.tracks.find(track => (track.info.duration >= (data.info.duration - 1500)) && (track.info.duration <= (data.info.duration + 1500)));
352
+ // try to find via isrc
353
+ if (data.info.isrc && !trackToUse)
354
+ trackToUse = res.tracks.find(track => track.info.isrc === data.info.isrc);
355
+ // apply unresolved data and return the track
356
+ return applyUnresolvedData(trackToUse || res.tracks[0], data, utils);
357
+ });
306
358
  }
@@ -7,7 +7,7 @@ export declare class FilterManager {
7
7
  equalizerBands: EQBand[];
8
8
  filterUpdatedState: number;
9
9
  filters: PlayerFilters;
10
- data: LavalinkFilterData;
10
+ data: FilterData;
11
11
  constructor(player: Player);
12
12
  applyPlayerFilters(): Promise<void>;
13
13
  /**
@@ -215,9 +215,8 @@ export interface ReverbFilter {
215
215
  delay: number;
216
216
  decay: number;
217
217
  }
218
- export interface LavalinkFilterData {
218
+ export interface FilterData {
219
219
  volume?: number;
220
- equalizer?: EQBand[];
221
220
  karaoke?: KaraokeFilter;
222
221
  timescale?: TimescaleFilter;
223
222
  tremolo?: FreqFilter;
@@ -229,3 +228,6 @@ export interface LavalinkFilterData {
229
228
  echo: EchoFilter;
230
229
  reverb: ReverbFilter;
231
230
  }
231
+ export interface LavalinkFilterData extends FilterData {
232
+ equalizer?: EQBand[];
233
+ }