lavalink-client 1.1.1 → 1.1.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.
@@ -91,6 +91,8 @@ class LavalinkNode {
91
91
  this.calls++;
92
92
  if (options.method === "DELETE")
93
93
  return;
94
+ if (request.statusCode === 404)
95
+ throw new Error(`Node Request resulted into an error, request-URL: ${url} | headers: ${JSON.stringify(request.headers)}`);
94
96
  return parseAsText ? await request.body.text() : await request.body.json();
95
97
  }
96
98
  /**
@@ -2,8 +2,8 @@ import { EQBand, FilterData, FilterManager, LavalinkFilterData } from "./Filters
2
2
  import { LavalinkManager } from "./LavalinkManager";
3
3
  import { LavalinkNode } from "./Node";
4
4
  import { Queue } from "./Queue";
5
- import { Track } from "./Track";
6
- import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult } from "./Utils";
5
+ import { Track, UnresolvedTrack } from "./Track";
6
+ import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult, LavaSearchType, LavaSearchResponse, LavaSrcSearchPlatformBase } from "./Utils";
7
7
  type PlayerDestroyReasons = "QueueEmpty" | "NodeDestroy" | "NodeDeleted" | "LavalinkNoVoice" | "NodeReconnectFail" | "PlayerReconnectFail" | "Disconnected" | "ChannelDeleted";
8
8
  export type DestroyReasonsType = PlayerDestroyReasons | string;
9
9
  export declare const DestroyReasons: Record<PlayerDestroyReasons, PlayerDestroyReasons>;
@@ -43,7 +43,7 @@ export interface PlayerOptions {
43
43
  }
44
44
  export interface PlayOptions {
45
45
  /** Which Track to play | don't provide, if it should pick from the Queue */
46
- track?: Track;
46
+ track?: Track | UnresolvedTrack;
47
47
  /** Encoded Track to use, instead of the queue system... */
48
48
  encodedTrack?: string | null;
49
49
  /** Encoded Track to use&search, instead of the queue system (yt only)... */
