lavalink-client 1.0.0 → 1.0.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.
Files changed (76) hide show
  1. package/README.md +103 -152
  2. package/dist/cjs/index.d.ts +9 -0
  3. package/dist/cjs/index.js +12 -0
  4. package/dist/cjs/structures/Filters.d.ts +231 -0
  5. package/dist/cjs/structures/Filters.js +481 -0
  6. package/dist/cjs/structures/LavalinkManager.d.ts +124 -0
  7. package/dist/cjs/structures/LavalinkManager.js +168 -0
  8. package/dist/cjs/structures/LavalinkManagerStatics.d.ts +3 -0
  9. package/dist/cjs/structures/LavalinkManagerStatics.js +84 -0
  10. package/dist/cjs/structures/Node.d.ts +245 -0
  11. package/dist/cjs/structures/Node.js +602 -0
  12. package/dist/cjs/structures/NodeManager.d.ts +61 -0
  13. package/dist/cjs/structures/NodeManager.js +35 -0
  14. package/dist/cjs/structures/Player.d.ts +191 -0
  15. package/dist/cjs/structures/Player.js +395 -0
  16. package/dist/cjs/structures/Queue.d.ts +107 -0
  17. package/dist/cjs/structures/Queue.js +215 -0
  18. package/dist/cjs/structures/Track.d.ts +47 -0
  19. package/dist/cjs/structures/Track.js +2 -0
  20. package/dist/cjs/structures/Utils.d.ts +258 -0
  21. package/dist/cjs/structures/Utils.js +179 -0
  22. package/dist/esm/index.d.ts +9 -0
  23. package/dist/esm/index.js +9 -0
  24. package/dist/esm/structures/Filters.d.ts +231 -0
  25. package/dist/esm/structures/Filters.js +477 -0
  26. package/dist/esm/structures/LavalinkManager.d.ts +124 -0
  27. package/dist/esm/structures/LavalinkManager.js +164 -0
  28. package/dist/esm/structures/LavalinkManagerStatics.d.ts +3 -0
  29. package/dist/esm/structures/LavalinkManagerStatics.js +81 -0
  30. package/dist/esm/structures/Node.d.ts +245 -0
  31. package/dist/esm/structures/Node.js +597 -0
  32. package/dist/esm/structures/NodeManager.d.ts +61 -0
  33. package/dist/esm/structures/NodeManager.js +31 -0
  34. package/dist/esm/structures/Player.d.ts +191 -0
  35. package/dist/esm/structures/Player.js +391 -0
  36. package/dist/esm/structures/Queue.d.ts +107 -0
  37. package/dist/esm/structures/Queue.js +208 -0
  38. package/dist/esm/structures/Track.d.ts +47 -0
  39. package/dist/esm/structures/Track.js +1 -0
  40. package/dist/esm/structures/Utils.d.ts +258 -0
  41. package/dist/esm/structures/Utils.js +173 -0
  42. package/dist/index.d.ts +10 -0
  43. package/dist/index.js +13 -0
  44. package/dist/structures/Filters.d.ts +230 -0
  45. package/dist/structures/Filters.js +472 -0
  46. package/dist/structures/LavalinkManager.d.ts +47 -0
  47. package/dist/structures/LavalinkManager.js +36 -0
  48. package/dist/structures/LavalinkManagerStatics.d.ts +3 -0
  49. package/dist/structures/LavalinkManagerStatics.js +76 -0
  50. package/dist/structures/Node.d.ts +171 -0
  51. package/dist/structures/Node.js +462 -0
  52. package/dist/structures/NodeManager.d.ts +58 -0
  53. package/dist/structures/NodeManager.js +25 -0
  54. package/dist/structures/Player.d.ts +101 -0
  55. package/dist/structures/Player.js +232 -0
  56. package/dist/structures/PlayerManager.d.ts +62 -0
  57. package/dist/structures/PlayerManager.js +26 -0
  58. package/dist/structures/Queue.d.ts +93 -0
  59. package/dist/structures/Queue.js +160 -0
  60. package/dist/structures/QueueManager.d.ts +77 -0
  61. package/dist/structures/QueueManager.js +74 -0
  62. package/dist/structures/Track.d.ts +27 -0
  63. package/dist/structures/Track.js +2 -0
  64. package/dist/structures/Utils.d.ts +183 -0
  65. package/dist/structures/Utils.js +43 -0
  66. package/dist/types/index.d.ts +9 -0
  67. package/dist/types/structures/Filters.d.ts +231 -0
  68. package/dist/types/structures/LavalinkManager.d.ts +124 -0
  69. package/dist/types/structures/LavalinkManagerStatics.d.ts +3 -0
  70. package/dist/types/structures/Node.d.ts +245 -0
  71. package/dist/types/structures/NodeManager.d.ts +61 -0
  72. package/dist/types/structures/Player.d.ts +191 -0
  73. package/dist/types/structures/Queue.d.ts +107 -0
  74. package/dist/types/structures/Track.d.ts +47 -0
  75. package/dist/types/structures/Utils.d.ts +258 -0
  76. package/package.json +63 -26
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NodeManager = void 0;
4
+ const stream_1 = require("stream");
5
+ const Node_1 = require("./Node");
6
+ const Utils_1 = require("./Utils");
7
+ const Player_1 = require("./Player");
8
+ class NodeManager extends stream_1.EventEmitter {
9
+ nodes = new Utils_1.MiniMap();
10
+ constructor(LavalinkManager) {
11
+ super();
12
+ this.LavalinkManager = LavalinkManager;
13
+ if (this.LavalinkManager.options.nodes)
14
+ this.LavalinkManager.options.nodes.forEach(node => this.createNode(node));
15
+ }
16
+ createNode(options) {
17
+ if (this.nodes.has(options.id || options.host))
18
+ return this.nodes.get(options.id || options.host);
19
+ const newNode = new Node_1.LavalinkNode(options, this);
20
+ this.nodes.set(newNode.id, newNode);
21
+ return newNode;
22
+ }
23
+ get leastUsedNodes() {
24
+ return [...this.nodes.values()].filter(v => v);
25
+ }
26
+ deleteNode(node) {
27
+ const decodeNode = typeof node === "string" ? this.nodes.get(node) : node || this.leastUsedNodes[0];
28
+ if (!decodeNode)
29
+ throw new Error("Node was not found");
30
+ decodeNode.destroy(Player_1.DestroyReasons.NodeDeleted);
31
+ this.nodes.delete(decodeNode.id);
32
+ return;
33
+ }
34
+ }
35
+ exports.NodeManager = NodeManager;
@@ -0,0 +1,191 @@
1
+ import { FilterManager, LavalinkFilterData } from "./Filters";
2
+ import { LavalinkManager } from "./LavalinkManager";
3
+ import { LavalinkNode } from "./Node";
4
+ import { Queue } from "./Queue";
5
+ import { Track } from "./Track";
6
+ import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult } from "./Utils";
7
+ type PlayerDestroyReasons = "QueueEmpty" | "NodeDestroy" | "NodeDeleted" | "LavalinkNoVoice" | "NodeReconnectFail" | "PlayerReconnectFail" | "Disconnected" | "ChannelDeleted";
8
+ export type DestroyReasonsType = PlayerDestroyReasons | string;
9
+ export declare const DestroyReasons: Record<PlayerDestroyReasons, PlayerDestroyReasons>;
10
+ export type RepeatMode = "queue" | "track" | "off";
11
+ export interface PlayerOptions {
12
+ guildId: string;
13
+ voiceChannelId: string;
14
+ volume?: number;
15
+ vcRegion?: string;
16
+ selfDeaf?: boolean;
17
+ selfMute?: boolean;
18
+ textChannelId?: string;
19
+ node?: LavalinkNode | string;
20
+ instaUpdateFiltersFix?: boolean;
21
+ applyVolumeAsFilter?: boolean;
22
+ }
23
+ export interface PlayOptions {
24
+ /** Which Track to play | don't provide, if it should pick from the Queue */
25
+ track?: Track;
26
+ /** Encoded Track to use, instead of the queue system... */
27
+ encodedTrack?: string | null;
28
+ /** Encoded Track to use&search, instead of the queue system (yt only)... */
29
+ identifier?: string;
30
+ /** The position to start the track. */
31
+ position?: number;
32
+ /** The position to end the track. */
33
+ endTime?: number;
34
+ /** Whether to not replace the track if a play payload is sent. */
35
+ noReplace?: boolean;
36
+ /** If to start "paused" */
37
+ paused?: boolean;
38
+ /** The Volume to start with */
39
+ volume?: number;
40
+ /** The Lavalink Filters to use | only with the new REST API */
41
+ filters?: Partial<LavalinkFilterData>;
42
+ voice?: LavalinkPlayerVoiceOptions;
43
+ }
44
+ export interface Player {
45
+ filterManager: FilterManager;
46
+ LavalinkManager: LavalinkManager;
47
+ options: PlayerOptions;
48
+ node: LavalinkNode;
49
+ queue: Queue;
50
+ }
51
+ export declare class Player {
52
+ /** The Guild Id of the Player */
53
+ guildId: string;
54
+ /** The Voice Channel Id of the Player */
55
+ voiceChannelId: string | null;
56
+ /** The Text Channel Id of the Player */
57
+ textChannelId: string | null;
58
+ /** States if the Bot is supposed to be outputting audio */
59
+ playing: boolean;
60
+ /** States if the Bot is paused or not */
61
+ paused: boolean;
62
+ /** Repeat Mode of the Player */
63
+ repeatMode: RepeatMode;
64
+ /** Player's ping */
65
+ ping: {
66
+ lavalink: number;
67
+ ws: number;
68
+ };
69
+ /** The Display Volume */
70
+ volume: number;
71
+ /** The Volume Lavalink actually is outputting */
72
+ lavalinkVolume: number;
73
+ /** The current Positin of the player (Calculated) */
74
+ position: number;
75
+ /** The current Positin of the player (from Lavalink) */
76
+ lastPosition: number;
77
+ /** When the player was created [Timestamp in Ms] (from lavalink) */
78
+ createdTimeStamp: number;
79
+ /** The Player Connection's State (from Lavalink) */
80
+ connected: boolean | undefined;
81
+ /** Voice Server Data (from Lavalink) */
82
+ voice: LavalinkPlayerVoiceOptions;
83
+ private readonly data;
84
+ /**
85
+ * Create a new Player
86
+ * @param options
87
+ * @param LavalinkManager
88
+ */
89
+ constructor(options: PlayerOptions, LavalinkManager: LavalinkManager);
90
+ /**
91
+ * Set custom data.
92
+ * @param key
93
+ * @param value
94
+ */
95
+ set(key: string, value: unknown): void;
96
+ /**
97
+ * Get custom data.
98
+ * @param key
99
+ */
100
+ get<T>(key: string): T;
101
+ /**
102
+ * CLears all the custom data.
103
+ */
104
+ clearData(): void;
105
+ /**
106
+ * Get all custom Data
107
+ */
108
+ getAllData(): Record<string, unknown>;
109
+ /**
110
+ * Play the next track from the queue / a specific track, with playoptions for Lavalink
111
+ * @param options
112
+ */
113
+ play(options?: Partial<PlayOptions>): Promise<void>;
114
+ /**
115
+ * Set the Volume for the Player
116
+ * @param volume The Volume in percent
117
+ * @param ignoreVolumeDecrementer If it should ignore the volumedecrementer option
118
+ */
119
+ setVolume(volume: number, ignoreVolumeDecrementer?: boolean): Promise<void>;
120
+ /**
121
+ *
122
+ * @param query Query for your data
123
+ * @param requestUser
124
+ */
125
+ search(query: {
126
+ query: string;
127
+ source?: SearchPlatform;
128
+ } | string, requestUser: unknown): Promise<SearchResult>;
129
+ /**
130
+ * Pause the player
131
+ */
132
+ pause(): Promise<void>;
133
+ /**
134
+ * Resume the Player
135
+ */
136
+ resume(): Promise<void>;
137
+ /**
138
+ * Seek to a specific Position
139
+ * @param position
140
+ */
141
+ seek(position: number): Promise<any>;
142
+ /**
143
+ * Set the Repeatmode of the Player
144
+ * @param repeatMode
145
+ */
146
+ setRepeatMode(repeatMode: RepeatMode): Promise<void>;
147
+ /**
148
+ * Skip the current song, or a specific amount of songs
149
+ * @param amount provide the index of the next track to skip to
150
+ */
151
+ skip(skipTo?: number): Promise<true | void>;
152
+ /**
153
+ * Connects the Player to the Voice Channel
154
+ * @returns
155
+ */
156
+ connect(): Promise<void>;
157
+ /**
158
+ * Disconnects the Player from the Voice Channel, but keeps the player in the cache
159
+ * @param force If false it throws an error, if player thinks it's already disconnected
160
+ * @returns
161
+ */
162
+ disconnect(force?: boolean): Promise<void>;
163
+ /**
164
+ * Destroy the player and disconnect from the voice channel
165
+ */
166
+ destroy(reason?: string): Promise<void>;
167
+ /**
168
+ * Move the player on a different Audio-Node
169
+ * @param newNode New Node / New Node Id
170
+ */
171
+ changeNode(newNode: LavalinkNode | string): Promise<string>;
172
+ /** Converts the Player including Queue to a Json state */
173
+ toJSON(): {
174
+ guildId: string;
175
+ voiceChannelId: string;
176
+ textChannelId: string;
177
+ position: number;
178
+ lastPosition: number;
179
+ volume: number;
180
+ lavalinkVolume: number;
181
+ repeatMode: RepeatMode;
182
+ paused: boolean;
183
+ playing: boolean;
184
+ createdTimeStamp: number;
185
+ filters: {};
186
+ equalizer: import("./Filters").EQBand[];
187
+ queue: import("./Queue").StoredQueue;
188
+ nodeId: string;
189
+ };
190
+ }
191
+ export {};
@@ -0,0 +1,395 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Player = exports.DestroyReasons = void 0;
4
+ const Filters_1 = require("./Filters");
5
+ const LavalinkManagerStatics_1 = require("./LavalinkManagerStatics");
6
+ const Queue_1 = require("./Queue");
7
+ const Utils_1 = require("./Utils");
8
+ exports.DestroyReasons = {
9
+ QueueEmpty: "QueueEmpty",
10
+ NodeDestroy: "NodeDestroy",
11
+ NodeDeleted: "NodeDeleted",
12
+ LavalinkNoVoice: "LavalinkNoVoice",
13
+ NodeReconnectFail: "NodeReconnectFail",
14
+ Disconnected: "Disconnected",
15
+ PlayerReconnectFail: "PlayerReconnectFail",
16
+ ChannelDeleted: "ChannelDeleted"
17
+ };
18
+ class Player {
19
+ /** The Guild Id of the Player */
20
+ guildId;
21
+ /** The Voice Channel Id of the Player */
22
+ voiceChannelId = null;
23
+ /** The Text Channel Id of the Player */
24
+ textChannelId = null;
25
+ /** States if the Bot is supposed to be outputting audio */
26
+ playing = false;
27
+ /** States if the Bot is paused or not */
28
+ paused = false;
29
+ /** Repeat Mode of the Player */
30
+ repeatMode = "off";
31
+ /** Player's ping */
32
+ ping = {
33
+ /* Response time for rest actions with Lavalink Server */
34
+ lavalink: 0,
35
+ /* Latency of the Discord's Websocket Voice Server */
36
+ ws: 0
37
+ };
38
+ /** The Display Volume */
39
+ volume = 100;
40
+ /** The Volume Lavalink actually is outputting */
41
+ lavalinkVolume = 100;
42
+ /** The current Positin of the player (Calculated) */
43
+ position = 0;
44
+ /** The current Positin of the player (from Lavalink) */
45
+ lastPosition = 0;
46
+ /** When the player was created [Timestamp in Ms] (from lavalink) */
47
+ createdTimeStamp;
48
+ /** The Player Connection's State (from Lavalink) */
49
+ connected = false;
50
+ /** Voice Server Data (from Lavalink) */
51
+ voice = {
52
+ endpoint: null,
53
+ sessionId: null,
54
+ token: null
55
+ };
56
+ data = {};
57
+ /**
58
+ * Create a new Player
59
+ * @param options
60
+ * @param LavalinkManager
61
+ */
62
+ constructor(options, LavalinkManager) {
63
+ this.options = options;
64
+ this.filterManager = new Filters_1.FilterManager(this);
65
+ this.LavalinkManager = LavalinkManager;
66
+ this.guildId = this.options.guildId;
67
+ this.voiceChannelId = this.options.voiceChannelId;
68
+ this.textChannelId = this.options.textChannelId || null;
69
+ this.node = this.LavalinkManager.nodeManager.leastUsedNodes.filter(v => options.vcRegion ? v.options?.regions?.includes(options.vcRegion) : true)[0] || this.LavalinkManager.nodeManager.leastUsedNodes[0] || null;
70
+ if (!this.node)
71
+ throw new Error("No available Node was found, please add a LavalinkNode to the Manager via Manager.NodeManager#createNode");
72
+ if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
73
+ this.volume *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
74
+ this.LavalinkManager.emit("playerCreate", this);
75
+ if (typeof options.volume === "number" && !isNaN(options.volume))
76
+ this.setVolume(options.volume);
77
+ this.queue = new Queue_1.Queue(this.guildId, {}, new Queue_1.QueueSaver(this.LavalinkManager.options.queueStore, this.LavalinkManager.options.queueOptions), this.LavalinkManager.options.queueChangesWatcher);
78
+ }
79
+ /**
80
+ * Set custom data.
81
+ * @param key
82
+ * @param value
83
+ */
84
+ set(key, value) {
85
+ this.data[key] = value;
86
+ return;
87
+ }
88
+ /**
89
+ * Get custom data.
90
+ * @param key
91
+ */
92
+ get(key) {
93
+ return this.data[key];
94
+ }
95
+ /**
96
+ * CLears all the custom data.
97
+ */
98
+ clearData() {
99
+ const toKeep = Object.keys(this.data).filter(v => v.startsWith("internal_"));
100
+ for (const key in this.data) {
101
+ if (toKeep.includes(key))
102
+ continue;
103
+ delete this.data[key];
104
+ }
105
+ return;
106
+ }
107
+ /**
108
+ * Get all custom Data
109
+ */
110
+ getAllData() {
111
+ return Object.fromEntries(Object.entries(this.data).filter(v => !v[0].startsWith("internal_")));
112
+ }
113
+ /**
114
+ * Play the next track from the queue / a specific track, with playoptions for Lavalink
115
+ * @param options
116
+ */
117
+ async play(options) {
118
+ if (this.get("internal_queueempty")) {
119
+ clearTimeout(this.get("internal_queueempty"));
120
+ this.set("internal_queueempty", undefined);
121
+ }
122
+ if (options?.track && this.LavalinkManager.utils.isTrack(options?.track)) {
123
+ await this.queue.add(options?.track, 0);
124
+ await (0, Utils_1.queueTrackEnd)(this.queue, this.repeatMode === "queue");
125
+ }
126
+ if (!this.queue.current && this.queue.tracks.length)
127
+ await (0, Utils_1.queueTrackEnd)(this.queue, this.repeatMode === "queue");
128
+ const track = this.queue.current;
129
+ if (!track)
130
+ throw new Error(`There is no Track in the Queue, nor provided in the PlayOptions`);
131
+ if (typeof options?.volume === "number" && !isNaN(options?.volume)) {
132
+ this.volume = Math.max(Math.min(options?.volume, 500), 0);
133
+ let vol = Number(this.volume);
134
+ if (this.LavalinkManager.options.playerOptions.volumeDecrementer)
135
+ vol *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
136
+ this.lavalinkVolume = Math.floor(vol * 100) / 100;
137
+ options.volume = vol;
138
+ }
139
+ const finalOptions = {
140
+ encodedTrack: track.encoded,
141
+ volume: this.lavalinkVolume,
142
+ position: 0,
143
+ ...options,
144
+ };
145
+ if ("track" in finalOptions)
146
+ delete finalOptions.track;
147
+ if ((typeof finalOptions.position !== "undefined" && isNaN(finalOptions.position)) || (typeof finalOptions.position === "number" && (finalOptions.position < 0 || finalOptions.position >= track.info.duration)))
148
+ throw new Error("PlayerOption#position must be a positive number, less than track's duration");
149
+ if ((typeof finalOptions.volume !== "undefined" && isNaN(finalOptions.volume) || (typeof finalOptions.volume === "number" && finalOptions.volume < 0)))
150
+ throw new Error("PlayerOption#volume must be a positive number");
151
+ if ((typeof finalOptions.endTime !== "undefined" && isNaN(finalOptions.endTime)) || (typeof finalOptions.endTime === "number" && (finalOptions.endTime < 0 || finalOptions.endTime >= track.info.duration)))
152
+ throw new Error("PlayerOption#endTime must be a positive number, less than track's duration");
153
+ if (typeof finalOptions.position === "number" && typeof finalOptions.endTime === "number" && finalOptions.endTime < finalOptions.position)
154
+ throw new Error("PlayerOption#endTime must be bigger than PlayerOption#position");
155
+ if ("noReplace" in finalOptions)
156
+ delete finalOptions.noReplace;
157
+ const now = performance.now();
158
+ await this.node.updatePlayer({
159
+ guildId: this.guildId,
160
+ noReplace: options?.noReplace ?? false,
161
+ playerOptions: finalOptions,
162
+ });
163
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
164
+ }
165
+ /**
166
+ * Set the Volume for the Player
167
+ * @param volume The Volume in percent
168
+ * @param ignoreVolumeDecrementer If it should ignore the volumedecrementer option
169
+ */
170
+ async setVolume(volume, ignoreVolumeDecrementer = false) {
171
+ volume = Number(volume);
172
+ if (isNaN(volume))
173
+ throw new TypeError("Volume must be a number.");
174
+ this.volume = Math.max(Math.min(volume, 500), 0);
175
+ volume = Number(this.volume);
176
+ if (this.LavalinkManager.options.playerOptions.volumeDecrementer && !ignoreVolumeDecrementer)
177
+ volume *= this.LavalinkManager.options.playerOptions.volumeDecrementer;
178
+ this.lavalinkVolume = Math.floor(volume * 100) / 100;
179
+ const now = performance.now();
180
+ if (this.LavalinkManager.options.playerOptions.applyVolumeAsFilter) {
181
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { filters: { volume: volume / 100 } } });
182
+ }
183
+ else {
184
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { volume } });
185
+ }
186
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
187
+ return;
188
+ }
189
+ /**
190
+ *
191
+ * @param query Query for your data
192
+ * @param requestUser
193
+ */
194
+ async search(query, requestUser) {
195
+ // transform the query object
196
+ const Query = {
197
+ query: typeof query === "string" ? query : query.query,
198
+ source: LavalinkManagerStatics_1.DefaultSources[(typeof query === "string" ? undefined : query.source) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform] ?? (typeof query === "string" ? undefined : query.source) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform
199
+ };
200
+ // if user does player.search("ytsearch:Hello")
201
+ const foundSource = [...Object.keys(LavalinkManagerStatics_1.DefaultSources)].find(source => Query.query.startsWith(`${source}:`));
202
+ if (foundSource && LavalinkManagerStatics_1.DefaultSources[foundSource]) {
203
+ Query.source = LavalinkManagerStatics_1.DefaultSources[foundSource]; // set the source to ytsearch:
204
+ Query.query = Query.query.replace(`${foundSource}:`, ""); // remove ytsearch: from the query
205
+ }
206
+ // request the data
207
+ const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}`);
208
+ // transform the data which can be Error, Track or Track[] to enfore [Track]
209
+ const resTracks = res.loadType === "playlist" ? res.data?.tracks : res.loadType === "track" ? [res.data] : res.loadType === "search" ? Array.isArray(res.data) ? res.data : [res.data] : [];
210
+ return {
211
+ loadType: res.loadType,
212
+ exception: res.loadType === "error" ? res.data : null,
213
+ pluginInfo: res.pluginInfo || {},
214
+ playlist: res.loadType === "playlist" ? {
215
+ title: res.data.info?.name || res.data.pluginInfo?.name || null,
216
+ author: res.data.info?.author || res.data.pluginInfo?.author || null,
217
+ thumbnail: (res.data.info?.artworkUrl) || (res.data.pluginInfo?.artworkUrl) || ((typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1) ? null : resTracks[res.data?.info?.selectedTrack] ? (resTracks[res.data?.info?.selectedTrack]?.info?.artworkUrl || resTracks[res.data?.info?.selectedTrack]?.info?.pluginInfo?.artworkUrl) : null) || null,
218
+ uri: res.data.info?.url || res.data.info?.uri || res.data.info?.link || res.data.pluginInfo?.url || res.data.pluginInfo?.uri || res.data.pluginInfo?.link || null,
219
+ selectedTrack: typeof res.data?.info?.selectedTrack !== "number" || res.data?.info?.selectedTrack === -1 ? null : resTracks[res.data?.info?.selectedTrack] ? this.LavalinkManager.utils.buildTrack(resTracks[res.data?.info?.selectedTrack], requestUser) : null,
220
+ duration: resTracks.length ? resTracks.reduce((acc, cur) => acc + (cur?.info?.duration || 0), 0) : 0,
221
+ } : null,
222
+ tracks: resTracks.length ? resTracks.map(t => this.LavalinkManager.utils.buildTrack(t, requestUser)) : []
223
+ };
224
+ }
225
+ /**
226
+ * Pause the player
227
+ */
228
+ async pause() {
229
+ if (this.paused && !this.playing)
230
+ throw new Error("Player is already paused - not able to pause.");
231
+ this.paused = true;
232
+ const now = performance.now();
233
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: true } });
234
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
235
+ return;
236
+ }
237
+ /**
238
+ * Resume the Player
239
+ */
240
+ async resume() {
241
+ if (!this.paused)
242
+ throw new Error("Player isn't paused - not able to resume.");
243
+ this.paused = false;
244
+ const now = performance.now();
245
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { paused: false } });
246
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
247
+ return;
248
+ }
249
+ /**
250
+ * Seek to a specific Position
251
+ * @param position
252
+ */
253
+ async seek(position) {
254
+ if (!this.queue.current)
255
+ return undefined;
256
+ position = Number(position);
257
+ if (isNaN(position))
258
+ throw new RangeError("Position must be a number.");
259
+ if (!this.queue.current.info.isSeekable || this.queue.current.info.isStream)
260
+ throw new RangeError("Current Track is not seekable / a stream");
261
+ if (position < 0 || position > this.queue.current.info.duration)
262
+ position = Math.max(Math.min(position, this.queue.current.info.duration), 0);
263
+ this.position = position;
264
+ this.lastPosition = position;
265
+ const now = performance.now();
266
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { position } });
267
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
268
+ return;
269
+ }
270
+ /**
271
+ * Set the Repeatmode of the Player
272
+ * @param repeatMode
273
+ */
274
+ async setRepeatMode(repeatMode) {
275
+ if (!["off", "track", "queue"].includes(repeatMode))
276
+ throw new RangeError("Repeatmode must be either 'off', 'track', or 'queue'");
277
+ this.repeatMode = repeatMode;
278
+ return;
279
+ }
280
+ /**
281
+ * Skip the current song, or a specific amount of songs
282
+ * @param amount provide the index of the next track to skip to
283
+ */
284
+ async skip(skipTo = 0) {
285
+ if (!this.queue.tracks.length)
286
+ throw new RangeError("Can't skip more than the queue size");
287
+ if (typeof skipTo === "number" && skipTo > 1) {
288
+ if (skipTo > this.queue.tracks.length)
289
+ throw new RangeError("Can't skip more than the queue size");
290
+ await this.queue.splice(0, skipTo - 1);
291
+ }
292
+ if (!this.playing)
293
+ return await this.play();
294
+ const now = performance.now();
295
+ await this.node.updatePlayer({ guildId: this.guildId, playerOptions: { encodedTrack: null } });
296
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
297
+ return true;
298
+ }
299
+ /**
300
+ * Connects the Player to the Voice Channel
301
+ * @returns
302
+ */
303
+ async connect() {
304
+ if (!this.options.voiceChannelId)
305
+ throw new RangeError("No Voice Channel id has been set.");
306
+ await this.LavalinkManager.options.sendToShard(this.guildId, {
307
+ op: 4,
308
+ d: {
309
+ guild_id: this.guildId,
310
+ channel_id: this.options.voiceChannelId,
311
+ self_mute: this.options.selfMute ?? false,
312
+ self_deaf: this.options.selfDeaf ?? true,
313
+ }
314
+ });
315
+ return;
316
+ }
317
+ /**
318
+ * Disconnects the Player from the Voice Channel, but keeps the player in the cache
319
+ * @param force If false it throws an error, if player thinks it's already disconnected
320
+ * @returns
321
+ */
322
+ async disconnect(force = false) {
323
+ if (!force && !this.options.voiceChannelId)
324
+ throw new RangeError("No Voice Channel id has been set.");
325
+ await this.LavalinkManager.options.sendToShard(this.guildId, {
326
+ op: 4,
327
+ d: {
328
+ guild_id: this.guildId,
329
+ channel_id: null,
330
+ self_mute: false,
331
+ self_deaf: false,
332
+ }
333
+ });
334
+ this.voiceChannelId = null;
335
+ return;
336
+ }
337
+ /**
338
+ * Destroy the player and disconnect from the voice channel
339
+ */
340
+ async destroy(reason) {
341
+ await this.disconnect(true);
342
+ await this.queue.utils.destroy();
343
+ this.LavalinkManager.deletePlayer(this.guildId);
344
+ await this.node.destroyPlayer(this.guildId);
345
+ this.LavalinkManager.emit("playerDestroy", this, reason);
346
+ return;
347
+ }
348
+ /**
349
+ * Move the player on a different Audio-Node
350
+ * @param newNode New Node / New Node Id
351
+ */
352
+ async changeNode(newNode) {
353
+ const updateNode = typeof newNode === "string" ? this.LavalinkManager.nodeManager.nodes.get(newNode) : newNode;
354
+ if (!updateNode)
355
+ throw new Error("Could not find the new Node");
356
+ const data = this.toJSON();
357
+ await this.node.destroyPlayer(this.guildId);
358
+ this.node = updateNode;
359
+ await this.connect();
360
+ const now = performance.now();
361
+ await this.node.updatePlayer({
362
+ guildId: this.guildId,
363
+ noReplace: false,
364
+ playerOptions: {
365
+ position: data.position,
366
+ volume: data.volume,
367
+ paused: data.paused,
368
+ filters: { ...data.filters, equalizer: data.equalizer },
369
+ },
370
+ });
371
+ this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
372
+ return this.node.id;
373
+ }
374
+ /** Converts the Player including Queue to a Json state */
375
+ toJSON() {
376
+ return {
377
+ guildId: this.guildId,
378
+ voiceChannelId: this.voiceChannelId,
379
+ textChannelId: this.textChannelId,
380
+ position: this.position,
381
+ lastPosition: this.lastPosition,
382
+ volume: this.volume,
383
+ lavalinkVolume: this.lavalinkVolume,
384
+ repeatMode: this.repeatMode,
385
+ paused: this.paused,
386
+ playing: this.playing,
387
+ createdTimeStamp: this.createdTimeStamp,
388
+ filters: this.filterManager?.data || {},
389
+ equalizer: this.filterManager?.equalizerBands || [],
390
+ queue: this.queue?.utils?.getStored?.() || { current: null, tracks: [], previous: [] },
391
+ nodeId: this.node?.id,
392
+ };
393
+ }
394
+ }
395
+ exports.Player = Player;