magmastream 2.9.3-dev.9 → 2.10.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.
Files changed (41) hide show
  1. package/dist/config/blockedWords.d.ts +1 -0
  2. package/dist/index.d.ts +19 -3700
  3. package/dist/index.js +1 -1
  4. package/dist/statestorage/JsonQueue.d.ts +173 -0
  5. package/dist/statestorage/JsonQueue.js +32 -4
  6. package/dist/statestorage/MemoryQueue.d.ts +154 -0
  7. package/dist/statestorage/MemoryQueue.js +56 -36
  8. package/dist/statestorage/RedisQueue.d.ts +178 -0
  9. package/dist/statestorage/RedisQueue.js +29 -7
  10. package/dist/structures/Enums.d.ts +310 -0
  11. package/dist/structures/Enums.js +4 -0
  12. package/dist/structures/Filters.d.ts +352 -0
  13. package/dist/structures/Filters.js +5 -4
  14. package/dist/structures/MagmastreamError.d.ts +14 -0
  15. package/dist/structures/Manager.d.ts +259 -0
  16. package/dist/structures/Manager.js +285 -542
  17. package/dist/structures/Node.d.ts +390 -0
  18. package/dist/structures/Node.js +100 -145
  19. package/dist/structures/Player.d.ts +347 -0
  20. package/dist/structures/Player.js +54 -128
  21. package/dist/structures/Plugin.d.ts +23 -0
  22. package/dist/structures/Rest.d.ts +93 -0
  23. package/dist/structures/Types.d.ts +1315 -0
  24. package/dist/structures/Utils.d.ts +169 -0
  25. package/dist/structures/Utils.js +107 -56
  26. package/dist/utils/filtersEqualizers.d.ts +16 -0
  27. package/dist/utils/managerCheck.d.ts +7 -0
  28. package/dist/utils/nodeCheck.d.ts +7 -0
  29. package/dist/utils/playerCheck.d.ts +7 -0
  30. package/dist/wrappers/discord.js.d.ts +15 -0
  31. package/dist/wrappers/discord.js.js +19 -4
  32. package/dist/wrappers/discordeno.d.ts +19 -0
  33. package/dist/wrappers/discordeno.js +77 -0
  34. package/dist/wrappers/eris.d.ts +15 -0
  35. package/dist/wrappers/eris.js +20 -3
  36. package/dist/wrappers/oceanic.d.ts +15 -0
  37. package/dist/wrappers/oceanic.js +22 -4
  38. package/dist/wrappers/seyfert.d.ts +37 -0
  39. package/dist/wrappers/seyfert.js +25 -1
  40. package/package.json +107 -102
  41. package/dist/wrappers/detritus.js +0 -52
