magmastream 2.5.8 → 2.5.10

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
@@ -265,6 +265,11 @@ declare class Player {
265
265
  * @param botUser
266
266
  */
267
267
  setAutoplay(autoplayState: boolean, botUser: object): this;
268
+ /**
269
+ * Gets recommended tracks and returns an array of tracks.
270
+ * @param track
271
+ */
272
+ getRecommended(track: Track): Promise<any[]>;
268
273
  /**
269
274
  * Sets the player volume.
270
275
  * @param volume
@@ -483,6 +488,8 @@ declare class Node {
483
488
  protected handleEvent(payload: PlayerEvent & PlayerEvents): Promise<void>;
484
489
  protected trackStart(player: Player, track: Track, payload: TrackStartEvent): void;
485
490
  protected trackEnd(player: Player, track: Track, payload: TrackEndEvent): Promise<void>;
491
+ extractSpotifyTrackID(url: string): string | null;
492
+ extractSpotifyArtistID(url: string): string | null;
486
493
  private handleAutoplay;
487
494
  private handleFailedTrack;
488
495
  private handleRepeatedTrack;
@@ -556,6 +563,29 @@ interface FrameStats {
556
563
  /** The amount of deficit frames. */
557
564
  deficit?: number;
558
565
  }
566
+ interface LavalinkInfo {
567
+ version: {
568
+ semver: string;
569
+ major: number;
570
+ minor: number;
571
+ patch: number;
572
+ preRelease: string;
573
+ };
574
+ buildTime: number;
575
+ git: {
576
+ branch: string;
577
+ commit: string;
578
+ commitTime: number;
579
+ };
580
+ jvm: string;
581
+ lavaplayer: string;
582
+ sourceManagers: string[];
583
+ filters: string[];
584
+ plugins: {
585
+ name: string;
586
+ version: string;
587
+ }[];
588
+ }
559
589
 
