magmastream 2.9.0-dev.2 → 2.9.0-dev.4

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.d.ts CHANGED
@@ -678,11 +678,26 @@ declare abstract class AutoPlayUtils {
678
678
  * @hidden
679
679
  */
680
680
  static init(manager: Manager): void;
681
- static getRecommendedTracks(player: Player, track: Track, attempt?: number): Promise<Track[]>;
681
+ /**
682
+ * Gets recommended tracks for the given track.
683
+ * @param track The track to get recommended tracks for.
684
+ * @returns An array of recommended tracks.
685
+ */
686
+ static getRecommendedTracks(track: Track): Promise<Track[]>;
687
+ /**
688
+ * Gets recommended tracks from Last.fm for the given track.
689
+ * @param track The track to get recommended tracks for.
690
+ * @param apiKey The API key for Last.fm.
691
+ * @returns An array of recommended tracks.
692
+ */
682
693
  static getRecommendedTracksFromLastFm(track: Track, apiKey: string): Promise<Track[]>;
683
- static getRecommendedTracksFromSource(track: Track, mappedPlatform: string): Promise<Track[]>;
684
- static getRecommendedTracksFromYouTube(track: Track): Promise<Track[]>;
685
- static selectPlatform(enabledSources: string[]): SearchPlatform | null;
694
+ /**
695
+ * Gets recommended tracks from the given source.
696
+ * @param track The track to get recommended tracks for.
697
+ * @param platform The source to get recommended tracks from.
698
+ * @returns An array of recommended tracks.
699
+ */
700
+ static getRecommendedTracksFromSource(track: Track, platform: string): Promise<Track[]>;
686
701
  }
687
702
  /** Gets or extends structures to extend the built in, or already extended, classes to add more functionality. */