@@ -138,6 +138,11 @@ export declare class Player {
138
138
  * @param ignoreVolumeDecrementer If it should ignore the volumedecrementer option
139
139
  */
140
140
  setVolume(volume: number, ignoreVolumeDecrementer?: boolean): Promise<void>;
141
+ lavaSearch(query: {
142
+ query: string;
143
+ source: LavaSrcSearchPlatformBase;
144
+ types?: LavaSearchType[];
145
+ }, requestUser: unknown): Promise<SearchResult | LavaSearchResponse>;
141
146
  /**
142
147
  *
143
148
  * @param query Query for your data
@@ -123,7 +123,9 @@ class Player {
123
123
  clearTimeout(this.get("internal_queueempty"));
124
124
  this.set("internal_queueempty", undefined);
125
125
  }
126
- if (options?.track && this.LavalinkManager.utils.isTrack(options?.track)) {
126
+ if (options?.track && (this.LavalinkManager.utils.isTrack(options?.track) || this.LavalinkManager.utils.isUnresolvedTrack(options.track))) {
127
+ if (this.LavalinkManager.utils.isUnresolvedTrack(options.track))
128
+ await options.track.resolve(this);
127
129
  await this.queue.add(options?.track, 0);
128
130
  await (0, Utils_1.queueTrackEnd)(this);
129
131
  }
@@ -131,13 +133,17 @@ class Player {
131
133
  await (0, Utils_1.queueTrackEnd)(this);
132
134
  // @ts-ignore
133
135
  if (this.queue.current && this.LavalinkManager.utils.isUnresolvedTrack(this.queue.current)) {
134
- try {
135
- this.queue.current = await this.LavalinkManager.utils.getClosestTrack({ ...(this.queue.current || {}) }, this);
136
+ try { // @ts-ignore
137
+ this.queue.current = await this.queue.current.resolve(this);
136
138
  }
137
139
  catch (error) {
138
140
  this.LavalinkManager.emit("trackError", this, this.queue.current, error);
141
+ if (options && "track" in options)
142
+ delete options.track;
143
+ if (options && "encodedTrack" in options)
144
+ delete options.encodedTrack;
139
145
  if (this.queue.tracks[0])
140
- return this.play();
146
+ return this.play(options);
141
147
  return;
142
148
  }
143
149
  }
@@ -202,6 +208,37 @@ class Player {
202
208
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
203
209
  return;
204
210
  }
211
+ async lavaSearch(query, requestUser) {
212
+ // transform the query object
213
+ const Query = {
214
+ query: typeof query === "string" ? query : query.query,
215
+ types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(v => query.types?.find(x => x.toLowerCase().startsWith(v))) : ["track", "playlist", "artist", "album", "text"],
216
+ source: LavalinkManagerStatics_1.DefaultSources[(typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform.toLowerCase()] ?? (typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform
217
+ };
218
+ // if user does player.search("ytsearch:Hello")
219
+ const foundSource = [...Object.keys(LavalinkManagerStatics_1.DefaultSources)].find(source => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
220
+ if (foundSource && LavalinkManagerStatics_1.DefaultSources[foundSource]) {
221
+ Query.source = LavalinkManagerStatics_1.DefaultSources[foundSource]; // set the source to ytsearch:
222
+ Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length); // remove ytsearch: from the query
223
+ }
224
+ if (Query.source)
225
+ this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
226
+ if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source))
227
+ throw new SyntaxError(`Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`);
228
+ if (/^https?:\/\//.test(Query.query))
229
+ return await this.search({ query: Query.query, source: Query.source }, requestUser);
230
+ if (!this.node.info.plugins.find(v => v.name === "lavasearch-plugin"))
231
+ throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.node.id}`);
232
+ const res = await this.node.request(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
233
+ return {
234
+ tracks: res.tracks?.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
235
+ albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
236
+ artists: res.artists?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
237
+ playlists: res.playlists?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
238
+ texts: res.texts?.map(v => ({ text: v.text, pluginInfo: v?.plugin || v.pluginInfo })) || [],
239
+ pluginInfo: res.pluginInfo || res?.plugin
240
+ };
241
+ }
205
242
  /**
206
243
  *
207
244
  * @param query Query for your data
@@ -211,14 +248,18 @@ class Player {
211
248
  // transform the query object
212
249
  const Query = {
213
250
  query: typeof query === "string" ? query : query.query,
214
- 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
251
+ source: LavalinkManagerStatics_1.DefaultSources[(typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform.toLowerCase()] ?? (typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform
215
252
  };
216
253
  // if user does player.search("ytsearch:Hello")
217
- const foundSource = [...Object.keys(LavalinkManagerStatics_1.DefaultSources)].find(source => Query.query.startsWith(`${source}:`));
254
+ const foundSource = [...Object.keys(LavalinkManagerStatics_1.DefaultSources)].find(source => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
218
255
  if (foundSource && LavalinkManagerStatics_1.DefaultSources[foundSource]) {
219
256
  Query.source = LavalinkManagerStatics_1.DefaultSources[foundSource]; // set the source to ytsearch:
220
- Query.query = Query.query.replace(`${foundSource}:`, ""); // remove ytsearch: from the query
257
+ Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length); // remove ytsearch: from the query
221
258
  }
259
+ if (/^https?:\/\//.test(Query.query))
260
+ this.LavalinkManager.utils.validatedQueryString(this.node, Query.source);
261
+ else if (Query.source)
262
+ this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
222
263
  // ftts query parameters: ?voice=Olivia&audio_format=ogg_opus&translate=False&silence=1000&speed=1.0 | example raw get query: https://api.flowery.pw/v1/tts?voice=Olivia&audio_format=ogg_opus&translate=False&silence=0&speed=1.0&text=Hello%20World
223
264
  // request the data
224
265
  const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:${Query.source === "ftts" ? "//" : ""}` : ""}${encodeURIComponent(Query.query)}`);
@@ -17,7 +17,21 @@ export interface TrackInfo {
17
17
  }
18
18
  export interface PluginInfo {
19
19
  /** The Type provided by a plugin */
20
- type?: string;
20
+ type?: "album" | "playlist" | "artist" | "recommendations" | string;
21
+ /** The Identifier provided by a plugin */
22
+ albumName?: string;
23
+ /** The url of the album art */
24
+ albumArtUrl?: string;
25
+ /** The url of the artist */
26
+ artistUrl?: string;
27
+ /** The url of the artist artwork */
28
+ artistArtworkUrl?: string;
29
+ /** The url of the preview */
30
+ previewUrl?: string;
31
+ /** Whether the track is a preview */
32
+ isPreview?: boolean;
33
+ /** The total number of tracks in the playlist */
34
+ totalTracks?: number;
21
35
  /** The Identifier provided by a plugin */
22
36
  identifier?: string;
23
37
  /** The ArtworkUrl provided by a plugin */
@@ -7,13 +7,18 @@ export declare const TrackSymbol: unique symbol;
7
7
  export declare const UnresolvedTrackSymbol: unique symbol;
8
8
  export declare const QueueSymbol: unique symbol;
9
9
  export declare const NodeSymbol: unique symbol;
10
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch" | "speak" | "tts" | "ftts";
10
+ export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
11
+ export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
12
+ export type DuncteSearchPlatform = "speak" | "tts";
13
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform;
11
14
  export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
12
15
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
13
16
  export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
14
17
  export interface PlaylistInfo {
15
18
  /** The playlist title. */
16
19
  title: string;
20
+ /** The playlist name (if provided instead of title) */
21
+ name: string;
17
22
  /** The Playlist Author */
18
23
  author?: string;
19
24
  /** The Playlist Thumbnail */
@@ -72,7 +77,8 @@ export declare class ManagerUitls {
72
77
  * @param requester
73
78
  */
74
79
  buildUnresolvedTrack(query: UnresolvedQuery | UnresolvedTrack, requester: unknown): UnresolvedTrack;
75
- validatedQuery(queryString: string, node: LavalinkNode): void;
80
+ validatedQueryString(node: LavalinkNode, queryString: string): void;
81
+ validateSourceString(node: LavalinkNode, sourceString: SearchPlatform): void;
76
82
  }
77
83
  /**
78
84
  * @internal
@@ -282,4 +288,27 @@ export interface NodeMessage extends NodeStats {
282
288
  guildId: string;
283
289
  }
284
290
  export declare function queueTrackEnd(player: Player): Promise<Track>;
291
+ export type LavaSearchType = "track" | "album" | "artist" | "playlist" | "text" | "tracks" | "albums" | "artists" | "playlists" | "texts";
292
+ export interface LavaSearchFilteredResponse {
293
+ info: PlaylistInfo;
294
+ pluginInfo: PluginInfo;
295
+ tracks: Track[];
296
+ }
297
+ export interface LavaSearchResponse {
298
+ /** An array of tracks, only present if track is in types */
299
+ tracks: Track[];
300
+ /** An array of albums, only present if album is in types */
301
+ albums: LavaSearchFilteredResponse[];
302
+ /** An array of artists, only present if artist is in types */
303
+ artists: LavaSearchFilteredResponse[];
304
+ /** An array of playlists, only present if playlist is in types */
305
+ playlists: LavaSearchFilteredResponse[];
306
+ /** An array of text results, only present if text is in types */
307
+ texts: {
308
+ text: string;
309
+ pluginInfo: PluginInfo;
310
+ }[];
311
+ /** Addition result data provided by plugins */
312
+ pluginInfo: PluginInfo;
313
+ }
285
314
  export {};
@@ -148,7 +148,7 @@ class ManagerUitls {
148
148
  Object.defineProperty(unresolvedTrack, exports.UnresolvedTrackSymbol, { configurable: true, value: true });
149
149
  return unresolvedTrack;
150
150
  }
151
- validatedQuery(queryString, node) {
151
+ validatedQueryString(node, queryString) {
152
152
  if (!node.info)
153
153
  throw new Error("No Lavalink Node was provided");
154
154
  if (!node.info.sourceManagers?.length)
@@ -190,12 +190,14 @@ class ManagerUitls {
190
190
  if (LavalinkManagerStatics_1.SourceLinksRegexes.musicYandex.test(queryString) && !node.info.sourceManagers.includes("yandexmusic")) {
191
191
  throw new Error("Lavalink Node has not 'yandexmusic' enabled");
192
192
  }
193
- const hasSource = queryString.split(":")[0];
194
- if (queryString.split(" ").length <= 1 || !queryString.split(" ")[0].includes(":"))
195
- return;
196
- const source = LavalinkManagerStatics_1.DefaultSources[hasSource];
193
+ return;
194
+ }
195
+ validateSourceString(node, sourceString) {
196
+ if (!sourceString)
197
+ throw new Error(`No SourceString was provided`);
198
+ const source = LavalinkManagerStatics_1.DefaultSources[sourceString.toLowerCase()] || Object.values(LavalinkManagerStatics_1.DefaultSources).find(v => v.toLowerCase() === sourceString?.toLowerCase());
197
199
  if (!source)
198
- throw new Error(`Lavalink Node SearchQuerySource: '${hasSource}' is not available`);
200
+ throw new Error(`Lavalink Node SearchQuerySource: '${sourceString}' is not available`);
199
201
  if (source === "amsearch" && !node.info.sourceManagers.includes("applemusic")) {
200
202
  throw new Error("Lavalink Node has not 'applemusic' enabled, which is required to have 'amsearch' work");
201
203
  }
@@ -87,6 +87,8 @@ export class LavalinkNode {
87
87
  this.calls++;
88
88
  if (options.method === "DELETE")
89
89
  return;
90
+ if (request.statusCode === 404)
91
+ throw new Error(`Node Request resulted into an error, request-URL: ${url} | headers: ${JSON.stringify(request.headers)}`);
90
92
  return parseAsText ? await request.body.text() : await request.body.json();
91
93
  }
92
94
  /**
@@ -2,8 +2,8 @@ import { EQBand, FilterData, FilterManager, LavalinkFilterData } from "./Filters
2
2
  import { LavalinkManager } from "./LavalinkManager";
3
3
  import { LavalinkNode } from "./Node";
4
4
  import { Queue } from "./Queue";
5
- import { Track } from "./Track";
6
- import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult } from "./Utils";
5
+ import { Track, UnresolvedTrack } from "./Track";
6
+ import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult, LavaSearchType, LavaSearchResponse, LavaSrcSearchPlatformBase } from "./Utils";
7
7
  type PlayerDestroyReasons = "QueueEmpty" | "NodeDestroy" | "NodeDeleted" | "LavalinkNoVoice" | "NodeReconnectFail" | "PlayerReconnectFail" | "Disconnected" | "ChannelDeleted";
8
8
  export type DestroyReasonsType = PlayerDestroyReasons | string;
9
9
  export declare const DestroyReasons: Record<PlayerDestroyReasons, PlayerDestroyReasons>;
@@ -43,7 +43,7 @@ export interface PlayerOptions {
43
43
  }
44
44
  export interface PlayOptions {
45
45
  /** Which Track to play | don't provide, if it should pick from the Queue */
46
- track?: Track;
46
+ track?: Track | UnresolvedTrack;
47
47
  /** Encoded Track to use, instead of the queue system... */
48
48
  encodedTrack?: string | null;
49
49
  /** Encoded Track to use&search, instead of the queue system (yt only)... */
@@ -138,6 +138,11 @@ export declare class Player {
138
138
  * @param ignoreVolumeDecrementer If it should ignore the volumedecrementer option
139
139
  */
140
140
  setVolume(volume: number, ignoreVolumeDecrementer?: boolean): Promise<void>;
141
+ lavaSearch(query: {
142
+ query: string;
143
+ source: LavaSrcSearchPlatformBase;
144
+ types?: LavaSearchType[];
145
+ }, requestUser: unknown): Promise<SearchResult | LavaSearchResponse>;
141
146
  /**
142
147
  *
143
148
  * @param query Query for your data
@@ -120,7 +120,9 @@ export class Player {
120
120
  clearTimeout(this.get("internal_queueempty"));
121
121
  this.set("internal_queueempty", undefined);
122
122
  }
123
- if (options?.track && this.LavalinkManager.utils.isTrack(options?.track)) {
123
+ if (options?.track && (this.LavalinkManager.utils.isTrack(options?.track) || this.LavalinkManager.utils.isUnresolvedTrack(options.track))) {
124
+ if (this.LavalinkManager.utils.isUnresolvedTrack(options.track))
125
+ await options.track.resolve(this);
124
126
  await this.queue.add(options?.track, 0);
125
127
  await queueTrackEnd(this);
126
128
  }
@@ -128,13 +130,17 @@ export class Player {
128
130
  await queueTrackEnd(this);
129
131
  // @ts-ignore
130
132
  if (this.queue.current && this.LavalinkManager.utils.isUnresolvedTrack(this.queue.current)) {
131
- try {
132
- this.queue.current = await this.LavalinkManager.utils.getClosestTrack({ ...(this.queue.current || {}) }, this);
133
+ try { // @ts-ignore
134
+ this.queue.current = await this.queue.current.resolve(this);
133
135
  }
134
136
  catch (error) {
135
137
  this.LavalinkManager.emit("trackError", this, this.queue.current, error);
138
+ if (options && "track" in options)
139
+ delete options.track;
140
+ if (options && "encodedTrack" in options)
141
+ delete options.encodedTrack;
136
142
  if (this.queue.tracks[0])
137
- return this.play();
143
+ return this.play(options);
138
144
  return;
139
145
  }
140
146
  }
@@ -199,6 +205,37 @@ export class Player {
199
205
  this.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
200
206
  return;
201
207
  }
208
+ async lavaSearch(query, requestUser) {
209
+ // transform the query object
210
+ const Query = {
211
+ query: typeof query === "string" ? query : query.query,
212
+ types: query.types ? ["track", "playlist", "artist", "album", "text"].filter(v => query.types?.find(x => x.toLowerCase().startsWith(v))) : ["track", "playlist", "artist", "album", "text"],
213
+ source: DefaultSources[(typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform.toLowerCase()] ?? (typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform
214
+ };
215
+ // if user does player.search("ytsearch:Hello")
216
+ const foundSource = [...Object.keys(DefaultSources)].find(source => Query.query.toLowerCase().startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
217
+ if (foundSource && DefaultSources[foundSource]) {
218
+ Query.source = DefaultSources[foundSource]; // set the source to ytsearch:
219
+ Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length); // remove ytsearch: from the query
220
+ }
221
+ if (Query.source)
222
+ this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
223
+ if (!["spsearch", "sprec", "amsearch", "dzsearch", "dzisrc", "ytmsearch", "ytsearch"].includes(Query.source))
224
+ throw new SyntaxError(`Query.source must be a source from LavaSrc: "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ytmsearch" | "ytsearch"`);
225
+ if (/^https?:\/\//.test(Query.query))
226
+ return await this.search({ query: Query.query, source: Query.source }, requestUser);
227
+ if (!this.node.info.plugins.find(v => v.name === "lavasearch-plugin"))
228
+ throw new RangeError(`there is no lavasearch-plugin available in the lavalink node: ${this.node.id}`);
229
+ const res = await this.node.request(`/loadsearch?query=${Query.source ? `${Query.source}:` : ""}${encodeURIComponent(Query.query)}${Query.types?.length ? `&types=${Query.types.join(",")}` : ""}`);
230
+ return {
231
+ tracks: res.tracks?.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) || [],
232
+ albums: res.albums?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
233
+ artists: res.artists?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
234
+ playlists: res.playlists?.map(v => ({ info: v.info, pluginInfo: v?.plugin || v.pluginInfo, tracks: v.tracks.map(v => this.LavalinkManager.utils.buildTrack(v, requestUser)) })) || [],
235
+ texts: res.texts?.map(v => ({ text: v.text, pluginInfo: v?.plugin || v.pluginInfo })) || [],
236
+ pluginInfo: res.pluginInfo || res?.plugin
237
+ };
238
+ }
202
239
  /**
203
240
  *
204
241
  * @param query Query for your data
@@ -208,14 +245,18 @@ export class Player {
208
245
  // transform the query object
209
246
  const Query = {
210
247
  query: typeof query === "string" ? query : query.query,
211
- source: DefaultSources[(typeof query === "string" ? undefined : query.source) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform] ?? (typeof query === "string" ? undefined : query.source) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform
248
+ source: DefaultSources[(typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform.toLowerCase()] ?? (typeof query === "string" ? undefined : query.source?.trim?.()?.toLowerCase?.()) ?? this.LavalinkManager.options.playerOptions.defaultSearchPlatform
212
249
  };
213
250
  // if user does player.search("ytsearch:Hello")
214
- const foundSource = [...Object.keys(DefaultSources)].find(source => Query.query.startsWith(`${source}:`));
251
+ const foundSource = [...Object.keys(DefaultSources)].find(source => Query.query?.toLowerCase?.()?.startsWith(`${source}:`.toLowerCase()))?.trim?.()?.toLowerCase?.();
215
252
  if (foundSource && DefaultSources[foundSource]) {
216
253
  Query.source = DefaultSources[foundSource]; // set the source to ytsearch:
217
- Query.query = Query.query.replace(`${foundSource}:`, ""); // remove ytsearch: from the query
254
+ Query.query = Query.query.slice(`${foundSource}:`.length, Query.query.length); // remove ytsearch: from the query
218
255
  }
256
+ if (/^https?:\/\//.test(Query.query))
257
+ this.LavalinkManager.utils.validatedQueryString(this.node, Query.source);
258
+ else if (Query.source)
259
+ this.LavalinkManager.utils.validateSourceString(this.node, Query.source);
219
260
  // ftts query parameters: ?voice=Olivia&audio_format=ogg_opus&translate=False&silence=1000&speed=1.0 | example raw get query: https://api.flowery.pw/v1/tts?voice=Olivia&audio_format=ogg_opus&translate=False&silence=0&speed=1.0&text=Hello%20World
220
261
  // request the data
221
262
  const res = await this.node.request(`/loadtracks?identifier=${!/^https?:\/\//.test(Query.query) ? `${Query.source}:${Query.source === "ftts" ? "//" : ""}` : ""}${encodeURIComponent(Query.query)}`);
@@ -17,7 +17,21 @@ export interface TrackInfo {
17
17
  }
18
18
  export interface PluginInfo {
19
19
  /** The Type provided by a plugin */
20
- type?: string;
20
+ type?: "album" | "playlist" | "artist" | "recommendations" | string;
21
+ /** The Identifier provided by a plugin */
22
+ albumName?: string;
23
+ /** The url of the album art */
24
+ albumArtUrl?: string;
25
+ /** The url of the artist */
26
+ artistUrl?: string;
27
+ /** The url of the artist artwork */
28
+ artistArtworkUrl?: string;
29
+ /** The url of the preview */
30
+ previewUrl?: string;
31
+ /** Whether the track is a preview */
32
+ isPreview?: boolean;
33
+ /** The total number of tracks in the playlist */
34
+ totalTracks?: number;
21
35
  /** The Identifier provided by a plugin */
22
36
  identifier?: string;
23
37
  /** The ArtworkUrl provided by a plugin */
@@ -7,13 +7,18 @@ export declare const TrackSymbol: unique symbol;
7
7
  export declare const UnresolvedTrackSymbol: unique symbol;
8
8
  export declare const QueueSymbol: unique symbol;
9
9
  export declare const NodeSymbol: unique symbol;
10
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch" | "speak" | "tts" | "ftts";
10
+ export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
11
+ export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
12
+ export type DuncteSearchPlatform = "speak" | "tts";
13
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform;
11
14
  export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
12
15
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
13
16
  export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
14
17
  export interface PlaylistInfo {
15
18
  /** The playlist title. */
16
19
  title: string;
20
+ /** The playlist name (if provided instead of title) */
21
+ name: string;
17
22
  /** The Playlist Author */
18
23
  author?: string;
19
24
  /** The Playlist Thumbnail */
@@ -72,7 +77,8 @@ export declare class ManagerUitls {
72
77
  * @param requester
73
78
  */
74
79
  buildUnresolvedTrack(query: UnresolvedQuery | UnresolvedTrack, requester: unknown): UnresolvedTrack;
75
- validatedQuery(queryString: string, node: LavalinkNode): void;
80
+ validatedQueryString(node: LavalinkNode, queryString: string): void;
81
+ validateSourceString(node: LavalinkNode, sourceString: SearchPlatform): void;
76
82
  }
77
83
  /**
78
84
  * @internal
@@ -282,4 +288,27 @@ export interface NodeMessage extends NodeStats {
282
288
  guildId: string;
283
289
  }
284
290
  export declare function queueTrackEnd(player: Player): Promise<Track>;
291
+ export type LavaSearchType = "track" | "album" | "artist" | "playlist" | "text" | "tracks" | "albums" | "artists" | "playlists" | "texts";
292
+ export interface LavaSearchFilteredResponse {
293
+ info: PlaylistInfo;
294
+ pluginInfo: PluginInfo;
295
+ tracks: Track[];
296
+ }
297
+ export interface LavaSearchResponse {
298
+ /** An array of tracks, only present if track is in types */
299
+ tracks: Track[];
300
+ /** An array of albums, only present if album is in types */
301
+ albums: LavaSearchFilteredResponse[];
302
+ /** An array of artists, only present if artist is in types */
303
+ artists: LavaSearchFilteredResponse[];
304
+ /** An array of playlists, only present if playlist is in types */
305
+ playlists: LavaSearchFilteredResponse[];
306
+ /** An array of text results, only present if text is in types */
307
+ texts: {
308
+ text: string;
309
+ pluginInfo: PluginInfo;
310
+ }[];
311
+ /** Addition result data provided by plugins */
312
+ pluginInfo: PluginInfo;
313
+ }
285
314
  export {};
@@ -145,7 +145,7 @@ export class ManagerUitls {
145
145
  Object.defineProperty(unresolvedTrack, UnresolvedTrackSymbol, { configurable: true, value: true });
146
146
  return unresolvedTrack;
147
147
  }
148
- validatedQuery(queryString, node) {
148
+ validatedQueryString(node, queryString) {
149
149
  if (!node.info)
150
150
  throw new Error("No Lavalink Node was provided");
151
151
  if (!node.info.sourceManagers?.length)
@@ -187,12 +187,14 @@ export class ManagerUitls {
187
187
  if (SourceLinksRegexes.musicYandex.test(queryString) && !node.info.sourceManagers.includes("yandexmusic")) {
188
188
  throw new Error("Lavalink Node has not 'yandexmusic' enabled");
189
189
  }
190
- const hasSource = queryString.split(":")[0];
191
- if (queryString.split(" ").length <= 1 || !queryString.split(" ")[0].includes(":"))
192
- return;
193
- const source = DefaultSources[hasSource];
190
+ return;
191
+ }
192
+ validateSourceString(node, sourceString) {
193
+ if (!sourceString)
194
+ throw new Error(`No SourceString was provided`);
195
+ const source = DefaultSources[sourceString.toLowerCase()] || Object.values(DefaultSources).find(v => v.toLowerCase() === sourceString?.toLowerCase());
194
196
  if (!source)
195
- throw new Error(`Lavalink Node SearchQuerySource: '${hasSource}' is not available`);
197
+ throw new Error(`Lavalink Node SearchQuerySource: '${sourceString}' is not available`);
196
198
  if (source === "amsearch" && !node.info.sourceManagers.includes("applemusic")) {
197
199
  throw new Error("Lavalink Node has not 'applemusic' enabled, which is required to have 'amsearch' work");
198
200
  }
@@ -2,8 +2,8 @@ import { EQBand, FilterData, FilterManager, LavalinkFilterData } from "./Filters
2
2
  import { LavalinkManager } from "./LavalinkManager";
3
3
  import { LavalinkNode } from "./Node";
4
4
  import { Queue } from "./Queue";
5
- import { Track } from "./Track";
6
- import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult } from "./Utils";
5
+ import { Track, UnresolvedTrack } from "./Track";
6
+ import { LavalinkPlayerVoiceOptions, SearchPlatform, SearchResult, LavaSearchType, LavaSearchResponse, LavaSrcSearchPlatformBase } from "./Utils";
7
7
  type PlayerDestroyReasons = "QueueEmpty" | "NodeDestroy" | "NodeDeleted" | "LavalinkNoVoice" | "NodeReconnectFail" | "PlayerReconnectFail" | "Disconnected" | "ChannelDeleted";
8
8
  export type DestroyReasonsType = PlayerDestroyReasons | string;
9
9
  export declare const DestroyReasons: Record<PlayerDestroyReasons, PlayerDestroyReasons>;
@@ -43,7 +43,7 @@ export interface PlayerOptions {
43
43
  }
44
44
  export interface PlayOptions {
45
45
  /** Which Track to play | don't provide, if it should pick from the Queue */
46
- track?: Track;
46
+ track?: Track | UnresolvedTrack;
47
47
  /** Encoded Track to use, instead of the queue system... */
48
48
  encodedTrack?: string | null;
49
49
  /** Encoded Track to use&search, instead of the queue system (yt only)... */
@@ -138,6 +138,11 @@ export declare class Player {
138
138
  * @param ignoreVolumeDecrementer If it should ignore the volumedecrementer option
139
139
  */
140
140
  setVolume(volume: number, ignoreVolumeDecrementer?: boolean): Promise<void>;
141
+ lavaSearch(query: {
142
+ query: string;
143
+ source: LavaSrcSearchPlatformBase;
144
+ types?: LavaSearchType[];
145
+ }, requestUser: unknown): Promise<SearchResult | LavaSearchResponse>;
141
146
  /**
142
147
  *
143
148
  * @param query Query for your data
@@ -17,7 +17,21 @@ export interface TrackInfo {
17
17
  }
18
18
  export interface PluginInfo {
19
19
  /** The Type provided by a plugin */
20
- type?: string;
20
+ type?: "album" | "playlist" | "artist" | "recommendations" | string;
21
+ /** The Identifier provided by a plugin */
22
+ albumName?: string;
23
+ /** The url of the album art */
24
+ albumArtUrl?: string;
25
+ /** The url of the artist */
26
+ artistUrl?: string;
27
+ /** The url of the artist artwork */
28
+ artistArtworkUrl?: string;
29
+ /** The url of the preview */
30
+ previewUrl?: string;
31
+ /** Whether the track is a preview */
32
+ isPreview?: boolean;
33
+ /** The total number of tracks in the playlist */
34
+ totalTracks?: number;
21
35
  /** The Identifier provided by a plugin */
22
36
  identifier?: string;
23
37
  /** The ArtworkUrl provided by a plugin */
@@ -7,13 +7,18 @@ export declare const TrackSymbol: unique symbol;
7
7
  export declare const UnresolvedTrackSymbol: unique symbol;
8
8
  export declare const QueueSymbol: unique symbol;
9
9
  export declare const NodeSymbol: unique symbol;
10
- export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch" | "speak" | "tts" | "ftts";
10
+ export type LavaSrcSearchPlatformBase = "spsearch" | "sprec" | "amsearch" | "dzsearch" | "dzisrc" | "ymsearch";
11
+ export type LavaSrcSearchPlatform = LavaSrcSearchPlatformBase | "ftts";
12
+ export type DuncteSearchPlatform = "speak" | "tts";
13
+ export type LavalinkSearchPlatform = "ytsearch" | "ytmsearch" | "scsearch" | LavaSrcSearchPlatform | DuncteSearchPlatform;
11
14
  export type ClientSearchPlatform = "youtube" | "yt" | "youtube music" | "youtubemusic" | "ytm" | "soundcloud" | "sc" | "am" | "apple music" | "applemusic" | "apple" | "sp" | "spsuggestion" | "spotify" | "dz" | "deezer" | "yandex" | "yandex music" | "yandexmusic";
12
15
  export type SearchPlatform = LavalinkSearchPlatform | ClientSearchPlatform;
13
16
  export type SourcesRegex = "YoutubeRegex" | "YoutubeMusicRegex" | "SoundCloudRegex" | "SoundCloudMobileRegex" | "DeezerTrackRegex" | "DeezerArtistRegex" | "DeezerEpisodeRegex" | "DeezerMixesRegex" | "DeezerPageLinkRegex" | "DeezerPlaylistRegex" | "DeezerAlbumRegex" | "AllDeezerRegex" | "AllDeezerRegexWithoutPageLink" | "SpotifySongRegex" | "SpotifyPlaylistRegex" | "SpotifyArtistRegex" | "SpotifyEpisodeRegex" | "SpotifyShowRegex" | "SpotifyAlbumRegex" | "AllSpotifyRegex" | "mp3Url" | "m3uUrl" | "m3u8Url" | "mp4Url" | "m4aUrl" | "wavUrl" | "aacpUrl" | "tiktok" | "mixcloud" | "musicYandex" | "radiohost" | "bandcamp" | "appleMusic" | "TwitchTv" | "vimeo";
14
17
  export interface PlaylistInfo {
15
18
  /** The playlist title. */
16
19
  title: string;
20
+ /** The playlist name (if provided instead of title) */
21
+ name: string;
17
22
  /** The Playlist Author */
18
23
  author?: string;
19
24
  /** The Playlist Thumbnail */
@@ -72,7 +77,8 @@ export declare class ManagerUitls {
72
77
  * @param requester
73
78
  */
74
79
  buildUnresolvedTrack(query: UnresolvedQuery | UnresolvedTrack, requester: unknown): UnresolvedTrack;
75
- validatedQuery(queryString: string, node: LavalinkNode): void;
80
+ validatedQueryString(node: LavalinkNode, queryString: string): void;
81
+ validateSourceString(node: LavalinkNode, sourceString: SearchPlatform): void;
76
82
  }
77
83
  /**
78
84
  * @internal
@@ -282,4 +288,27 @@ export interface NodeMessage extends NodeStats {
282
288
  guildId: string;
283
289
  }
284
290
  export declare function queueTrackEnd(player: Player): Promise<Track>;
291
+ export type LavaSearchType = "track" | "album" | "artist" | "playlist" | "text" | "tracks" | "albums" | "artists" | "playlists" | "texts";
292
+ export interface LavaSearchFilteredResponse {
293
+ info: PlaylistInfo;
294
+ pluginInfo: PluginInfo;
295
+ tracks: Track[];
296
+ }
297
+ export interface LavaSearchResponse {
298
+ /** An array of tracks, only present if track is in types */
299
+ tracks: Track[];
300
+ /** An array of albums, only present if album is in types */
301
+ albums: LavaSearchFilteredResponse[];
302
+ /** An array of artists, only present if artist is in types */
303
+ artists: LavaSearchFilteredResponse[];
304
+ /** An array of playlists, only present if playlist is in types */
305
+ playlists: LavaSearchFilteredResponse[];
306
+ /** An array of text results, only present if text is in types */
307
+ texts: {
308
+ text: string;
309
+ pluginInfo: PluginInfo;
310
+ }[];
311
+ /** Addition result data provided by plugins */
312
+ pluginInfo: PluginInfo;
313
+ }
285
314
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavalink-client",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Easy and advanced lavalink client. Use it with lavalink plugins as well as latest lavalink versions",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",