@khang07/zing-mp3-api 1.0.1 → 1.3.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.
@@ -5,11 +5,22 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var axios = require('axios');
6
6
  var m3u8stream = require('m3u8stream');
7
7
  var node_stream = require('node:stream');
8
+ var node_url = require('node:url');
8
9
  var cookies = require('./utils/cookies.cjs');
9
10
  var encrypt = require('./utils/encrypt.cjs');
10
11
  var lapse = require('./utils/lapse.cjs');
12
+ var refined = require('./utils/refined.cjs');
11
13
 
12
- class ZingClient {
14
+ const isURL = (value) => {
15
+ try {
16
+ new node_url.URL(value);
17
+ return true;
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ };
23
+ class Client {
13
24
  static BASE_URL = 'https://zingmp3.vn/';
14
25
  static VERSION_URL_V1 = '1.6.34';
15
26
  static VERSION_URL_V2 = '1.13.13';
@@ -17,37 +28,47 @@ class ZingClient {
17
28
  static SECRET_KEY_V2 = '10a01dcf33762d3a204cb96429918ff6';
18
29
  static API_KEY_V1 = '88265e23d4284f25963e6eedac8fbfa3';
19
30
  static API_KEY_V2 = '38e8643fb0dc04e8d65b99994d3dafff';
20
- jar;
21
- instance;
22
- get ctime() {
23
- return Math.floor(Date.now() / 1000).toString();
31
+ static API_VIDEO_PATH = '/api/v2/page/get/video';
32
+ static API_MUSIC_PATH = '/api/v2/song/get/streaming';
33
+ static EXTRA_API_MUSIC_PATH = '/api/song/get-song-info';
34
+ static API_SEARCH_PATH = '/api/v2/search';
35
+ static API_PLAYLIST_PATH = '/api/v2/page/get/playlist';
36
+ static API_MEDIA_DETAILS_PATH = '/api/v2/song/get/info';
37
+ static API_ARTIST_PATH = '/api/v2/page/get/artist';
38
+ static getIDFromURL(url) {
39
+ if (typeof url !== 'string' || !url.trim().length)
40
+ throw new lapse.Lapse('URL must be a non-empty string', 'ERROR_INVALID_URL');
41
+ const match = url.match(/\/([A-Z0-9]{8})\.html(?:\?|#|$)/i) || url.match(/^https?:\/\/zingmp3\.vn\/([^/?#]+)\/?(?:[?#].*)?$/i);
42
+ if (!match)
43
+ throw new lapse.Lapse('Could not extract ID from URL', 'ERROR_INVALID_URL');
44
+ return match[1];
24
45
  }
25
- maxRate;
46
+ ctime = Math.floor(Date.now() / 1000).toString();
47
+ instance;
48
+ maxLoad;
49
+ maxHighWaterMark;
50
+ userAgent;
51
+ jar;
26
52
  constructor(options = {}) {
27
- this.maxRate = [
28
- options?.maxRate?.[0] ?? 100 * 1024,
29
- options?.maxRate?.[1] ?? 16 * 1024
30
- ];
31
- this.jar = new cookies.Cookies();
32
- const axiosOptions = {
33
- baseURL: ZingClient.BASE_URL,
53
+ this.maxLoad = options.maxLoad ?? 1024 * 1024;
54
+ this.maxHighWaterMark = options.maxHighWaterMark ?? 16 * 1024;
55
+ this.userAgent = options?.userAgent ?? 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3';
56
+ this.jar = options.jar instanceof cookies.Cookies ? options.jar : new cookies.Cookies();
57
+ this.instance = axios.create({
58
+ baseURL: Client.BASE_URL,
34
59
  params: {
35
- version: ZingClient.VERSION_URL_V1,
36
- apiKey: ZingClient.API_KEY_V1,
60
+ version: Client.VERSION_URL_V1,
61
+ apiKey: Client.API_KEY_V1,
37
62
  ctime: this.ctime
38
63
  },
39
- maxRate: [
40
- 100 * 1024,
41
- this.maxRate[0]
42
- ],
64
+ maxRate: this.maxLoad,
43
65
  headers: {
44
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
66
+ 'User-Agent': this.userAgent
45
67
  }
46
- };
47
- this.instance = axios.create(axiosOptions);
48
- this.instance.interceptors.request.use((options) => {
68
+ });
69
+ this.instance.interceptors.request.use(async (options) => {
49
70
  const base = options.baseURL ?? '';
50
- const url = new URL(options.url ?? '/', base).toString();
71
+ const url = new node_url.URL(options.url ?? '/', base).toString();
51
72
  const additionalHeaders = this.jar.applyToHeaders(url);
52
73
  for (const [key, value] of Object.entries(additionalHeaders))
53
74
  options.headers.set(key, value);
@@ -58,38 +79,37 @@ class ZingClient {
58
79
  const requestUrl = response.request?.res?.responseUrl ?? response.config.url;
59
80
  if (requestUrl && Array.isArray(setCookie))
60
81
  this.jar.setCookies(setCookie, requestUrl);
61
- return response;
82
+ return response.data;
62
83
  });
63
84
  }
85
+ async ensureCookies() {
86
+ if (this.jar.getCookies(Client.BASE_URL).length === 0)
87
+ void await this.instance.get('/');
88
+ }
64
89
  async video(videoID) {
65
- if (typeof videoID !== 'string' || !videoID.length)
90
+ const value = videoID instanceof node_url.URL ? videoID.toString() : videoID;
91
+ if (typeof value !== 'string' || !value.trim().length)
66
92
  throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
67
- const uri = '/api/v2/page/get/video';
68
93
  try {
69
- if (this.jar.getCookies(ZingClient.BASE_URL).length === 0)
70
- await this.instance.get('/');
71
- const response = await this.instance.get(uri, {
94
+ videoID = isURL(value) ? Client.getIDFromURL(value) : value;
95
+ void await this.ensureCookies();
96
+ const response = await this.instance.get(Client.API_VIDEO_PATH, {
72
97
  params: {
73
98
  id: videoID,
74
- sig: encrypt.createSignature(uri, 'ctime=' + this.ctime + 'id=' + videoID + 'version=' + ZingClient.VERSION_URL_V1, ZingClient.SECRET_KEY_V1)
75
- },
76
- maxRate: [
77
- 100 * 1024,
78
- this.maxRate[0]
79
- ]
99
+ sig: encrypt.createSignature(Client.API_VIDEO_PATH, 'ctime=' + this.ctime + 'id=' + videoID + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
100
+ }
80
101
  });
81
- const body = response.data;
82
- if (body.err !== 0)
83
- throw new lapse.Lapse(body.msg, 'ERROR_VIDEO_NOT_FOUND');
84
- const videoURL = body.data?.streaming?.hls?.['360p'];
102
+ if (response.err !== 0)
103
+ throw new lapse.Lapse('Video could not be found', 'ERROR_VIDEO_NOT_FOUND', response.err, response);
104
+ const videoURL = response.data?.streaming?.hls?.['720p'] || response.data?.streaming?.hls?.['360p'];
85
105
  if (!videoURL || !videoURL.length)
86
106
  throw new lapse.Lapse('Streaming URL not found', 'ERROR_STREAM_URL_NOT_FOUND');
87
- const streamVideo = m3u8stream(videoURL);
88
- streamVideo.once('error', (error) => {
107
+ const source = m3u8stream(videoURL);
108
+ source.once('error', (error) => {
89
109
  const lapse$1 = new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
90
- streamVideo.destroy(lapse$1);
110
+ source.destroy(lapse$1);
91
111
  });
92
- return streamVideo;
112
+ return source;
93
113
  }
94
114
  catch (error) {
95
115
  if (error instanceof lapse.Lapse)
@@ -97,86 +117,87 @@ class ZingClient {
97
117
  throw new lapse.Lapse('Failed to fetch video stream', 'ERROR_VIDEO_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
98
118
  }
99
119
  }
100
- videoSyncLike(videoID) {
101
- const video = new node_stream.PassThrough({ highWaterMark: this.maxRate[1] });
120
+ videoSync(videoID) {
121
+ const video = new node_stream.PassThrough({ highWaterMark: this.maxHighWaterMark });
122
+ let copySource;
123
+ let closed = false;
124
+ let sourceDestroyed = false;
125
+ const destroy = () => {
126
+ if (!copySource || sourceDestroyed)
127
+ return;
128
+ sourceDestroyed = true;
129
+ if (!copySource)
130
+ return;
131
+ copySource.unpipe(video);
132
+ if (!copySource.destroyed)
133
+ copySource.destroy();
134
+ };
135
+ const closer = () => {
136
+ closed = true;
137
+ destroy();
138
+ };
139
+ video.once('close', closer);
140
+ video.once('error', closer);
102
141
  void this.video(videoID)
103
142
  .then((source) => {
143
+ copySource = source;
104
144
  source.once('error', (error) => {
105
145
  const lapse$1 = error instanceof lapse.Lapse ? error : new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
106
146
  video.destroy(lapse$1);
107
147
  });
108
- const destroy = () => {
109
- if (!source.destroyed)
110
- source.destroy();
111
- };
112
- video.once('close', destroy);
113
- video.once('error', destroy);
148
+ if (closed || video.destroyed) {
149
+ destroy();
150
+ return;
151
+ }
114
152
  source.pipe(video);
115
153
  })
116
154
  .catch((error) => {
117
- if (error instanceof lapse.Lapse)
118
- throw error;
119
- throw new lapse.Lapse('Failed to fetch video stream', 'ERROR_VIDEO_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
155
+ const lapse$1 = error instanceof lapse.Lapse ? error : new lapse.Lapse('Failed to fetch video stream', 'ERROR_VIDEO_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
156
+ video.destroy(lapse$1);
120
157
  });
121
158
  return video;
122
159
  }
123
160
  async music(musicID) {
124
- if (typeof musicID !== 'string' || !musicID.trim().length)
161
+ const value = musicID instanceof node_url.URL ? musicID.toString() : musicID;
162
+ if (typeof value !== 'string' || !value.trim().length)
125
163
  throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
126
- let musicURL;
127
- const uri = '/api/v2/song/get/streaming';
128
164
  try {
129
- if (this.jar.getCookies(ZingClient.BASE_URL).length === 0)
130
- await this.instance.get('/');
131
- const response = await this.instance.get(uri, {
165
+ musicID = isURL(value) ? Client.getIDFromURL(value) : value;
166
+ void await this.ensureCookies();
167
+ const response = await this.instance.get(Client.API_MUSIC_PATH, {
132
168
  params: {
133
169
  id: musicID,
134
- sig: encrypt.createSignature(uri, 'ctime=' + this.ctime + 'id=' + musicID + 'version=' + ZingClient.VERSION_URL_V1, ZingClient.SECRET_KEY_V1)
135
- },
136
- maxRate: [
137
- 100 * 1024,
138
- this.maxRate[0]
139
- ]
170
+ sig: encrypt.createSignature(Client.API_MUSIC_PATH, 'ctime=' + this.ctime + 'id=' + musicID + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
171
+ }
140
172
  });
141
- const body = response.data;
142
- if (body.err === -1150) {
143
- let retrySuccess = false;
144
- const uri_v2 = '/api/song/get-song-info';
145
- for (let step = 0; step < 2; step++) {
146
- const retry = await this.instance.get(uri_v2, {
173
+ let musicURL = response.data?.[320] && response.data[320] !== 'VIP' ? response.data[320] : response.data?.[128];
174
+ if (response.err === -1150) {
175
+ const retry = async (step, before) => {
176
+ if (step > 3)
177
+ throw new lapse.Lapse('Music requested by VIP, PRI', 'ERROR_MUSIC_VIP_ONLY', before ? before.err : void 0, before);
178
+ const retryData = await this.instance.get(Client.EXTRA_API_MUSIC_PATH, {
147
179
  params: {
148
180
  id: musicID,
149
- api_key: ZingClient.API_KEY_V2,
150
- sig: encrypt.createSignature('/song/get-song-info', 'ctime=' + this.ctime + 'id=' + musicID, ZingClient.SECRET_KEY_V2),
181
+ api_key: Client.API_KEY_V2,
182
+ sig: encrypt.createSignature('/song/get-song-info', 'ctime=' + this.ctime + 'id=' + musicID, Client.SECRET_KEY_V2),
151
183
  version: void 0,
152
184
  apiKey: void 0
153
- },
154
- maxRate: [
155
- 100 * 1024,
156
- this.maxRate[0]
157
- ]
185
+ }
158
186
  });
159
- if (retry.data.err === 0) {
160
- retrySuccess = true;
161
- musicURL = retry.data.data?.streaming?.default?.[128];
162
- break;
163
- }
164
- }
165
- if (!retrySuccess)
166
- throw new lapse.Lapse('Music requested by VIP, PRI', 'ERROR_MUSIC_VIP_ONLY');
187
+ if (retryData.err === 0)
188
+ return retryData.data?.streaming?.default?.[128];
189
+ return retry(step + 1, retryData);
190
+ };
191
+ musicURL = await retry(0);
167
192
  }
168
- else if (body.err === 0)
169
- musicURL = body.data?.[128];
170
- else
171
- throw new lapse.Lapse('This song could not be found', 'ERROR_MUSIC_NOT_FOUND');
172
193
  if (!musicURL || !musicURL.length)
173
194
  throw new lapse.Lapse('Streaming URL not found', 'ERROR_STREAM_URL_NOT_FOUND');
174
- const streamMusic = await this.instance.get(musicURL, { responseType: 'stream' });
175
- streamMusic.data.once('error', (error) => {
176
- const lapse$1 = new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', streamMusic.status, error);
177
- streamMusic.data.destroy(lapse$1);
195
+ const source = await this.instance.get(musicURL, { responseType: 'stream' });
196
+ source.once('error', (error) => {
197
+ const lapse$1 = new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
198
+ source.destroy(lapse$1);
178
199
  });
179
- return streamMusic.data;
200
+ return source;
180
201
  }
181
202
  catch (error) {
182
203
  if (error instanceof lapse.Lapse)
@@ -184,38 +205,216 @@ class ZingClient {
184
205
  throw new lapse.Lapse('Failed to fetch music stream', 'ERROR_MUSIC_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
185
206
  }
186
207
  }
187
- musicSyncLike(musicID) {
188
- const music = new node_stream.PassThrough({ highWaterMark: this.maxRate[1] });
208
+ musicSync(musicID) {
209
+ const music = new node_stream.PassThrough({ highWaterMark: this.maxHighWaterMark });
210
+ let copySource;
211
+ let closed = false;
212
+ let sourceDestroyed = false;
213
+ const destroy = () => {
214
+ if (!copySource || sourceDestroyed)
215
+ return;
216
+ sourceDestroyed = true;
217
+ copySource.unpipe(music);
218
+ if (!copySource.destroyed)
219
+ copySource.destroy();
220
+ };
221
+ const closer = () => {
222
+ closed = true;
223
+ destroy();
224
+ };
225
+ music.once('close', closer);
226
+ music.once('error', closer);
189
227
  void this.music(musicID)
190
228
  .then((source) => {
229
+ copySource = source;
191
230
  source.once('error', (error) => {
192
231
  const lapse$1 = error instanceof lapse.Lapse ? error : new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
193
232
  music.destroy(lapse$1);
194
233
  });
195
- const destroy = () => {
196
- if (!source.destroyed)
197
- source.destroy();
198
- };
199
- music.once('close', destroy);
200
- music.once('error', destroy);
234
+ if (closed || music.destroyed) {
235
+ destroy();
236
+ return;
237
+ }
201
238
  source.pipe(music);
202
239
  })
203
240
  .catch((error) => {
204
- if (error instanceof lapse.Lapse)
205
- throw error;
206
- throw new lapse.Lapse('Failed to fetch music stream', 'ERROR_MUSIC_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
241
+ const lapse$1 = error instanceof lapse.Lapse ? error : new lapse.Lapse('Failed to fetch music stream', 'ERROR_MUSIC_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
242
+ music.destroy(lapse$1);
207
243
  });
208
244
  return music;
209
245
  }
246
+ async playlist(playlistID) {
247
+ const value = playlistID instanceof node_url.URL ? playlistID.toString() : playlistID;
248
+ if (typeof value !== 'string' || !value.trim().length)
249
+ throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
250
+ try {
251
+ playlistID = isURL(value) ? Client.getIDFromURL(value) : value;
252
+ void await this.ensureCookies();
253
+ const response = await this.instance.get(Client.API_PLAYLIST_PATH, {
254
+ params: {
255
+ id: playlistID,
256
+ sig: encrypt.createSignature(Client.API_PLAYLIST_PATH, 'ctime=' + this.ctime + 'id=' + playlistID + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
257
+ }
258
+ });
259
+ if (response.err !== 0)
260
+ throw new lapse.Lapse('Could not find playlist', 'ERROR_PLAYLIST_NOT_FOUND', response.err, response);
261
+ return refined.createPlayList(response.data);
262
+ }
263
+ catch (error) {
264
+ if (error instanceof lapse.Lapse)
265
+ throw error;
266
+ throw new lapse.Lapse('Failed to fetch playlist', 'ERROR_PLAYLIST_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
267
+ }
268
+ }
269
+ async artist(aliasID) {
270
+ const value = aliasID instanceof node_url.URL ? aliasID.toString() : aliasID;
271
+ if (typeof value !== 'string' || !value.trim().length)
272
+ throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
273
+ try {
274
+ aliasID = isURL(value) ? Client.getIDFromURL(value) : value;
275
+ void await this.ensureCookies();
276
+ const response = await this.instance.get(Client.API_ARTIST_PATH, {
277
+ params: {
278
+ alias: aliasID,
279
+ sig: encrypt.createSignature(Client.API_ARTIST_PATH, 'ctime=' + this.ctime + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
280
+ }
281
+ });
282
+ if (response.err === -108)
283
+ throw new lapse.Lapse('Artist not found', 'ERROR_ARTIST_NOT_FOUND', response.err, response);
284
+ if (response.err !== 0)
285
+ throw new lapse.Lapse('Could not fetch artist', 'ERROR_ARTIST_FETCH', response.err, response);
286
+ return refined.createArtist(response.data);
287
+ }
288
+ catch (error) {
289
+ if (error instanceof lapse.Lapse)
290
+ throw error;
291
+ throw new lapse.Lapse('Failed to fetch artist', 'ERROR_ARTIST_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
292
+ }
293
+ }
294
+ async mediaDetails(mediaID) {
295
+ const value = mediaID instanceof node_url.URL ? mediaID.toString() : mediaID;
296
+ if (typeof value !== 'string' || !value.trim().length)
297
+ throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
298
+ try {
299
+ mediaID = isURL(value) ? Client.getIDFromURL(value) : value;
300
+ void await this.ensureCookies();
301
+ const response = await this.instance.get(Client.API_MEDIA_DETAILS_PATH, {
302
+ params: {
303
+ id: mediaID,
304
+ sig: encrypt.createSignature(Client.API_MEDIA_DETAILS_PATH, 'ctime=' + this.ctime + 'id=' + mediaID + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
305
+ }
306
+ });
307
+ if (response.err === -1023)
308
+ throw new lapse.Lapse('Media not found', 'ERROR_MEDIA_NOT_FOUND', response.err, response);
309
+ if (response.err !== 0)
310
+ throw new lapse.Lapse('Could not fetch media', 'ERROR_MEDIA_FETCH', response.err, response);
311
+ return refined.createMedia(response.data);
312
+ }
313
+ catch (error) {
314
+ if (error instanceof lapse.Lapse)
315
+ throw error;
316
+ throw new lapse.Lapse('Failed to fetch media', 'ERROR_MEDIA_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
317
+ }
318
+ }
319
+ async searchMusic(query) {
320
+ if (typeof query !== 'string' || !query.trim().length)
321
+ throw new lapse.Lapse('Query must be a non-empty string', 'ERROR_INVALID_QUERY');
322
+ try {
323
+ void await this.ensureCookies();
324
+ const response = await this.instance.get(Client.API_SEARCH_PATH, {
325
+ params: {
326
+ q: query,
327
+ type: 'song',
328
+ page: 1,
329
+ count: 20,
330
+ sig: encrypt.createSignature(Client.API_SEARCH_PATH, 'count=20ctime=' + this.ctime + 'page=1type=songversion=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
331
+ }
332
+ });
333
+ if (response.err !== 0)
334
+ throw new lapse.Lapse('Search could not be fetched', 'ERROR_SEARCH_FAILED', response.err, response);
335
+ return response.data.items.map(refined.createMedia);
336
+ }
337
+ catch (error) {
338
+ if (error instanceof lapse.Lapse)
339
+ throw error;
340
+ throw new lapse.Lapse('Search could not be fetch', 'ERROR_SEARCH_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
341
+ }
342
+ }
343
+ async searchVideo(query) {
344
+ if (typeof query !== 'string' || !query.trim().length)
345
+ throw new lapse.Lapse('Query must be a non-empty string', 'ERROR_INVALID_QUERY');
346
+ try {
347
+ void await this.ensureCookies();
348
+ const response = await this.instance.get(Client.API_SEARCH_PATH, {
349
+ params: {
350
+ q: query,
351
+ type: 'video',
352
+ page: 1,
353
+ count: 20,
354
+ sig: encrypt.createSignature(Client.API_SEARCH_PATH, 'count=20ctime=' + this.ctime + 'page=1type=videoversion=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
355
+ }
356
+ });
357
+ if (response.err !== 0)
358
+ throw new lapse.Lapse('Search could not be fetched', 'ERROR_SEARCH_FAILED', response.err, response);
359
+ return response.data.items.filter(item => item.artists).map(refined.createSearchMedia);
360
+ }
361
+ catch (error) {
362
+ if (error instanceof lapse.Lapse)
363
+ throw error;
364
+ throw new lapse.Lapse('Search could not be fetch', 'ERROR_SEARCH_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
365
+ }
366
+ }
367
+ async searchList(query) {
368
+ if (typeof query !== 'string' || !query.trim().length)
369
+ throw new lapse.Lapse('Query must be a non-empty string', 'ERROR_INVALID_QUERY');
370
+ try {
371
+ void await this.ensureCookies();
372
+ const response = await this.instance.get(Client.API_SEARCH_PATH, {
373
+ params: {
374
+ q: query,
375
+ type: 'playlist',
376
+ page: 1,
377
+ count: 20,
378
+ sig: encrypt.createSignature(Client.API_SEARCH_PATH, 'count=20ctime=' + this.ctime + 'page=1type=playlistversion=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
379
+ }
380
+ });
381
+ if (response.err !== 0)
382
+ throw new lapse.Lapse('Search could not be fetched', 'ERROR_SEARCH_FAILED', response.err, response);
383
+ return response.data.items.map(refined.createSearchPlayList);
384
+ }
385
+ catch (error) {
386
+ if (error instanceof lapse.Lapse)
387
+ throw error;
388
+ throw new lapse.Lapse('Search could not be fetch', 'ERROR_SEARCH_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
389
+ }
390
+ }
391
+ async searchArtist(query) {
392
+ if (typeof query !== 'string' || !query.trim().length)
393
+ throw new lapse.Lapse('Query must be a non-empty string', 'ERROR_INVALID_QUERY');
394
+ try {
395
+ void await this.ensureCookies();
396
+ const response = await this.instance.get(Client.API_SEARCH_PATH, {
397
+ params: {
398
+ q: query,
399
+ type: 'artist',
400
+ page: 1,
401
+ count: 20,
402
+ sig: encrypt.createSignature(Client.API_SEARCH_PATH, 'count=20ctime=' + this.ctime + 'page=1type=artistversion=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
403
+ }
404
+ });
405
+ if (response.err !== 0)
406
+ throw new lapse.Lapse('Search could not be fetched', 'ERROR_SEARCH_FAILED', response.err, response);
407
+ return response.data.items.map(refined.createSearchArtist);
408
+ }
409
+ catch (error) {
410
+ if (error instanceof lapse.Lapse)
411
+ throw error;
412
+ throw new lapse.Lapse('Search could not be fetch', 'ERROR_SEARCH_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
413
+ }
414
+ }
210
415
  }
211
- const clientOptions = {
212
- maxRate: [
213
- 100 * 1024,
214
- 16 * 1024
215
- ]
216
- };
217
- const client = new ZingClient(clientOptions);
416
+ const client = new Client();
218
417
 
219
- exports.ZingClient = ZingClient;
418
+ exports.Client = Client;
220
419
  exports.default = client;
221
420
  //# sourceMappingURL=index.cjs.map