688
703
  declare abstract class Structure {
@@ -901,7 +916,7 @@ declare class Manager extends EventEmitter {
901
916
  * @param options.enabledPlugins - An array of enabledPlugins to load.
902
917
  * @param options.nodes - An array of node options to create nodes from.
903
918
  * @param options.playNextOnEnd - Whether to automatically play the first track in the queue when the player is created.
904
- * @param options.autoPlaySearchPlatform - The search platform autoplay will use. Fallback to Youtube if not found.
919
+ * @param options.autoPlaySearchPlatforms - The search platform autoplay will use. Fallback to Youtube if not found.
905
920
  * @param options.enablePriorityMode - Whether to use the priority when selecting a node to play on.
906
921
  * @param options.clientName - The name of the client to send to Lavalink.
907
922
  * @param options.defaultSearchPlatform - The default search platform to use when searching for tracks.
@@ -1118,9 +1133,10 @@ interface ManagerOptions {
1118
1133
  enablePriorityMode?: boolean;
1119
1134
  /** Automatically play the next track when the current one ends. */
1120
1135
  playNextOnEnd?: boolean;
1121
- /** The search platform autoplay should use
1122
- * Use enum `SearchPlatform`. */
1123
- autoPlaySearchPlatform?: SearchPlatform;
1136
+ /** An array of search platforms to use for autoplay. First to last matters
1137
+ * Use enum `AutoPlayPlatform`.
1138
+ */
1139
+ autoPlaySearchPlatforms?: AutoPlayPlatform[];
1124
1140
  /** The client ID to use. */
1125
1141
  clientId?: string;
1126
1142
  /** Value to use for the `Client-Name` header. */
@@ -1202,6 +1218,12 @@ declare enum SearchPlatform {
1202
1218
  YouTube = "ytsearch",
1203
1219
  YouTubeMusic = "ytmsearch"
1204
1220
  }
1221
+ declare enum AutoPlayPlatform {
1222
+ Spotify = "spotify",
1223
+ Deezer = "deezer",
1224
+ SoundCloud = "soundcloud",
1225
+ YouTube = "youtube"
1226
+ }
1205
1227
  declare enum PlayerStateEventTypes {
1206
1228
  AutoPlayChange = "playerAutoplay",
1207
1229
  ConnectionChange = "playerConnection",
@@ -1402,7 +1424,7 @@ declare class Player {
1402
1424
  /** The autoplay state of the player. */
1403
1425
  isAutoplay: boolean;
1404
1426
  /** The number of times to try autoplay before emitting queueEnd. */
1405
- autoplayTries: number | null;
1427
+ autoplayTries: number;
1406
1428
  private static _manager;
1407
1429
  private readonly data;
1408
1430
  private dynamicLoopInterval;
@@ -2136,5 +2158,5 @@ declare class Plugin {
2136
2158
  load(manager: Manager): void;
2137
2159
  }
2138
2160
 
2139
- export { AutoPlayUtils, AvailableFilters, Filters, LoadTypes, Manager, ManagerEventTypes, Node, Player, PlayerStateEventTypes, Plugin, Queue, Rest, SearchPlatform, SeverityTypes, SponsorBlockSegment, StateTypes, Structure, TrackEndReasonTypes, TrackPartial, TrackSourceTypes, TrackUtils, UseNodeOptions };
2161
+ export { AutoPlayPlatform, AutoPlayUtils, AvailableFilters, Filters, LoadTypes, Manager, ManagerEventTypes, Node, Player, PlayerStateEventTypes, Plugin, Queue, Rest, SearchPlatform, SeverityTypes, SponsorBlockSegment, StateTypes, Structure, TrackEndReasonTypes, TrackPartial, TrackSourceTypes, TrackUtils, UseNodeOptions };
2140
2162
  export type { CPUStats, EqualizerBand, Exception, Extendable, FrameStats, LavalinkInfo, LavalinkResponse, LoadType, Lyrics, LyricsLine, ManagerEvents, ManagerOptions, MemoryStats, NodeMessage, NodeOptions, NodeStats, Payload, PlayOptions, PlayerEvent, PlayerEventType, PlayerEvents, PlayerOptions, PlayerUpdate, PlaylistData, PlaylistInfoData, PlaylistRawData, SearchQuery, SearchResult, Severity, Sizes, SponsorBlockChapterStarted, SponsorBlockChaptersLoaded, SponsorBlockSegmentEventType, SponsorBlockSegmentEvents, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded, State, Track, TrackData, TrackDataInfo, TrackEndEvent, TrackEndReason, TrackExceptionEvent, TrackPluginInfo, TrackSourceName, TrackStartEvent, TrackStuckEvent, UseNodeOption, VoicePacket, VoiceServer, VoiceState, WebSocketClosedEvent };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ManagerEventTypes = exports.PlayerStateEventTypes = exports.SearchPlatform = exports.UseNodeOptions = exports.TrackPartial = exports.Manager = void 0;
3
+ exports.ManagerEventTypes = exports.PlayerStateEventTypes = exports.AutoPlayPlatform = exports.SearchPlatform = exports.UseNodeOptions = exports.TrackPartial = exports.Manager = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const Utils_1 = require("./Utils");
6
6
  const collection_1 = require("@discordjs/collection");
@@ -27,7 +27,7 @@ class Manager extends events_1.EventEmitter {
27
27
  * @param options.enabledPlugins - An array of enabledPlugins to load.
28
28
  * @param options.nodes - An array of node options to create nodes from.
29
29
  * @param options.playNextOnEnd - Whether to automatically play the first track in the queue when the player is created.
30
- * @param options.autoPlaySearchPlatform - The search platform autoplay will use. Fallback to Youtube if not found.
30
+ * @param options.autoPlaySearchPlatforms - The search platform autoplay will use. Fallback to Youtube if not found.
31
31
  * @param options.enablePriorityMode - Whether to use the priority when selecting a node to play on.
32
32
  * @param options.clientName - The name of the client to send to Lavalink.
33
33
  * @param options.defaultSearchPlatform - The default search platform to use when searching for tracks.
@@ -61,7 +61,6 @@ class Manager extends events_1.EventEmitter {
61
61
  enablePriorityMode: false,
62
62
  clientName: "Magmastream",
63
63
  defaultSearchPlatform: SearchPlatform.YouTube,
64
- // autoPlaySearchPlatform: SearchPlatform.YouTube,
65
64
  useNode: UseNodeOptions.LeastPlayers,
66
65
  maxPreviousTracks: options.maxPreviousTracks ?? 20,
67
66
  ...options,
@@ -924,6 +923,13 @@ var SearchPlatform;
924
923
  SearchPlatform["YouTube"] = "ytsearch";
925
924
  SearchPlatform["YouTubeMusic"] = "ytmsearch";
926
925
  })(SearchPlatform || (exports.SearchPlatform = SearchPlatform = {}));
926
+ var AutoPlayPlatform;
927
+ (function (AutoPlayPlatform) {
928
+ AutoPlayPlatform["Spotify"] = "spotify";
929
+ AutoPlayPlatform["Deezer"] = "deezer";
930
+ AutoPlayPlatform["SoundCloud"] = "soundcloud";
931
+ AutoPlayPlatform["YouTube"] = "youtube";
932
+ })(AutoPlayPlatform || (exports.AutoPlayPlatform = AutoPlayPlatform = {}));
927
933
  var PlayerStateEventTypes;
928
934
  (function (PlayerStateEventTypes) {
929
935
  PlayerStateEventTypes["AutoPlayChange"] = "playerAutoplay";
@@ -602,11 +602,13 @@ class Node {
602
602
  */
603
603
  async handleAutoplay(player, attempt = 0) {
604
604
  // If autoplay is not enabled or all attempts have failed, early exit
605
- if (!player.isAutoplay || attempt === player.autoplayTries || !player.queue.previous.length)
605
+ if (!player.isAutoplay || attempt > player.autoplayTries || !player.queue.previous.length)
606
606
  return false;
607
607
  const lastTrack = player.queue.previous[player.queue.previous.length - 1];
608
608
  lastTrack.requester = player.get("Internal_BotUser");
609
- const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(player, lastTrack, attempt);
609
+ if (!lastTrack)
610
+ return false;
611
+ const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(lastTrack);
610
612
  if (tracks.length) {
611
613
  player.queue.add(tracks[0]);
612
614
  await player.play();
@@ -714,13 +716,13 @@ class Node {
714
716
  this.manager.emit(Manager_1.ManagerEventTypes.QueueEnd, player, track, payload);
715
717
  return;
716
718
  }
717
- let attempts = 1;
719
+ let attempt = 1;
718
720
  let success = false;
719
- while (attempts <= player.autoplayTries) {
720
- success = await this.handleAutoplay(player, attempts);
721
+ while (attempt <= player.autoplayTries) {
722
+ success = await this.handleAutoplay(player, attempt);
721
723
  if (success)
722
724
  return;
723
- attempts++;
725
+ attempt++;
724
726
  }
725
727
  // If all attempts fail, reset the player state and emit queueEnd
726
728
  player.playing = false;
@@ -50,7 +50,7 @@ class Player {
50
50
  /** The autoplay state of the player. */
51
51
  isAutoplay = false;
52
52
  /** The number of times to try autoplay before emitting queueEnd. */
53
- autoplayTries = null;
53
+ autoplayTries = 3;
54
54
  static _manager;
55
55
  data = {};
56
56
  dynamicLoopInterval = null;
@@ -375,7 +375,7 @@ class Player {
375
375
  * @returns {Promise<Track[]>} - Array of recommended tracks.
376
376
  */
377
377
  async getRecommendedTracks(track) {
378
- const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(this, track);
378
+ const tracks = await Utils_1.AutoPlayUtils.getRecommendedTracks(track);
379
379
  return tracks;
380
380
  }
381
381
  /**
@@ -143,274 +143,224 @@ class AutoPlayUtils {
143
143
  throw new Error("AutoPlayUtils.init() requires a valid Manager instance.");
144
144
  this.manager = manager;
145
145
  }
146
- static async getRecommendedTracks(player, track, attempt = 0) {
147
- console.log(`[AutoPlay] Attempt ${attempt} for track: ${track.title}`);
146
+ /**
147
+ * Gets recommended tracks for the given track.
148
+ * @param track The track to get recommended tracks for.
149
+ * @returns An array of recommended tracks.
150
+ */
151
+ static async getRecommendedTracks(track) {
148
152
  const node = this.manager.useableNode;
149
153
  if (!node) {
150
- console.error("[AutoPlay] No available nodes.");
151
154
  throw new Error("No available nodes.");
152
155
  }
153
- if (!player.isAutoplay) {
154
- console.log("[AutoPlay] Autoplay is disabled. Returning an empty array.");
155
- return [];
156
- }
157
- if (attempt >= player.autoplayTries) {
158
- console.warn(`[AutoPlay] Reached max autoplay attempts (${player.autoplayTries}).`);
159
- return [];
160
- }
161
- if (!player.queue.previous.length) {
162
- console.log("[AutoPlay] No previous tracks in the queue. Cannot generate recommendations.");
163
- return [];
164
- }
165
156
  const apiKey = this.manager.options.lastFmApiKey;
166
157
  const enabledSources = node.info.sourceManagers;
167
- const { autoPlaySearchPlatform } = this.manager.options;
168
- console.log(`[AutoPlay] Enabled sources: ${enabledSources.join(", ")}`);
169
- console.log(`[AutoPlay] Preferred autoplay platform: ${autoPlaySearchPlatform}`);
170
- const supportedPlatforms = ["spotify", "deezer", "soundcloud", "youtube"];
171
- const platformMapping = {
172
- [Manager_1.SearchPlatform.AppleMusic]: "applemusic",
173
- [Manager_1.SearchPlatform.Bandcamp]: "bandcamp",
174
- [Manager_1.SearchPlatform.Deezer]: "deezer",
175
- [Manager_1.SearchPlatform.Jiosaavn]: "jiosaavn",
176
- [Manager_1.SearchPlatform.SoundCloud]: "soundcloud",
177
- [Manager_1.SearchPlatform.Spotify]: "spotify",
178
- [Manager_1.SearchPlatform.Tidal]: "tidal",
179
- [Manager_1.SearchPlatform.VKMusic]: "vkmusic",
180
- [Manager_1.SearchPlatform.YouTube]: "youtube",
181
- [Manager_1.SearchPlatform.YouTubeMusic]: "youtube",
182
- };
183
- const mappedPlatform = platformMapping[autoPlaySearchPlatform];
184
- // Last attempt fallback to YouTube
185
- if (attempt === player.autoplayTries - 1 && player.autoplayTries > 1 && enabledSources.includes("youtube")) {
186
- console.log("[AutoPlay] Final attempt: Falling back to YouTube recommendations.");
187
- return await this.getRecommendedTracksFromYouTube(track);
188
- }
189
- // Check if the preferred autoplay platform is supported and enabled
190
- if (mappedPlatform && supportedPlatforms.includes(mappedPlatform) && enabledSources.includes(mappedPlatform)) {
191
- console.log(`[AutoPlay] Using recommended platform: ${mappedPlatform}`);
192
- return await this.getRecommendedTracksFromSource(track, mappedPlatform);
158
+ const autoPlaySearchPlatforms = this.manager.options.autoPlaySearchPlatforms;
159
+ // Iterate over autoplay platforms in order of priority
160
+ for (const platform of autoPlaySearchPlatforms) {
161
+ if (enabledSources.includes(platform)) {
162
+ const recommendedTracks = await this.getRecommendedTracksFromSource(track, platform);
163
+ // If tracks are found, return them immediately
164
+ if (recommendedTracks.length > 0) {
165
+ return recommendedTracks;
166
+ }
167
+ }
193
168
  }
194
169
  // Check if Last.fm API is available
195
170
  if (apiKey) {
196
- console.log("[AutoPlay] No preferred platform found. Using Last.fm recommendations.");
197
171
  return await this.getRecommendedTracksFromLastFm(track, apiKey);
198
172
  }
199
- // Fallback to YouTube if all else fails
200
- if (enabledSources.includes("youtube")) {
201
- console.warn("[AutoPlay] No other sources available. Falling back to YouTube.");
202
- return await this.getRecommendedTracksFromYouTube(track);
203
- }
204
- console.error("[AutoPlay] No suitable platform found. Returning an empty array.");
205
173
  return [];
206
174
  }
175
+ /**
176
+ * Gets recommended tracks from Last.fm for the given track.
177
+ * @param track The track to get recommended tracks for.
178
+ * @param apiKey The API key for Last.fm.
179
+ * @returns An array of recommended tracks.
180
+ */
207
181
  static async getRecommendedTracksFromLastFm(track, apiKey) {
208
- const enabledSources = this.manager.useableNode.info.sourceManagers;
209
- const selectedSource = this.selectPlatform(enabledSources);
210
- console.log(`[AutoPlay] Selected source: ${selectedSource}`);
211
182
  let { author: artist } = track;
212
183
  const { title } = track;
213
- console.log(`[AutoPlay] Searching for recommended tracks for: ${artist} - ${title}`);
214
184
  if (!artist || !title) {
215
185
  if (!title) {
216
186
  // No title provided, search for the artist's top tracks
217
187
  const noTitleUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
218
- console.log(`[AutoPlay] No title provided. Fetching artist's top tracks from: ${noTitleUrl}`);
219
188
  const response = await axios_1.default.get(noTitleUrl);
220
189
  if (response.data.error || !response.data.toptracks?.track?.length) {
221
- console.error("[AutoPlay] Error or no tracks found for the artist. Returning an empty array.");
222
190
  return [];
223
191
  }
224
- console.log("[AutoPlay] Successfully fetched artist's top tracks.");
225
192
  const randomTrack = response.data.toptracks.track[Math.floor(Math.random() * response.data.toptracks.track.length)];
226
- console.log(`[AutoPlay] Selected random track: ${randomTrack.artist.name} - ${randomTrack.name}`);
227
- const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, track.requester);
193
+ const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: this.manager.options.defaultSearchPlatform }, track.requester);
228
194
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
229
- console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
230
195
  return [];
231
196
  }
232
197
  const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
233
198
  if (!filteredTracks.length) {
234
- console.error("[AutoPlay] No suitable tracks found. Returning an empty array.");
235
199
  return [];
236
200
  }
237
- console.log("[AutoPlay] Found suitable tracks.");
238
201
  return filteredTracks;
239
202
  }
240
203
  if (!artist) {
241
204
  // No artist provided, search for the track title
242
205
  const noArtistUrl = `https://ws.audioscrobbler.com/2.0/?method=track.search&track=${title}&api_key=${apiKey}&format=json`;
243
- console.log(`[AutoPlay] No artist provided. Searching for track: ${title} from: ${noArtistUrl}`);
244
206
  const response = await axios_1.default.get(noArtistUrl);
245
207
  artist = response.data.results.trackmatches?.track?.[0]?.artist;
246
208
  if (!artist) {
247
- console.error("[AutoPlay] No artist found for track. Returning an empty array.");
248
209
  return [];
249
210
  }
250
- console.log(`[AutoPlay] Found artist for track: ${artist}`);
251
211
  }
252
212
  }
253
213
  // Search for similar tracks to the current track
254
214
  const url = `https://ws.audioscrobbler.com/2.0/?method=track.getSimilar&artist=${artist}&track=${title}&limit=10&autocorrect=1&api_key=${apiKey}&format=json`;
255
- console.log(`[AutoPlay] Searching for similar tracks using URL: ${url}`);
256
215
  let response;
257
216
  try {
258
217
  response = await axios_1.default.get(url);
259
- console.log("[AutoPlay] Successfully fetched similar tracks.");
260
218
  }
261
219
  catch (error) {
262
- console.error("[AutoPlay] Error fetching similar tracks. Returning an empty array.");
263
- console.log(error);
220
+ console.error("[AutoPlay] Error fetching similar tracks from Last.fm:", error);
264
221
  return [];
265
222
  }
266
223
  if (response.data.error || !response.data.similartracks?.track?.length) {
267
- console.error("[AutoPlay] Error or no similar tracks found. Retrying with top tracks.");
268
224
  // Retry the request if the first attempt fails
269
225
  const retryUrl = `https://ws.audioscrobbler.com/2.0/?method=artist.getTopTracks&artist=${artist}&autocorrect=1&api_key=${apiKey}&format=json`;
270
226
  const retryResponse = await axios_1.default.get(retryUrl);
271
227
  if (retryResponse.data.error || !retryResponse.data.toptracks?.track?.length) {
272
- console.error("[AutoPlay] Retry failed. Returning an empty array.");
273
228
  return [];
274
229
  }
275
230
  const randomTrack = retryResponse.data.toptracks.track[Math.floor(Math.random() * retryResponse.data.toptracks.track.length)];
276
- console.log(`[AutoPlay] Selected random track from retry: ${randomTrack.artist.name} - ${randomTrack.name}`);
277
- const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, track.requester);
231
+ const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: this.manager.options.defaultSearchPlatform }, track.requester);
278
232
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
279
- console.error("[AutoPlay] Retry search returned empty or error result. Returning an empty array.");
280
233
  return [];
281
234
  }
282
235
  const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
283
236
  if (!filteredTracks.length) {
284
- console.error("[AutoPlay] No suitable tracks found in retry. Returning an empty array.");
285
237
  return [];
286
238
  }
287
- console.log("[AutoPlay] Found suitable tracks from retry.");
288
239
  return filteredTracks;
289
240
  }
290
241
  const randomTrack = response.data.similartracks.track.sort(() => Math.random() - 0.5).shift();
291
242
  if (!randomTrack) {
292
- console.error("[AutoPlay] No similar tracks found after filtering. Returning an empty array.");
293
243
  return [];
294
244
  }
295
- console.log(`[AutoPlay] Selected random track: ${randomTrack.name} - ${randomTrack.artist.name}`);
296
- const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: selectedSource }, track.requester);
245
+ const res = await this.manager.search({ query: `${randomTrack.artist.name} - ${randomTrack.name}`, source: this.manager.options.defaultSearchPlatform }, track.requester);
297
246
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
298
- console.error("[AutoPlay] Final search returned empty or error result. Returning an empty array.");
299
247
  return [];
300
248
  }
301
249
  if (res.loadType === LoadTypes.Playlist)
302
250
  res.tracks = res.playlist.tracks;
303
251
  if (!res.tracks.length) {
304
- console.error("[AutoPlay] No tracks found in final search. Returning an empty array.");
305
252
  return [];
306
253
  }
307
254
  return res.tracks;
308
255
  }
309
- static async getRecommendedTracksFromSource(track, mappedPlatform) {
310
- switch (mappedPlatform) {
256
+ /**
257
+ * Gets recommended tracks from the given source.
258
+ * @param track The track to get recommended tracks for.
259
+ * @param platform The source to get recommended tracks from.
260
+ * @returns An array of recommended tracks.
261
+ */
262
+ static async getRecommendedTracksFromSource(track, platform) {
263
+ switch (platform) {
311
264
  case "spotify":
312
- console.log("[AutoPlay] Checking if track URI includes 'spotify':", track.uri);
313
- if (!track.uri.includes("spotify")) {
314
- console.log("[AutoPlay] Track URI does not include 'spotify'. Searching for track:", `${track.author} - ${track.title}`);
315
- const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
265
+ try {
266
+ if (!track.uri.includes("spotify")) {
267
+ const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
268
+ if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
269
+ return [];
270
+ }
271
+ if (res.loadType === LoadTypes.Playlist) {
272
+ res.tracks = res.playlist.tracks;
273
+ }
274
+ if (!res.tracks.length) {
275
+ return [];
276
+ }
277
+ track = res.tracks[0];
278
+ }
279
+ const TOTP_SECRET = new Uint8Array([
280
+ 53, 53, 48, 55, 49, 52, 53, 56, 53, 51, 52, 56, 55, 52, 57, 57, 53, 57, 50, 50, 52, 56, 54, 51, 48, 51, 50, 57, 51, 52, 55,
281
+ ]);
282
+ const hmac = crypto_1.default.createHmac("sha1", TOTP_SECRET);
283
+ function generateTotp() {
284
+ const counter = Math.floor(Date.now() / 30000);
285
+ const counterBuffer = Buffer.alloc(8);
286
+ counterBuffer.writeBigInt64BE(BigInt(counter));
287
+ hmac.update(counterBuffer);
288
+ const hmacResult = hmac.digest();
289
+ const offset = hmacResult[hmacResult.length - 1] & 15;
290
+ const truncatedValue = ((hmacResult[offset] & 127) << 24) | ((hmacResult[offset + 1] & 255) << 16) | ((hmacResult[offset + 2] & 255) << 8) | (hmacResult[offset + 3] & 255);
291
+ const totp = (truncatedValue % 1000000).toString().padStart(6, "0");
292
+ return [totp, counter * 30000];
293
+ }
294
+ const [totp, timestamp] = generateTotp();
295
+ const params = {
296
+ reason: "transport",
297
+ productType: "embed",
298
+ totp: totp,
299
+ totpVer: 5,
300
+ ts: timestamp,
301
+ };
302
+ let body;
303
+ try {
304
+ const response = await axios_1.default.get("https://open.spotify.com/get_access_token", { params });
305
+ body = response.data;
306
+ }
307
+ catch (error) {
308
+ console.error("[AutoPlay] Failed to get spotify access token:", error.response?.status, error.response?.data || error.message);
309
+ return [];
310
+ }
311
+ let json;
312
+ try {
313
+ const response = await axios_1.default.get(`https://api.spotify.com/v1/recommendations`, {
314
+ params: { limit: 10, seed_tracks: track.identifier },
315
+ headers: {
316
+ Authorization: `Bearer ${body.accessToken}`,
317
+ "Content-Type": "application/json",
318
+ },
319
+ });
320
+ json = response.data;
321
+ }
322
+ catch (error) {
323
+ console.error("[AutoPlay] Failed to fetch spotify recommendations:", error.response?.status, error.response?.data || error.message);
324
+ return [];
325
+ }
326
+ if (!json.tracks || !json.tracks.length) {
327
+ return [];
328
+ }
329
+ const recommendedTrackId = json.tracks[Math.floor(Math.random() * json.tracks.length)].id;
330
+ const res = await this.manager.search({ query: `https://open.spotify.com/track/${recommendedTrackId}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
316
331
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
317
- console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
318
332
  return [];
319
333
  }
320
334
  if (res.loadType === LoadTypes.Playlist) {
321
- console.log("[AutoPlay] Search returned a playlist. Flattening tracks.");
322
335
  res.tracks = res.playlist.tracks;
323
336
  }
324
337
  if (!res.tracks.length) {
325
- console.error("[AutoPlay] No tracks found in the search. Returning an empty array.");
326
338
  return [];
327
339
  }
328
- console.log("[AutoPlay] Track found in search:", res.tracks[0].uri);
329
- track = res.tracks[0];
330
- }
331
- const TOTP_SECRET = new Uint8Array([
332
- 53, 53, 48, 55, 49, 52, 53, 56, 53, 51, 52, 56, 55, 52, 57, 57, 53, 57, 50, 50, 52, 56, 54, 51, 48, 51, 50, 57, 51, 52, 55,
333
- ]);
334
- const hmac = crypto_1.default.createHmac("sha1", TOTP_SECRET);
335
- function generateTotp() {
336
- const counter = Math.floor(Date.now() / 30000);
337
- const counterBuffer = Buffer.alloc(8);
338
- counterBuffer.writeBigInt64BE(BigInt(counter));
339
- hmac.update(counterBuffer);
340
- const hmacResult = hmac.digest();
341
- const offset = hmacResult[hmacResult.length - 1] & 15;
342
- const truncatedValue = ((hmacResult[offset] & 127) << 24) | ((hmacResult[offset + 1] & 255) << 16) | ((hmacResult[offset + 2] & 255) << 8) | (hmacResult[offset + 3] & 255);
343
- const totp = (truncatedValue % 1000000).toString().padStart(6, "0");
344
- return [totp, counter * 30000];
345
- }
346
- const [totp, timestamp] = generateTotp();
347
- console.log("[AutoPlay] Generated TOTP:", totp);
348
- const params = {
349
- reason: "transport",
350
- productType: "embed",
351
- totp: totp,
352
- totpVer: 5,
353
- ts: timestamp,
354
- };
355
- console.log("[AutoPlay] Sending request to get access token with params:", params);
356
- const { data: body } = await axios_1.default.get("https://open.spotify.com/get_access_token", { params });
357
- console.log("[AutoPlay] Access token received.");
358
- const { data: json } = await axios_1.default.get(`https://api.spotify.com/v1/recommendations`, {
359
- params: { limit: 10, seed_tracks: track.identifier },
360
- headers: {
361
- Authorization: `Bearer ${body.accessToken}`,
362
- "Content-Type": "application/json",
363
- },
364
- });
365
- if (!json.tracks || !json.tracks.length) {
366
- console.error("[AutoPlay] No recommended tracks received from Spotify API. Returning an empty array.");
367
- return [];
368
- }
369
- console.log("[AutoPlay] Recommended tracks received from Spotify.");
370
- // Return a random recommended track ID
371
- const recommendedTrackId = json.tracks[Math.floor(Math.random() * json.tracks.length)].id;
372
- console.log(`[AutoPlay] Selected random recommended track ID: ${recommendedTrackId}`);
373
- console.log("[AutoPlay] Searching for the recommended track:", recommendedTrackId);
374
- const res = await this.manager.search({ query: `https://open.spotify.com/track/${recommendedTrackId}`, source: Manager_1.SearchPlatform.Spotify }, track.requester);
375
- if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
376
- console.error("[AutoPlay] Final search returned empty or error result. Returning an empty array.");
377
- return [];
378
- }
379
- if (res.loadType === LoadTypes.Playlist) {
380
- console.log("[AutoPlay] Final search returned a playlist. Flattening tracks.");
381
- res.tracks = res.playlist.tracks;
340
+ return res.tracks;
382
341
  }
383
- if (!res.tracks.length) {
384
- console.error("[AutoPlay] No tracks found in final search. Returning an empty array.");
342
+ catch (error) {
343
+ console.error("[AutoPlay] Unexpected spotify error:", error.message || error);
385
344
  return [];
386
345
  }
387
- console.log("[AutoPlay] Recommended tracks found and ready to return.");
388
- return res.tracks;
346
+ break;
389
347
  case "deezer":
390
- console.log("[AutoPlay] Checking if track URI includes 'deezer':", track.uri);
391
348
  if (!track.uri.includes("deezer")) {
392
- console.log("[AutoPlay] Track URI does not include 'deezer'. Searching for track:", `${track.author} - ${track.title}`);
393
349
  const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.Deezer }, track.requester);
394
350
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
395
- console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
396
351
  return [];
397
352
  }
398
353
  if (res.loadType === LoadTypes.Playlist) {
399
- console.log("[AutoPlay] Search returned a playlist. Flattening tracks.");
400
354
  res.tracks = res.playlist.tracks;
401
355
  }
402
356
  if (!res.tracks.length) {
403
- console.error("[AutoPlay] No tracks found in the search. Returning an empty array.");
404
357
  return [];
405
358
  }
406
- console.log("[AutoPlay] Track found in search:", res.tracks[0].uri);
407
359
  track = res.tracks[0];
408
360
  }
409
361
  const identifier = `dzrec:${track.identifier}`;
410
- console.log("[AutoPlay] Generating Deezer recommendation identifier:", identifier);
411
362
  const recommendedResult = (await this.manager.useableNode.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
412
363
  if (!recommendedResult) {
413
- console.error("[AutoPlay] No recommended result received from Deezer. Returning an empty array.");
414
364
  return [];
415
365
  }
416
366
  let tracks = [];
@@ -418,15 +368,12 @@ class AutoPlayUtils {
418
368
  const requester = track.requester;
419
369
  switch (recommendedResult.loadType) {
420
370
  case LoadTypes.Search:
421
- console.log("[AutoPlay] Recommended result is of type 'Search'. Building tracks.");
422
371
  tracks = recommendedResult.data.map((track) => TrackUtils.build(track, requester));
423
372
  break;
424
373
  case LoadTypes.Track:
425
- console.log("[AutoPlay] Recommended result is of type 'Track'. Building a single track.");
426
374
  tracks = [TrackUtils.build(recommendedResult.data, requester)];
427
375
  break;
428
376
  case LoadTypes.Playlist: {
429
- console.log("[AutoPlay] Recommended result is of type 'Playlist'. Building playlist.");
430
377
  const playlistData = recommendedResult.data;
431
378
  tracks = playlistData.tracks.map((track) => TrackUtils.build(track, requester));
432
379
  playlist = {
@@ -441,42 +388,38 @@ class AutoPlayUtils {
441
388
  }
442
389
  const result = { loadType: recommendedResult.loadType, tracks, playlist };
443
390
  if (result.loadType === LoadTypes.Empty || result.loadType === LoadTypes.Error) {
444
- console.error("[AutoPlay] Final result load type is empty or error. Returning an empty array.");
445
391
  return [];
446
392
  }
447
393
  if (result.loadType === LoadTypes.Playlist) {
448
- console.log("[AutoPlay] Final result load type is Playlist. Flattening tracks.");
449
394
  result.tracks = result.playlist.tracks;
450
395
  }
451
396
  if (!result.tracks.length) {
452
- console.error("[AutoPlay] No tracks found in final result. Returning an empty array.");
453
397
  return [];
454
398
  }
455
- console.log("[AutoPlay] Tracks found and ready to return.");
456
399
  return result.tracks;
400
+ break;
457
401
  case "soundcloud":
458
- console.log("[AutoPlay] Checking if track URI includes 'soundcloud':", track.uri);
459
402
  if (!track.uri.includes("soundcloud")) {
460
- console.log("[AutoPlay] Track URI does not include 'soundcloud'. Searching for track:", `${track.author} - ${track.title}`);
461
403
  const res = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.SoundCloud }, track.requester);
462
404
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
463
- console.error("[AutoPlay] Search returned empty or error result. Returning an empty array.");
464
405
  return [];
465
406
  }
466
407
  if (res.loadType === LoadTypes.Playlist) {
467
- console.log("[AutoPlay] Search returned a playlist. Flattening tracks.");
468
408
  res.tracks = res.playlist.tracks;
469
409
  }
470
410
  if (!res.tracks.length) {
471
- console.error("[AutoPlay] No tracks found in the search. Returning an empty array.");
472
411
  return [];
473
412
  }
474
- console.log("[AutoPlay] Track found in search:", res.tracks[0].uri);
475
413
  track = res.tracks[0];
476
414
  }
477
415
  try {
478
- console.log("[AutoPlay] Fetching SoundCloud recommendations from:", `${track.uri}/recommended`);
479
- const recommendedRes = await axios_1.default.get(`${track.uri}/recommended`);
416
+ const recommendedRes = await axios_1.default.get(`${track.uri}/recommended`).catch((err) => {
417
+ console.error(`[AutoPlay] Failed to fetch SoundCloud recommendations. Status: ${err.response?.status || "Unknown"}`, err.message);
418
+ return null;
419
+ });
420
+ if (!recommendedRes) {
421
+ return [];
422
+ }
480
423
  const html = recommendedRes.data;
481
424
  const dom = new jsdom_1.JSDOM(html);
482
425
  const document = dom.window.document;
@@ -484,7 +427,6 @@ class AutoPlayUtils {
484
427
  const sectionElement = secondNoscript.querySelector("section");
485
428
  const articleElements = sectionElement.querySelectorAll("article");
486
429
  if (!articleElements || articleElements.length === 0) {
487
- console.error("[AutoPlay] No article elements found for recommendations. Returning an empty array.");
488
430
  return [];
489
431
  }
490
432
  const urls = Array.from(articleElements)
@@ -495,117 +437,55 @@ class AutoPlayUtils {
495
437
  })
496
438
  .filter(Boolean);
497
439
  if (!urls.length) {
498
- console.error("[AutoPlay] No valid URLs found in the recommendations. Returning an empty array.");
499
440
  return [];
500
441
  }
501
442
  const randomUrl = urls[Math.floor(Math.random() * urls.length)];
502
- console.log("[AutoPlay] Selected random URL for recommended track:", randomUrl);
503
443
  const res = await this.manager.search({ query: randomUrl, source: Manager_1.SearchPlatform.SoundCloud }, track.requester);
504
444
  if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
505
- console.error("[AutoPlay] Search for recommended track returned empty or error result. Returning an empty array.");
506
445
  return [];
507
446
  }
508
447
  if (res.loadType === LoadTypes.Playlist) {
509
- console.log("[AutoPlay] Search for recommended track returned a playlist. Flattening tracks.");
510
448
  res.tracks = res.playlist.tracks;
511
449
  }
512
450
  if (!res.tracks.length) {
513
- console.error("[AutoPlay] No tracks found in the search for recommended track. Returning an empty array.");
514
451
  return [];
515
452
  }
516
- console.log("[AutoPlay] Found recommended tracks:", res.tracks.map((track) => track.uri));
517
453
  return res.tracks;
518
454
  }
519
455
  catch (error) {
520
- console.error("[AutoPlay] Error occurred while fetching recommendations:", error);
456
+ console.error("[AutoPlay] Error occurred while fetching soundcloud recommendations:", error);
521
457
  return [];
522
458
  }
523
459
  break;
524
460
  case "youtube":
525
- return this.getRecommendedTracksFromYouTube(track);
526
- break;
461
+ const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
462
+ let videoID = null;
463
+ if (hasYouTubeURL) {
464
+ videoID = track.uri.split("=").pop();
465
+ }
466
+ else {
467
+ const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.YouTube }, track.requester);
468
+ videoID = searchResult.tracks[0]?.uri.split("=").pop();
469
+ }
470
+ if (!videoID) {
471
+ return [];
472
+ }
473
+ let randomIndex;
474
+ let searchURI;
475
+ do {
476
+ randomIndex = Math.floor(Math.random() * 23) + 2;
477
+ searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
478
+ } while (track.uri.includes(searchURI));
479
+ const res = await this.manager.search({ query: searchURI, source: Manager_1.SearchPlatform.YouTube }, track.requester);
480
+ if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
481
+ return [];
482
+ }
483
+ const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
484
+ return filteredTracks;
527
485
  default:
528
486
  return [];
529
487
  }
530
488
  }
531
- static async getRecommendedTracksFromYouTube(track) {
532
- console.log("[YouTube Recommendation] Checking if track URI includes YouTube URL:", track.uri);
533
- // Check if the previous track has a YouTube URL
534
- const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
535
- let videoID = null;
536
- if (hasYouTubeURL) {
537
- console.log("[YouTube Recommendation] Track contains a YouTube URL. Extracting video ID from URI.");
538
- videoID = track.uri.split("=").pop();
539
- }
540
- else {
541
- console.log("[YouTube Recommendation] Track does not contain a YouTube URL. Searching for the track on YouTube.");
542
- const searchResult = await this.manager.search({ query: `${track.author} - ${track.title}`, source: Manager_1.SearchPlatform.YouTube }, track.requester);
543
- videoID = searchResult.tracks[0]?.uri.split("=").pop();
544
- }
545
- if (!videoID) {
546
- console.error("[YouTube Recommendation] Video ID not found. Returning an empty array.");
547
- return [];
548
- }
549
- console.log("[YouTube Recommendation] Video ID extracted:", videoID);
550
- // Get a random video index between 2 and 24
551
- let randomIndex;
552
- let searchURI;
553
- do {
554
- randomIndex = Math.floor(Math.random() * 23) + 2; // Random index between 2 and 24
555
- searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}&index=${randomIndex}`;
556
- console.log("[YouTube Recommendation] Generated random search URI:", searchURI);
557
- } while (track.uri.includes(searchURI));
558
- // Search for the video and return false if the search fails
559
- console.log("[YouTube Recommendation] Searching for the video using search URI:", searchURI);
560
- const res = await this.manager.search({ query: searchURI, source: Manager_1.SearchPlatform.YouTube }, track.requester);
561
- if (res.loadType === LoadTypes.Empty || res.loadType === LoadTypes.Error) {
562
- console.error("[YouTube Recommendation] Search failed or returned empty results. Returning an empty array.");
563
- return [];
564
- }
565
- // Filter out tracks that have the same URI as the current track
566
- console.log("[YouTube Recommendation] Filtering tracks that do not match the current track URI.");
567
- const filteredTracks = res.tracks.filter((t) => t.uri !== track.uri);
568
- console.log("[YouTube Recommendation] Returning filtered recommended tracks:", filteredTracks.map((t) => t.uri));
569
- return filteredTracks;
570
- }
571
- static selectPlatform(enabledSources) {
572
- const { autoPlaySearchPlatform } = this.manager.options;
573
- const platformMapping = {
574
- [Manager_1.SearchPlatform.AppleMusic]: "applemusic",
575
- [Manager_1.SearchPlatform.Bandcamp]: "bandcamp",
576
- [Manager_1.SearchPlatform.Deezer]: "deezer",
577
- [Manager_1.SearchPlatform.Jiosaavn]: "jiosaavn",
578
- [Manager_1.SearchPlatform.SoundCloud]: "soundcloud",
579
- [Manager_1.SearchPlatform.Spotify]: "spotify",
580
- [Manager_1.SearchPlatform.Tidal]: "tidal",
581
- [Manager_1.SearchPlatform.VKMusic]: "vkmusic",
582
- [Manager_1.SearchPlatform.YouTube]: "youtube",
583
- [Manager_1.SearchPlatform.YouTubeMusic]: "youtube",
584
- };
585
- // Try the autoPlaySearchPlatform first
586
- if (enabledSources.includes(platformMapping[autoPlaySearchPlatform])) {
587
- return autoPlaySearchPlatform;
588
- }
589
- // Fallback to other platforms in a predefined order
590
- const fallbackPlatforms = [
591
- Manager_1.SearchPlatform.Spotify,
592
- Manager_1.SearchPlatform.Deezer,
593
- Manager_1.SearchPlatform.SoundCloud,
594
- Manager_1.SearchPlatform.AppleMusic,
595
- Manager_1.SearchPlatform.Bandcamp,
596
- Manager_1.SearchPlatform.Jiosaavn,
597
- Manager_1.SearchPlatform.Tidal,
598
- Manager_1.SearchPlatform.VKMusic,
599
- Manager_1.SearchPlatform.YouTubeMusic,
600
- Manager_1.SearchPlatform.YouTube,
601
- ];
602
- for (const platform of fallbackPlatforms) {
603
- if (enabledSources.includes(platformMapping[platform])) {
604
- return platform;
605
- }
606
- }
607
- return null;
608
- }
609
489
  }
610
490
  exports.AutoPlayUtils = AutoPlayUtils;
611
491
  /** Gets or extends structures to extend the built in, or already extended, classes to add more functionality. */
@@ -10,35 +10,38 @@ const Manager_1 = require("../structures/Manager");
10
10
  function managerCheck(options) {
11
11
  if (!options)
12
12
  throw new TypeError("ManagerOptions must not be empty.");
13
- const { playNextOnEnd, clientName, defaultSearchPlatform, autoPlaySearchPlatform, nodes, enabledPlugins, send, trackPartial, enablePriorityMode, useNode, normalizeYouTubeTitles, lastFmApiKey, maxPreviousTracks, } = options;
13
+ const { playNextOnEnd, clientName, defaultSearchPlatform, autoPlaySearchPlatforms, nodes, enabledPlugins, send, trackPartial, enablePriorityMode, useNode, normalizeYouTubeTitles, lastFmApiKey, maxPreviousTracks, } = options;
14
14
  // Validate playNextOnEnd option
15
15
  if (typeof playNextOnEnd !== "boolean") {
16
16
  throw new TypeError('Manager option "playNextOnEnd" must be a boolean.');
17
17
  }
18
18
  // Validate clientName option
19
- if (typeof clientName !== "undefined") {
19
+ if (typeof clientName !== undefined) {
20
20
  if (typeof clientName !== "string" || clientName.trim().length === 0) {
21
21
  throw new TypeError('Manager option "clientName" must be a non-empty string.');
22
22
  }
23
23
  }
24
24
  // Validate defaultSearchPlatform option
25
- if (typeof defaultSearchPlatform !== "undefined") {
25
+ if (typeof defaultSearchPlatform !== undefined) {
26
26
  if (!Object.values(Manager_1.SearchPlatform).includes(defaultSearchPlatform)) {
27
27
  throw new TypeError(`Manager option "defaultSearchPlatform" must be one of: ${Object.values(Manager_1.SearchPlatform).join(", ")}.`);
28
28
  }
29
29
  }
30
- // Validate autoPlaySearchPlatform
31
- if (typeof autoPlaySearchPlatform !== "undefined") {
32
- if (!Object.values(Manager_1.SearchPlatform).includes(autoPlaySearchPlatform)) {
33
- throw new TypeError(`Manager option "autoPlaySearchPlatform" must be one of: ${Object.values(Manager_1.SearchPlatform).join(", ")}.`);
30
+ // Validate autoPlaySearchPlatforms
31
+ if (autoPlaySearchPlatforms !== undefined) {
32
+ if (!Array.isArray(autoPlaySearchPlatforms)) {
33
+ throw new TypeError('Manager option "autoPlaySearchPlatforms" must be an array.');
34
+ }
35
+ if (!autoPlaySearchPlatforms.every((platform) => Object.values(Manager_1.AutoPlayPlatform).includes(platform))) {
36
+ throw new TypeError(`Manager option "autoPlaySearchPlatforms" must be an array of valid AutoPlayPlatform values.`);
34
37
  }
35
38
  }
36
39
  // Validate nodes option
37
- if (typeof nodes === "undefined" || !Array.isArray(nodes)) {
40
+ if (typeof nodes === undefined || !Array.isArray(nodes)) {
38
41
  throw new TypeError('Manager option "nodes" must be an array.');
39
42
  }
40
43
  // Validate enabledPlugins option
41
- if (typeof enabledPlugins !== "undefined" && !Array.isArray(enabledPlugins)) {
44
+ if (typeof enabledPlugins !== undefined && !Array.isArray(enabledPlugins)) {
42
45
  throw new TypeError('Manager option "enabledPlugins" must be a Plugin array.');
43
46
  }
44
47
  // Validate send option
@@ -46,7 +49,7 @@ function managerCheck(options) {
46
49
  throw new TypeError('Manager option "send" must be present and a function.');
47
50
  }
48
51
  // Validate trackPartial option
49
- if (typeof trackPartial !== "undefined") {
52
+ if (typeof trackPartial !== undefined) {
50
53
  if (!Array.isArray(trackPartial)) {
51
54
  throw new TypeError('Manager option "trackPartial" must be an array.');
52
55
  }
@@ -55,7 +58,7 @@ function managerCheck(options) {
55
58
  }
56
59
  }
57
60
  // Validate enablePriorityMode option
58
- if (typeof enablePriorityMode !== "undefined" && typeof enablePriorityMode !== "boolean") {
61
+ if (typeof enablePriorityMode !== undefined && typeof enablePriorityMode !== "boolean") {
59
62
  throw new TypeError('Manager option "enablePriorityMode" must be a boolean.');
60
63
  }
61
64
  // Validate node priority if enablePriorityMode is enabled
@@ -67,7 +70,7 @@ function managerCheck(options) {
67
70
  }
68
71
  }
69
72
  // Validate useNode option
70
- if (typeof useNode !== "undefined") {
73
+ if (typeof useNode !== undefined) {
71
74
  if (typeof useNode !== "string") {
72
75
  throw new TypeError('Manager option "useNode" must be a string "leastLoad" or "leastPlayers".');
73
76
  }
@@ -76,15 +79,15 @@ function managerCheck(options) {
76
79
  }
77
80
  }
78
81
  // Validate normalizeYouTubeTitles option
79
- if (typeof normalizeYouTubeTitles !== "undefined" && typeof normalizeYouTubeTitles !== "boolean") {
82
+ if (typeof normalizeYouTubeTitles !== undefined && typeof normalizeYouTubeTitles !== "boolean") {
80
83
  throw new TypeError('Manager option "normalizeYouTubeTitles" must be a boolean.');
81
84
  }
82
85
  // Validate lastFmApiKey option
83
- if (typeof lastFmApiKey !== "undefined" && (typeof lastFmApiKey !== "string" || lastFmApiKey.trim().length === 0)) {
86
+ if (typeof lastFmApiKey !== undefined && (typeof lastFmApiKey !== "string" || lastFmApiKey.trim().length === 0)) {
84
87
  throw new TypeError('Manager option "lastFmApiKey" must be a non-empty string.');
85
88
  }
86
89
  // Validate maxPreviousTracks option
87
- if (typeof maxPreviousTracks !== "undefined") {
90
+ if (typeof maxPreviousTracks !== undefined) {
88
91
  if (typeof maxPreviousTracks !== "number" || isNaN(maxPreviousTracks)) {
89
92
  throw new TypeError('Manager option "maxPreviousTracks" must be a number.');
90
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magmastream",
3
- "version": "2.9.0-dev.2",
3
+ "version": "2.9.0-dev.4",
4
4
  "description": "A user-friendly Lavalink client designed for NodeJS.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",