@@ -0,0 +1,347 @@
1
+ import { Filters } from "./Filters";
2
+ import { Manager } from "./Manager";
3
+ import { Node } from "./Node";
4
+ import { AnyMessage, IQueue, Lyrics, PlayerOptions, PlayOptions, SearchQuery, SearchResult, Track, VoiceState } from "./Types";
5
+ import { SponsorBlockSegment, StateTypes } from "./Enums";
6
+ import { WebSocket } from "ws";
7
+ export declare class Player {
8
+ options: PlayerOptions;
9
+ /** The Queue for the Player. */
10
+ queue: IQueue;
11
+ /** The filters applied to the audio. */
12
+ filters: Filters;
13
+ /** Whether the queue repeats the track. */
14
+ trackRepeat: boolean;
15
+ /** Whether the queue repeats the queue. */
16
+ queueRepeat: boolean;
17
+ /**Whether the queue repeats and shuffles after each song. */
18
+ dynamicRepeat: boolean;
19
+ /** The time the player is in the track. */
20
+ position: number;
21
+ /** Whether the player is playing. */
22
+ playing: boolean;
23
+ /** Whether the player is paused. */
24
+ paused: boolean;
25
+ /** The volume for the player */
26
+ volume: number;
27
+ /** The Node for the Player. */
28
+ node: Node;
29
+ /** The guild ID for the player. */
30
+ guildId: string;
31
+ /** The voice channel for the player. */
32
+ voiceChannelId: string | null;
33
+ /** The text channel for the player. */
34
+ textChannelId: string | null;
35
+ /**The now playing message. */
36
+ nowPlayingMessage?: AnyMessage;
37
+ /** The current state of the player. */
38
+ state: StateTypes;
39
+ /** The equalizer bands array. */
40
+ bands: number[];
41
+ /** The voice state object from Discord. */
42
+ voiceState: VoiceState;
43
+ /** The Manager. */
44
+ manager: Manager;
45
+ /** The autoplay state of the player. */
46
+ isAutoplay: boolean;
47
+ /** The number of times to try autoplay before emitting queueEnd. */
48
+ autoplayTries: number;
49
+ /** The cluster ID for the player. */
50
+ clusterId: number;
51
+ private readonly data;
52
+ private dynamicLoopInterval;
53
+ dynamicRepeatIntervalMs: number | null;
54
+ private static _manager;
55
+ /** Should only be used when the node is a NodeLink */
56
+ protected voiceReceiverWsClient: WebSocket | null;
57
+ protected isConnectToVoiceReceiver: boolean;
58
+ protected voiceReceiverReconnectTimeout: NodeJS.Timeout | null;
59
+ protected voiceReceiverAttempt: number;
60
+ protected voiceReceiverReconnectTries: number;
61
+ /**
62
+ * Creates a new player, returns one if it already exists.
63
+ * @param options The player options.
64
+ * @see https://docs.magmastream.com/main/introduction/getting-started
65
+ */
66
+ constructor(options: PlayerOptions);
67
+ /**
68
+ * Initializes the static properties of the Player class.
69
+ * @hidden
70
+ * @param manager The Manager to use.
71
+ */
72
+ static init(manager: Manager): void;
73
+ /**
74
+ * Set custom data.
75
+ * @param key - The key to set the data for.
76
+ * @param value - The value to set the data to.
77
+ */
78
+ set(key: string, value: unknown): void;
79
+ /**
80
+ * Retrieves custom data associated with a given key.
81
+ * @template T - The expected type of the data.
82
+ * @param {string} key - The key to retrieve the data for.
83
+ * @returns {T} - The data associated with the key, cast to the specified type.
84
+ */
85
+ get<T>(key: string): T;
86
+ /**
87
+ * Same as Manager#search() but a shortcut on the player itself.
88
+ * @param query
89
+ * @param requester
90
+ */
91
+ search<T = unknown>(query: string | SearchQuery, requester?: T): Promise<SearchResult>;
92
+ /**
93
+ * Connects the player to the voice channel.
94
+ * @throws {RangeError} If no voice channel has been set.
95
+ * @returns {void}
96
+ */
97
+ connect(): void;
98
+ /**
99
+ * Disconnects the player from the voice channel.
100
+ * @returns {this} The player instance.
101
+ */
102
+ disconnect(): Promise<this>;
103
+ /**
104
+ * Destroys the player and clears the queue.
105
+ * @param {boolean} disconnect - Whether to disconnect the player from the voice channel.
106
+ * @returns {Promise<boolean>} - Whether the player was successfully destroyed.
107
+ * @emits {PlayerDestroy} - Emitted when the player is destroyed.
108
+ * @emits {PlayerStateUpdate} - Emitted when the player state is updated.
109
+ */
110
+ destroy(disconnect?: boolean): Promise<boolean>;
111
+ /**
112
+ * Sets the player voice channel.
113
+ * @param {string} channel - The new voice channel ID.
114
+ * @returns {this} - The player instance.
115
+ * @throws {TypeError} If the channel parameter is not a string.
116
+ */
117
+ setVoiceChannelId(channel: string): this;
118
+ /**
119
+ * Sets the player text channel.
120
+ *
121
+ * This method updates the text channel associated with the player. It also
122
+ * emits a player state update event indicating the change in the channel.
123
+ *
124
+ * @param {string} channel - The new text channel ID.
125
+ * @returns {this} - The player instance for method chaining.
126
+ * @throws {TypeError} If the channel parameter is not a string.
127
+ */
128
+ setTextChannelId(channel: string): this;
129
+ /**
130
+ * Sets the now playing message.
131
+ *
132
+ * @param message - The message of the now playing message.
133
+ * @returns The now playing message.
134
+ */
135
+ setNowPlayingMessage(message: AnyMessage): AnyMessage;
136
+ /**
137
+ * Plays the next track.
138
+ *
139
+ * If a track is provided, it will be played. Otherwise, the next track in the queue will be played.
140
+ * If the queue is not empty, but the current track has not finished yet, it will be replaced with the provided track.
141
+ *
142
+ * @param {object} [optionsOrTrack] - The track to play or the options to play with.
143
+ * @param {object} [playOptions] - The options to play with.
144
+ *
145
+ * @returns {Promise<void>}
146
+ */
147
+ play(): Promise<Player>;
148
+ play(track: Track): Promise<Player>;
149
+ play(options: PlayOptions): Promise<Player>;
150
+ play(track: Track, options: PlayOptions): Promise<Player>;
151
+ /**
152
+ * Sets the autoplay-state of the player.
153
+ *
154
+ * Autoplay is a feature that makes the player play a recommended
155
+ * track when the current track ends.
156
+ *
157
+ * @param {boolean} autoplayState - Whether or not autoplay should be enabled.
158
+ * @param {object} AutoplayUser - The user-object that should be used as the bot-user.
159
+ * @param {number} [tries=3] - The number of times the player should try to find a
160
+ * recommended track if the first one doesn't work.
161
+ * @returns {this} - The player instance.
162
+ */
163
+ setAutoplay<T = unknown>(autoplayState: boolean, AutoplayUser?: T, tries?: number): this;
164
+ /**
165
+ * Gets recommended tracks and returns an array of tracks.
166
+ * @param {Track} track - The track to find recommendations for.
167
+ * @returns {Promise<Track[]>} - Array of recommended tracks.
168
+ */
169
+ getRecommendedTracks(track: Track): Promise<Track[]>;
170
+ /**
171
+ * Sets the volume of the player.
172
+ * @param {number} volume - The new volume. Must be between 0 and 500 when using filter mode (100 = 100%).
173
+ * @returns {Promise<Player>} - The updated player.
174
+ * @throws {TypeError} If the volume is not a number.
175
+ * @throws {RangeError} If the volume is not between 0 and 500 when using filter mode (100 = 100%).
176
+ * @emits {PlayerStateUpdate} - Emitted when the volume is changed.
177
+ * @example
178
+ * player.setVolume(50);
179
+ */
180
+ setVolume(volume: number): Promise<this>;
181
+ /**
182
+ * Sets the sponsorblock for the player. This will set the sponsorblock segments for the player to the given segments.
183
+ * @param {SponsorBlockSegment[]} segments - The sponsorblock segments to set. Defaults to `[SponsorBlockSegment.Sponsor, SponsorBlockSegment.SelfPromo]` if not provided.
184
+ * @returns {Promise<void>} The promise is resolved when the operation is complete.
185
+ */
186
+ setSponsorBlock(segments?: SponsorBlockSegment[]): Promise<void>;
187
+ /**
188
+ * Gets the sponsorblock for the player.
189
+ * @returns {Promise<SponsorBlockSegment[]>} The sponsorblock segments.
190
+ */
191
+ getSponsorBlock(): Promise<SponsorBlockSegment[]>;
192
+ /**
193
+ * Deletes the sponsorblock for the player. This will remove all sponsorblock segments that have been set for the player.
194
+ * @returns {Promise<void>}
195
+ */
196
+ deleteSponsorBlock(): Promise<void>;
197
+ /**
198
+ * Sets the track repeat mode.
199
+ * When track repeat is enabled, the current track will replay after it ends.
200
+ * Disables queueRepeat and dynamicRepeat modes if enabled.
201
+ *
202
+ * @param repeat - A boolean indicating whether to enable track repeat.
203
+ * @returns {this} - The player instance.
204
+ * @throws {TypeError} If the repeat parameter is not a boolean.
205
+ */
206
+ setTrackRepeat(repeat: boolean): this;
207
+ /**
208
+ * Sets the queue repeat.
209
+ * @param repeat Whether to repeat the queue or not
210
+ * @returns {this} - The player instance.
211
+ * @throws {TypeError} If the repeat parameter is not a boolean
212
+ */
213
+ setQueueRepeat(repeat: boolean): this;
214
+ /**
215
+ * Sets the queue to repeat and shuffles the queue after each song.
216
+ * @param repeat "true" or "false".
217
+ * @param ms After how many milliseconds to trigger dynamic repeat.
218
+ * @returns {this} - The player instance.
219
+ * @throws {TypeError} If the repeat parameter is not a boolean.
220
+ * @throws {RangeError} If the queue size is less than or equal to 1.
221
+ */
222
+ setDynamicRepeat(repeat: boolean, ms: number): Promise<this>;
223
+ /**
224
+ * Restarts the currently playing track from the beginning.
225
+ * If there is no track playing, it will play the next track in the queue.
226
+ * @returns {Promise<Player>} The current instance of the Player class for method chaining.
227
+ */
228
+ restart(): Promise<Player>;
229
+ /**
230
+ * Stops the player and optionally removes tracks from the queue.
231
+ * @param {number} [amount] The amount of tracks to remove from the queue. If not provided, removes the current track if it exists.
232
+ * @returns {Promise<this>} - The player instance.
233
+ * @throws {RangeError} If the amount is greater than the queue length.
234
+ */
235
+ stop(amount?: number): Promise<this>;
236
+ /**
237
+ * Skips the current track.
238
+ * @returns {this} - The player instance.
239
+ * @throws {Error} If there are no tracks in the queue.
240
+ * @emits {PlayerStateUpdate} - With {@link PlayerStateEventTypes.TrackChange} as the change type.
241
+ */
242
+ pause(pause: boolean): Promise<this>;
243
+ /**
244
+ * Skips to the previous track in the queue.
245
+ * @returns {this} - The player instance.
246
+ * @throws {Error} If there are no previous tracks in the queue.
247
+ * @emits {PlayerStateUpdate} - With {@link PlayerStateEventTypes.TrackChange} as the change type.
248
+ */
249
+ previous(addBackToQueue?: boolean): Promise<this>;
250
+ /**
251
+ * Seeks to a given position in the currently playing track.
252
+ * @param position - The position in milliseconds to seek to.
253
+ * @returns {this} - The player instance.
254
+ * @throws {Error} If the position is invalid.
255
+ * @emits {PlayerStateUpdate} - With {@link PlayerStateEventTypes.TrackChange} as the change type.
256
+ */
257
+ seek(position: number): Promise<this>;
258
+ /**
259
+ * Returns the current repeat state of the player.
260
+ * @param player The player to get the repeat state from.
261
+ * @returns The repeat state of the player, or null if it is not repeating.
262
+ */
263
+ private getRepeatState;
264
+ /**
265
+ * Automatically moves the player to a usable node.
266
+ * @returns {Promise<Player | void>} - The player instance or void if not moved.
267
+ */
268
+ autoMoveNode(): Promise<Player | void>;
269
+ /**
270
+ * Moves the player to another node.
271
+ * @param {string} identifier - The identifier of the node to move to.
272
+ * @returns {Promise<Player>} - The player instance after being moved.
273
+ */
274
+ moveNode(identifier: string): Promise<Player>;
275
+ /**
276
+ * Retrieves the data associated with the player.
277
+ * @returns {Record<string, unknown>} - The data associated with the player.
278
+ */
279
+ getData(): Record<string, unknown>;
280
+ /**
281
+ * Retrieves the dynamic loop interval of the player.
282
+ * @returns {NodeJS.Timeout | null} - The dynamic loop interval of the player.
283
+ */
284
+ getDynamicLoopIntervalPublic(): NodeJS.Timeout | null;
285
+ /**
286
+ * Retrieves the data associated with the player.
287
+ * @returns {Record<string, unknown>} - The data associated with the player.
288
+ */
289
+ getSerializableData(): Record<string, unknown>;
290
+ /**
291
+ * Retrieves the current lyrics for the playing track.
292
+ * @param skipTrackSource - Indicates whether to skip the track source when fetching lyrics.
293
+ * @returns {Promise<Lyrics>} - The lyrics of the current track.
294
+ */
295
+ getCurrentLyrics(skipTrackSource?: boolean): Promise<Lyrics>;
296
+ /**
297
+ * Sets up the voice receiver for the player.
298
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is set up.
299
+ * @throws {Error} - If the node is not a NodeLink.
300
+ */
301
+ setupVoiceReceiver(): Promise<void>;
302
+ /**
303
+ * Removes the voice receiver for the player.
304
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is removed.
305
+ * @throws {Error} - If the node is not a NodeLink.
306
+ */
307
+ removeVoiceReceiver(): Promise<void>;
308
+ /**
309
+ * Closes the voice receiver for the player.
310
+ * @param {number} code - The code to close the voice receiver with.
311
+ * @param {string} reason - The reason to close the voice receiver with.
312
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is closed.
313
+ */
314
+ private closeVoiceReceiver;
315
+ /**
316
+ * Reconnects the voice receiver for the player.
317
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is reconnected.
318
+ */
319
+ private reconnectVoiceReceiver;
320
+ /**
321
+ * Disconnects the voice receiver for the player.
322
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is disconnected.
323
+ */
324
+ private disconnectVoiceReceiver;
325
+ /**
326
+ * Opens the voice receiver for the player.
327
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver is opened.
328
+ */
329
+ private openVoiceReceiver;
330
+ /**
331
+ * Handles a voice receiver message.
332
+ * @param {string} payload - The payload to handle.
333
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver message is handled.
334
+ */
335
+ private onVoiceReceiverMessage;
336
+ /**
337
+ * Handles a voice receiver error.
338
+ * @param {Error} error - The error to handle.
339
+ * @returns {Promise<void>} - A promise that resolves when the voice receiver error is handled.
340
+ */
341
+ private onVoiceReceiverError;
342
+ /**
343
+ * Updates the voice state for the player.
344
+ * @returns {Promise<void>} - A promise that resolves when the voice state is updated.
345
+ */
346
+ updateVoice(): Promise<void>;
347
+ }
@@ -82,7 +82,7 @@ class Player {
82
82
  message: "Manager instance is required.",
83
83
  });