560
590
  declare abstract class TrackUtils {
561
591
  static trackPartial: string[] | null;
@@ -846,7 +876,7 @@ interface ManagerOptions {
846
876
  */
847
877
  send(id: string, payload: Payload): void;
848
878
  }
849
- type SearchPlatform = "deezer" | "soundcloud" | "youtube music" | "youtube";
879
+ type SearchPlatform = "deezer" | "soundcloud" | "youtube music" | "youtube" | "spotify" | "jiosaavn" | "tidal" | "applemusic" | "bandcamp";
850
880
  interface SearchQuery {
851
881
  /** The source to search from. */
852
882
  source?: SearchPlatform | string;
@@ -907,4 +937,4 @@ interface ManagerEvents {
907
937
  trackError: [player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent];
908
938
  }
909
939
 
910
- export { type CPUStats, type EqualizerBand, type Exception, type Extendable, type FrameStats, type LavalinkResponse, type LoadType, Manager, type ManagerEvents, type ManagerOptions, type MemoryStats, Node, type NodeMessage, type NodeOptions, type NodeStats, type Payload, type PlayOptions, Player, type PlayerEvent, type PlayerEventType, type PlayerEvents, type PlayerOptions, type PlayerUpdate, type PlaylistData, type PlaylistRawData, Plugin, Queue, type SearchPlatform, type SearchQuery, type SearchResult, type Severity, type Sizes, type State, Structure, type Track, type TrackData, type TrackDataInfo, type TrackEndEvent, type TrackEndReason, type TrackExceptionEvent, type TrackPluginInfo, type TrackSourceName, type TrackStartEvent, type TrackStuckEvent, TrackUtils, type UnresolvedQuery, type UnresolvedTrack, type VoicePacket, type VoiceServer, type VoiceState, type WebSocketClosedEvent };
940
+ export { type CPUStats, type EqualizerBand, type Exception, type Extendable, type FrameStats, type LavalinkInfo, type LavalinkResponse, type LoadType, Manager, type ManagerEvents, type ManagerOptions, type MemoryStats, Node, type NodeMessage, type NodeOptions, type NodeStats, type Payload, type PlayOptions, Player, type PlayerEvent, type PlayerEventType, type PlayerEvents, type PlayerOptions, type PlayerUpdate, type PlaylistData, type PlaylistRawData, Plugin, Queue, type SearchPlatform, type SearchQuery, type SearchResult, type Severity, type Sizes, type State, Structure, type Track, type TrackData, type TrackDataInfo, type TrackEndEvent, type TrackEndReason, type TrackExceptionEvent, type TrackPluginInfo, type TrackSourceName, type TrackStartEvent, type TrackStuckEvent, TrackUtils, type UnresolvedQuery, type UnresolvedTrack, type VoicePacket, type VoiceServer, type VoiceState, type WebSocketClosedEvent };
@@ -17,8 +17,13 @@ class Manager extends events_1.EventEmitter {
17
17
  static DEFAULT_SOURCES = {
18
18
  "youtube music": "ytmsearch",
19
19
  youtube: "ytsearch",
20
+ spotify: "spsearch",
21
+ jiosaavn: "jssearch",
20
22
  soundcloud: "scsearch",
21
23
  deezer: "dzsearch",
24
+ tidal: "tdsearch",
25
+ applemusic: "amsearch",
26
+ bandcamp: "bcsearch",
22
27
  };
23
28
  /** The map of players. */
24
29
  players = new collection_1.Collection();
@@ -247,15 +247,59 @@ class Node {
247
247
  await this.queueEnd(player, track, payload);
248
248
  }
249
249
  }
250
+ extractSpotifyTrackID(url) {
251
+ const regex = /https:\/\/open\.spotify\.com\/track\/([a-zA-Z0-9]+)/;
252
+ const match = url.match(regex);
253
+ return match ? match[1] : null;
254
+ }
255
+ extractSpotifyArtistID(url) {
256
+ const regex = /https:\/\/open\.spotify\.com\/artist\/([a-zA-Z0-9]+)/;
257
+ const match = url.match(regex);
258
+ return match ? match[1] : null;
259
+ }
250
260
  // Handle autoplay
251
261
  async handleAutoplay(player, track) {
252
262
  const previousTrack = player.queue.previous;
253
263
  if (!player.isAutoplay || !previousTrack)
254
264
  return;
265
+ const hasSpotifyURL = ["spotify.com", "open.spotify.com"].some((url) => previousTrack.uri.includes(url));
266
+ if (hasSpotifyURL) {
267
+ const node = this.manager.useableNodes;
268
+ const res = await node.rest.get(`/v4/info`);
269
+ const info = res;
270
+ const isSpotifyPluginEnabled = info.plugins.some((plugin) => plugin.name === "lavasrc-plugin");
271
+ const isSpotifySourceManagerEnabled = info.sourceManagers.includes("spotify");
272
+ if (isSpotifyPluginEnabled && isSpotifySourceManagerEnabled) {
273
+ const trackID = this.extractSpotifyTrackID(previousTrack.uri);
274
+ const artistID = this.extractSpotifyArtistID(previousTrack.pluginInfo.artistUrl);
275
+ let identifier = "";
276
+ if (trackID && artistID) {
277
+ identifier = `sprec:seed_artists=${artistID}&seed_tracks=${trackID}`;
278
+ }
279
+ else if (trackID) {
280
+ identifier = `sprec:seed_tracks=${trackID}`;
281
+ }
282
+ else if (artistID) {
283
+ identifier = `sprec:seed_artists=${artistID}`;
284
+ }
285
+ if (identifier) {
286
+ const recommendedResult = (await node.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
287
+ if (recommendedResult.loadType === "playlist") {
288
+ const playlistData = recommendedResult.data;
289
+ const recommendedTrack = playlistData.tracks[0];
290
+ if (recommendedTrack) {
291
+ player.queue.add(Utils_1.TrackUtils.build(recommendedTrack, player.get("Internal_BotUser")));
292
+ player.play();
293
+ return;
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
255
299
  const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => previousTrack.uri.includes(url));
256
300
  let videoID = previousTrack.uri.substring(previousTrack.uri.indexOf("=") + 1);
257
301
  if (!hasYouTubeURL) {
258
- const res = await player.search(`${previousTrack.author} - ${previousTrack.title}`);
302
+ const res = await player.search(`${previousTrack.author} - ${previousTrack.title}`, player.get("Internal_BotUser"));
259
303
  videoID = res.tracks[0].uri.substring(res.tracks[0].uri.indexOf("=") + 1);
260
304
  }
261
305
  let randomIndex;
@@ -273,6 +317,15 @@ class Node {
273
317
  }
274
318
  const foundTrack = tracks.sort(() => Math.random() - 0.5).find((shuffledTrack) => shuffledTrack.uri !== track.uri);
275
319
  if (foundTrack) {
320
+ if (this.manager.options.replaceYouTubeCredentials) {
321
+ foundTrack.author = foundTrack.author.replace("- Topic", "");
322
+ foundTrack.title = foundTrack.title.replace("Topic -", "");
323
+ if (foundTrack.title.includes("-")) {
324
+ const [author, title] = foundTrack.title.split("-").map((str) => str.trim());
325
+ foundTrack.author = author;
326
+ foundTrack.title = title;
327
+ }
328
+ }
276
329
  player.queue.add(foundTrack);
277
330
  player.play();
278
331
  }
@@ -231,6 +231,90 @@ class Player {
231
231
  this.set("Internal_BotUser", botUser);
232
232
  return this;
233
233
  }
234
+ /**
235
+ * Gets recommended tracks and returns an array of tracks.
236
+ * @param track
237
+ */
238
+ async getRecommended(track) {
239
+ const node = this.manager.useableNodes;
240
+ if (!node) {
241
+ throw new Error("No available nodes.");
242
+ }
243
+ const hasSpotifyURL = ["spotify.com", "open.spotify.com"].some((url) => track.uri.includes(url));
244
+ const hasYouTubeURL = ["youtube.com", "youtu.be"].some((url) => track.uri.includes(url));
245
+ if (hasSpotifyURL) {
246
+ const res = await node.rest.get(`/v4/info`);
247
+ const info = res;
248
+ const isSpotifyPluginEnabled = info.plugins.some((plugin) => plugin.name === "lavasrc-plugin");
249
+ const isSpotifySourceManagerEnabled = info.sourceManagers.includes("spotify");
250
+ if (isSpotifyPluginEnabled && isSpotifySourceManagerEnabled) {
251
+ const trackID = node.extractSpotifyTrackID(track.uri);
252
+ const artistID = node.extractSpotifyArtistID(track.pluginInfo.artistUrl);
253
+ let identifier = "";
254
+ if (trackID && artistID) {
255
+ identifier = `sprec:seed_artists=${artistID}&seed_tracks=${trackID}`;
256
+ }
257
+ else if (trackID) {
258
+ identifier = `sprec:seed_tracks=${trackID}`;
259
+ }
260
+ else if (artistID) {
261
+ identifier = `sprec:seed_artists=${artistID}`;
262
+ }
263
+ if (identifier) {
264
+ const recommendedResult = (await node.rest.get(`/v4/loadtracks?identifier=${encodeURIComponent(identifier)}`));
265
+ if (recommendedResult.loadType === "playlist") {
266
+ const playlistData = recommendedResult.data;
267
+ const recommendedTracks = playlistData.tracks;
268
+ if (recommendedTracks) {
269
+ const spotifyArray = [];
270
+ recommendedTracks.forEach((song) => {
271
+ const track = {
272
+ track: song.encoded,
273
+ title: song.info.title,
274
+ identifier: song.info.title,
275
+ author: song.info.author,
276
+ duration: song.info.length,
277
+ uri: song.info.uri,
278
+ artworkUrl: song.info.artworkUrl,
279
+ sourceName: song.info.sourceName,
280
+ requester: undefined,
281
+ plugininfo: song.pluginInfo,
282
+ };
283
+ spotifyArray.push(track);
284
+ });
285
+ return spotifyArray;
286
+ }
287
+ }
288
+ }
289
+ }
290
+ }
291
+ let videoID = track.uri.substring(track.uri.indexOf("=") + 1);
292
+ if (!hasYouTubeURL) {
293
+ const res = await this.manager.search(`${track.author} - ${track.title}`);
294
+ videoID = res.tracks[0].uri.substring(res.tracks[0].uri.indexOf("=") + 1);
295
+ }
296
+ const searchURI = `https://www.youtube.com/watch?v=${videoID}&list=RD${videoID}`;
297
+ const res = await this.manager.search(searchURI);
298
+ if (res.loadType === "empty" || res.loadType === "error")
299
+ return;
300
+ let tracks = res.tracks;
301
+ if (res.loadType === "playlist") {
302
+ tracks = res.playlist.tracks;
303
+ }
304
+ const filteredTracks = tracks.filter((track) => track.uri !== `https://www.youtube.com/watch?v=${videoID}`);
305
+ if (this.manager.options.replaceYouTubeCredentials) {
306
+ for (const track of filteredTracks) {
307
+ track.author = track.author.replace("- Topic", "");
308
+ track.title = track.title.replace("Topic -", "");
309
+ if (track.title.includes("-")) {
310
+ const [author, title] = track.title.split("-").map((str) => str.trim());
311
+ track.author = author;
312
+ track.title = title;
313
+ }
314
+ }
315
+ }
316
+ return filteredTracks;
317
+ }
234
318
  /**
235
319
  * Sets the player volume.
236
320
  * @param volume
@@ -157,7 +157,9 @@ class TrackUtils {
157
157
  if (sameDuration)
158
158
  return sameDuration;
159
159
  }
160
- return res.tracks[0];
160
+ const finalTrack = res.tracks[0];
161
+ finalTrack.customData = unresolvedTrack.customData;
162
+ return finalTrack;
161
163
  }
162
164
  }
163
165
  exports.TrackUtils = TrackUtils;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magmastream",
3
- "version": "2.5.8",
3
+ "version": "2.5.10",
4
4
  "description": "A user-friendly Lavalink client designed for NodeJS.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,11 +15,11 @@
15
15
  },
16
16
  "devDependencies": {
17
17
  "@favware/rollup-type-bundler": "^3.3.0",
18
- "@types/lodash": "^4.17.6",
19
- "@types/node": "^20.14.10",
20
- "@types/ws": "^8.5.10",
21
- "@typescript-eslint/eslint-plugin": "^7.16.0",
22
- "@typescript-eslint/parser": "^7.16.0",
18
+ "@types/lodash": "^4.17.7",
19
+ "@types/node": "^20.14.14",
20
+ "@types/ws": "^8.5.12",
21
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
22
+ "@typescript-eslint/parser": "^7.18.0",
23
23
  "eslint": "^8.57.0",
24
24
  "npm-run-all": "^4.1.5",
25
25
  "typedoc": "^0.25.13",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@discordjs/collection": "^2.1.0",
31
- "axios": "^1.7.2",
31
+ "axios": "^1.7.3",
32
32
  "events": "^3.3.0",
33
33
  "lodash": "^4.17.21",
34
34
  "tslib": "^2.6.3",