84
84
  }
85
- this.clusterId = this.manager.options.clusterId || 0;
85
+ this.clusterId = this.manager.options.clusterId ?? 0;
86
86
  // Check the player options for errors.
87
87
  (0, playerCheck_1.default)(options);
88
88
  this.options = {
@@ -260,21 +260,30 @@ class Player {
260
260
  */
261
261
  async destroy(disconnect = true) {
262
262
  this.state = Enums_1.StateTypes.Destroying;
263
- await this.queue.clear();
264
- await this.queue.clearPrevious();
265
- await this.queue.setCurrent(null);
263
+ if (this.dynamicLoopInterval) {
264
+ clearInterval(this.dynamicLoopInterval);
265
+ this.dynamicLoopInterval = null;
266
+ }
267
+ if (this.voiceReceiverReconnectTimeout) {
268
+ clearTimeout(this.voiceReceiverReconnectTimeout);
269
+ this.voiceReceiverReconnectTimeout = null;
270
+ }
271
+ if (this.voiceReceiverWsClient) {
272
+ this.voiceReceiverWsClient.removeAllListeners();
273
+ this.voiceReceiverWsClient.close();
274
+ this.voiceReceiverWsClient = null;
275
+ }
266
276
  if (disconnect) {
267
- await this.disconnect().catch((err) => {
268
- console.warn(`[Player#destroy] Failed to disconnect player ${this.guildId}:`, err);
269
- });
277
+ await this.disconnect().catch(() => { });
270
278
  }
271
- await this.node.rest.destroyPlayer(this.guildId).catch((err) => {
272
- console.warn(`[Player#destroy] REST failed to destroy player ${this.guildId}:`, err);
273
- });
279
+ await this.node.rest.destroyPlayer(this.guildId).catch(() => { });
280
+ await this.queue.destroy();
281
+ this.nowPlayingMessage = undefined;
274
282
  this.manager.emit(Enums_1.ManagerEventTypes.PlayerDestroy, this);
275
283
  const deleted = this.manager.players.delete(this.guildId);
276
- if (this.manager.options.stateStorage.deleteInactivePlayers)
284
+ if (this.manager.options.stateStorage.deleteDestroyedPlayers) {
277
285
  await this.manager.cleanupInactivePlayer(this.guildId);
286
+ }
278
287
  return deleted;
279
288
  }
280
289
  /**
@@ -374,11 +383,8 @@ class Player {
374
383
  console.error(error);
375
384
  return this;
376
385
  }
377
- const finalOptions = playOptions
378
- ? playOptions
379
- : ["startTime", "endTime", "noReplace"].every((v) => Object.keys(optionsOrTrack || {}).includes(v))
380
- ? optionsOrTrack
381
- : {};
386
+ const isPlayOptions = (v) => typeof v === "object" && v !== null && ("startTime" in v || "endTime" in v || "noReplace" in v);
387
+ const finalOptions = playOptions ? playOptions : isPlayOptions(optionsOrTrack) ? optionsOrTrack : {};
382
388
  await this.node.rest.updatePlayer({
383
389
  guildId: this.guildId,
384
390
  data: {
@@ -387,7 +393,7 @@ class Player {
387
393
  },
388
394
  });
389
395
  this.playing = true;
390
- this.position = 0;
396
+ this.position = finalOptions.startTime || 0;
391
397
  return this;
392
398
  }
393
399
  /**
@@ -919,7 +925,8 @@ class Player {
919
925
  const sessionId = this.voiceState?.sessionId;
920
926
  const token = this.voiceState?.event?.token;
921
927
  const endpoint = this.voiceState?.event?.endpoint;
922
- if (!sessionId || !token || !endpoint) {
928
+ const channelId = this.voiceState?.channelId;
929
+ if (!sessionId || !token || !endpoint || !channelId) {
923
930
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[MANAGER] Voice state is not properly initialized for player ${this.guildId}. The bot might not be connected to a voice channel.`);
924
931
  throw new MagmastreamError_1.MagmaStreamError({
925
932
  code: Enums_1.MagmaStreamErrorCode.PLAYER_STATE_INVALID,
@@ -933,7 +940,7 @@ class Player {
933
940
  this.manager.players.set(this.guildId, this);
934
941
  await this.node.rest.updatePlayer({
935
942
  guildId: this.guildId,
936
- data: { paused: this.paused, volume: this.volume, position: playerPosition, encodedTrack: currentTrack?.track, voice: { token, endpoint, sessionId } },
943
+ data: { paused: this.paused, volume: this.volume, position: playerPosition, encodedTrack: currentTrack?.track, voice: { token, endpoint, sessionId, channelId } },
937
944
  });
938
945
  await this.filters.updateFilters();
939
946
  }
@@ -950,115 +957,6 @@ class Player {
950
957
  console.error(error);
951
958
  }
952
959
  }
953
- /**
954
- * Transfers the player to a new server. If the player already exists on the new server
955
- * and force is false, this method will return the existing player. Otherwise, a new player
956
- * will be created and the current player will be destroyed.
957
- * @param {PlayerOptions} newOptions - The new options for the player.
958
- * @param {boolean} force - Whether to force the creation of a new player.
959
- * @returns {Promise<Player>} - The new player instance.
960
- */
961
- async switchGuild(newOptions, force = false) {
962
- if (!newOptions.guildId) {
963
- throw new MagmastreamError_1.MagmaStreamError({
964
- code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
965
- message: "guildId is required for switchGuild",
966
- });
967
- }
968
- if (!newOptions.voiceChannelId) {
969
- throw new MagmastreamError_1.MagmaStreamError({
970
- code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
971
- message: "voiceChannelId is required for switchGuild",
972
- });
973
- }
974
- if (!newOptions.textChannelId) {
975
- throw new MagmastreamError_1.MagmaStreamError({
976
- code: Enums_1.MagmaStreamErrorCode.PLAYER_INVALID_CONFIG,
977
- message: "textChannelId is required for switchGuild",
978
- });
979
- }
980
- // Check if a player already exists for the new guild
981
- let newPlayer = this.manager.getPlayer(newOptions.guildId);
982
- // If the player already exists and force is false, return the existing player
983
- if (newPlayer && !force)
984
- return newPlayer;
985
- const oldPlayerProperties = {
986
- paused: this.paused,
987
- selfMute: this.options.selfMute,
988
- selfDeafen: this.options.selfDeafen,
989
- volume: this.volume,
990
- position: this.position,
991
- queue: {
992
- current: await this.queue.getCurrent(),
993
- tracks: [...(await this.queue.getTracks())],
994
- previous: [...(await this.queue.getPrevious())],
995
- },
996
- trackRepeat: this.trackRepeat,
997
- queueRepeat: this.queueRepeat,
998
- dynamicRepeat: this.dynamicRepeat,
999
- dynamicRepeatIntervalMs: this.dynamicRepeatIntervalMs,
1000
- ClientUser: this.get("Internal_AutoplayUser"),
1001
- filters: this.filters,
1002
- nowPlayingMessage: this.nowPlayingMessage,
1003
- isAutoplay: this.isAutoplay,
1004
- applyVolumeAsFilter: this.options.applyVolumeAsFilter,
1005
- pauseOnDisconnect: this.options.pauseOnDisconnect,
1006
- };
1007
- // If force is true, destroy the existing player for the new guild
1008
- if (force && newPlayer) {
1009
- await newPlayer.destroy();
1010
- }
1011
- newOptions.nodeIdentifier = newOptions.nodeIdentifier ?? this.options.nodeIdentifier;
1012
- newOptions.selfDeafen = newOptions.selfDeafen ?? oldPlayerProperties.selfDeafen;
1013
- newOptions.selfMute = newOptions.selfMute ?? oldPlayerProperties.selfMute;
1014
- newOptions.volume = newOptions.volume ?? oldPlayerProperties.volume;
1015
- newOptions.applyVolumeAsFilter = newOptions.applyVolumeAsFilter ?? oldPlayerProperties.applyVolumeAsFilter;
1016
- newOptions.pauseOnDisconnect = newOptions.pauseOnDisconnect ?? oldPlayerProperties.pauseOnDisconnect;
1017
- // Deep clone the current player
1018
- const clonedPlayer = this.manager.create(newOptions);
1019
- // Connect the cloned player to the new voice channel
1020
- clonedPlayer.connect();
1021
- // Update the player's state on the Lavalink node
1022
- await clonedPlayer.node.rest.updatePlayer({
1023
- guildId: clonedPlayer.guildId,
1024
- data: {
1025
- paused: oldPlayerProperties.paused,
1026
- volume: oldPlayerProperties.volume,
1027
- position: oldPlayerProperties.position,
1028
- encodedTrack: oldPlayerProperties.queue.current?.track,
1029
- },
1030
- });
1031
- await clonedPlayer.queue.setCurrent(oldPlayerProperties.queue.current);
1032
- await clonedPlayer.queue.addPrevious(oldPlayerProperties.queue.previous);
1033
- await clonedPlayer.queue.add(oldPlayerProperties.queue.tracks);
1034
- clonedPlayer.filters = oldPlayerProperties.filters;
1035
- clonedPlayer.isAutoplay = oldPlayerProperties.isAutoplay;
1036
- clonedPlayer.nowPlayingMessage = oldPlayerProperties.nowPlayingMessage;
1037
- clonedPlayer.trackRepeat = oldPlayerProperties.trackRepeat;
1038
- clonedPlayer.queueRepeat = oldPlayerProperties.queueRepeat;
1039
- clonedPlayer.dynamicRepeat = oldPlayerProperties.dynamicRepeat;
1040
- clonedPlayer.dynamicRepeatIntervalMs = oldPlayerProperties.dynamicRepeatIntervalMs;
1041
- clonedPlayer.set("Internal_AutoplayUser", oldPlayerProperties.ClientUser);
1042
- clonedPlayer.paused = oldPlayerProperties.paused;
1043
- // Update filters for the cloned player
1044
- await clonedPlayer.filters.updateFilters();
1045
- // Debug information
1046
- const debugInfo = {
1047
- success: true,
1048
- message: `Transferred ${await clonedPlayer.queue.size()} tracks successfully to <#${newOptions.voiceChannelId}> bound to <#${newOptions.textChannelId}>.`,
1049
- player: {
1050
- guildId: clonedPlayer.guildId,
1051
- voiceChannelId: clonedPlayer.voiceChannelId,
1052
- textChannelId: clonedPlayer.textChannelId,
1053
- volume: clonedPlayer.volume,
1054
- playing: clonedPlayer.playing,
1055
- queueSize: clonedPlayer.queue.size,
1056
- },
1057
- };
1058
- this.manager.emit(Enums_1.ManagerEventTypes.Debug, `[PLAYER] Transferred player to a new server: ${Utils_1.JSONUtils.safe(debugInfo, 2)}.`);
1059
- // Return the cloned player
1060
- return clonedPlayer;
1061
- }
1062
960
  /**
1063
961
  * Retrieves the data associated with the player.
1064
962
  * @returns {Record<string, unknown>} - The data associated with the player.
@@ -1073,6 +971,13 @@ class Player {
1073
971
  getDynamicLoopIntervalPublic() {
1074
972
  return this.dynamicLoopInterval;
1075
973
  }
974
+ /**
975
+ * Retrieves the data associated with the player.
976
+ * @returns {Record<string, unknown>} - The data associated with the player.
977
+ */
978
+ getSerializableData() {
979
+ return { ...this.data };
980
+ }
1076
981
  /**
1077
982
  * Retrieves the current lyrics for the playing track.
1078
983
  * @param skipTrackSource - Indicates whether to skip the track source when fetching lyrics.
@@ -1236,5 +1141,26 @@ class Player {
1236
1141
  this.manager.emit(Enums_1.ManagerEventTypes.Debug, `VoiceReceiver error for player ${this.guildId}: ${error.message}`);
1237
1142
  this.manager.emit(Enums_1.ManagerEventTypes.VoiceReceiverError, this, error);
1238
1143
  }
1144
+ /**
1145
+ * Updates the voice state for the player.
1146
+ * @returns {Promise<void>} - A promise that resolves when the voice state is updated.
1147
+ */
1148
+ async updateVoice() {
1149
+ const vs = this.voiceState;
1150
+ const ev = vs?.event;
1151
+ if (!vs?.channelId || !vs?.sessionId || !ev?.token || !ev?.endpoint)
1152
+ return;
1153
+ await this.node.rest.updatePlayer({
1154
+ guildId: this.options.guildId,
1155
+ data: {
1156
+ voice: {
1157
+ token: ev.token,
1158
+ endpoint: ev.endpoint,
1159
+ sessionId: vs.sessionId,
1160
+ channelId: vs.channelId,
1161
+ },
1162
+ },
1163
+ });
1164
+ }
1239
1165
  }
1240
1166
  exports.Player = Player;
@@ -0,0 +1,23 @@
1
+ import { Manager } from "./Manager";
2
+ /**
3
+ * Base abstract class for all plugins.
4
+ * Users must extend this and implement load and unload methods.
5
+ */
6
+ export declare abstract class Plugin {
7
+ readonly name: string;
8
+ /**
9
+ * @param name The name of the plugin
10
+ */
11
+ constructor(name: string);
12
+ /**
13
+ * Load the plugin.
14
+ * @param manager The MagmaStream Manager instance
15
+ */
16
+ abstract load(manager: Manager): void;
17
+ /**
18
+ * Unload the plugin.
19
+ * Called on shutdown to gracefully cleanup resources or detach listeners.
20
+ * @param manager The MagmaStream Manager instance
21
+ */
22
+ abstract unload(manager: Manager): void;
23
+ }