@khang07/zing-mp3-api 1.1.0 → 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.
- package/README.md +442 -476
- package/dist/cjs/index.cjs +301 -134
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/utils/refined.cjs +140 -0
- package/dist/cjs/utils/refined.cjs.map +1 -0
- package/dist/esm/index.js +301 -134
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/refined.js +132 -0
- package/dist/esm/utils/refined.js.map +1 -0
- package/dist/types/index.d.ts +34 -15
- package/dist/types/types/index.d.ts +7 -5
- package/dist/types/types/raw.d.ts +89 -0
- package/dist/types/types/response.d.ts +70 -0
- package/dist/types/utils/refined.d.ts +9 -0
- package/package.json +1 -2
- package/dist/types/types/fetch/response.d.ts +0 -22
- package/dist/types/types/fetch/uri.d.ts +0 -46
package/dist/cjs/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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,35 +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';
|
|
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];
|
|
45
|
+
}
|
|
20
46
|
ctime = Math.floor(Date.now() / 1000).toString();
|
|
21
|
-
jar;
|
|
22
47
|
instance;
|
|
23
|
-
|
|
48
|
+
maxLoad;
|
|
49
|
+
maxHighWaterMark;
|
|
50
|
+
userAgent;
|
|
51
|
+
jar;
|
|
24
52
|
constructor(options = {}) {
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
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,
|
|
32
59
|
params: {
|
|
33
|
-
version:
|
|
34
|
-
apiKey:
|
|
60
|
+
version: Client.VERSION_URL_V1,
|
|
61
|
+
apiKey: Client.API_KEY_V1,
|
|
35
62
|
ctime: this.ctime
|
|
36
63
|
},
|
|
37
|
-
maxRate:
|
|
38
|
-
100 * 1024,
|
|
39
|
-
this.maxRate[0]
|
|
40
|
-
],
|
|
64
|
+
maxRate: this.maxLoad,
|
|
41
65
|
headers: {
|
|
42
|
-
'User-Agent':
|
|
66
|
+
'User-Agent': this.userAgent
|
|
43
67
|
}
|
|
44
|
-
};
|
|
45
|
-
this.instance
|
|
46
|
-
this.instance.interceptors.request.use((options) => {
|
|
68
|
+
});
|
|
69
|
+
this.instance.interceptors.request.use(async (options) => {
|
|
47
70
|
const base = options.baseURL ?? '';
|
|
48
|
-
const url = new URL(options.url ?? '/', base).toString();
|
|
71
|
+
const url = new node_url.URL(options.url ?? '/', base).toString();
|
|
49
72
|
const additionalHeaders = this.jar.applyToHeaders(url);
|
|
50
73
|
for (const [key, value] of Object.entries(additionalHeaders))
|
|
51
74
|
options.headers.set(key, value);
|
|
@@ -56,34 +79,37 @@ class ZingClient {
|
|
|
56
79
|
const requestUrl = response.request?.res?.responseUrl ?? response.config.url;
|
|
57
80
|
if (requestUrl && Array.isArray(setCookie))
|
|
58
81
|
this.jar.setCookies(setCookie, requestUrl);
|
|
59
|
-
return response;
|
|
82
|
+
return response.data;
|
|
60
83
|
});
|
|
61
84
|
}
|
|
85
|
+
async ensureCookies() {
|
|
86
|
+
if (this.jar.getCookies(Client.BASE_URL).length === 0)
|
|
87
|
+
void await this.instance.get('/');
|
|
88
|
+
}
|
|
62
89
|
async video(videoID) {
|
|
63
|
-
|
|
90
|
+
const value = videoID instanceof node_url.URL ? videoID.toString() : videoID;
|
|
91
|
+
if (typeof value !== 'string' || !value.trim().length)
|
|
64
92
|
throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
|
|
65
|
-
const uri = '/api/v2/page/get/video';
|
|
66
93
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const response = await this.instance.get(
|
|
94
|
+
videoID = isURL(value) ? Client.getIDFromURL(value) : value;
|
|
95
|
+
void await this.ensureCookies();
|
|
96
|
+
const response = await this.instance.get(Client.API_VIDEO_PATH, {
|
|
70
97
|
params: {
|
|
71
98
|
id: videoID,
|
|
72
|
-
sig: encrypt.createSignature(
|
|
99
|
+
sig: encrypt.createSignature(Client.API_VIDEO_PATH, 'ctime=' + this.ctime + 'id=' + videoID + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
|
|
73
100
|
}
|
|
74
101
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
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'];
|
|
79
105
|
if (!videoURL || !videoURL.length)
|
|
80
106
|
throw new lapse.Lapse('Streaming URL not found', 'ERROR_STREAM_URL_NOT_FOUND');
|
|
81
|
-
const
|
|
82
|
-
|
|
107
|
+
const source = m3u8stream(videoURL);
|
|
108
|
+
source.once('error', (error) => {
|
|
83
109
|
const lapse$1 = new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
|
|
84
|
-
|
|
110
|
+
source.destroy(lapse$1);
|
|
85
111
|
});
|
|
86
|
-
return
|
|
112
|
+
return source;
|
|
87
113
|
}
|
|
88
114
|
catch (error) {
|
|
89
115
|
if (error instanceof lapse.Lapse)
|
|
@@ -91,78 +117,87 @@ class ZingClient {
|
|
|
91
117
|
throw new lapse.Lapse('Failed to fetch video stream', 'ERROR_VIDEO_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
|
|
92
118
|
}
|
|
93
119
|
}
|
|
94
|
-
|
|
95
|
-
const video = new node_stream.PassThrough({ highWaterMark: this.
|
|
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);
|
|
96
141
|
void this.video(videoID)
|
|
97
142
|
.then((source) => {
|
|
143
|
+
copySource = source;
|
|
98
144
|
source.once('error', (error) => {
|
|
99
145
|
const lapse$1 = error instanceof lapse.Lapse ? error : new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
|
|
100
146
|
video.destroy(lapse$1);
|
|
101
147
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
video.once('close', destroy);
|
|
107
|
-
video.once('error', destroy);
|
|
148
|
+
if (closed || video.destroyed) {
|
|
149
|
+
destroy();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
108
152
|
source.pipe(video);
|
|
109
153
|
})
|
|
110
154
|
.catch((error) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
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);
|
|
114
157
|
});
|
|
115
158
|
return video;
|
|
116
159
|
}
|
|
117
160
|
async music(musicID) {
|
|
118
|
-
|
|
161
|
+
const value = musicID instanceof node_url.URL ? musicID.toString() : musicID;
|
|
162
|
+
if (typeof value !== 'string' || !value.trim().length)
|
|
119
163
|
throw new lapse.Lapse('ID must be a non-empty string', 'ERROR_INVALID_ID');
|
|
120
|
-
let musicURL;
|
|
121
|
-
const uri = '/api/v2/song/get/streaming';
|
|
122
164
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const response = await this.instance.get(
|
|
165
|
+
musicID = isURL(value) ? Client.getIDFromURL(value) : value;
|
|
166
|
+
void await this.ensureCookies();
|
|
167
|
+
const response = await this.instance.get(Client.API_MUSIC_PATH, {
|
|
126
168
|
params: {
|
|
127
169
|
id: musicID,
|
|
128
|
-
sig: encrypt.createSignature(
|
|
170
|
+
sig: encrypt.createSignature(Client.API_MUSIC_PATH, 'ctime=' + this.ctime + 'id=' + musicID + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
|
|
129
171
|
}
|
|
130
172
|
});
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
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, {
|
|
137
179
|
params: {
|
|
138
180
|
id: musicID,
|
|
139
|
-
api_key:
|
|
140
|
-
sig: encrypt.createSignature('/song/get-song-info', 'ctime=' + this.ctime + 'id=' + musicID,
|
|
181
|
+
api_key: Client.API_KEY_V2,
|
|
182
|
+
sig: encrypt.createSignature('/song/get-song-info', 'ctime=' + this.ctime + 'id=' + musicID, Client.SECRET_KEY_V2),
|
|
141
183
|
version: void 0,
|
|
142
184
|
apiKey: void 0
|
|
143
185
|
}
|
|
144
186
|
});
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
if (!retrySuccess)
|
|
152
|
-
throw new lapse.Lapse('Music requested by VIP, PRI', 'ERROR_MUSIC_VIP_ONLY', response.status, body);
|
|
187
|
+
if (retryData.err === 0)
|
|
188
|
+
return retryData.data?.streaming?.default?.[128];
|
|
189
|
+
return retry(step + 1, retryData);
|
|
190
|
+
};
|
|
191
|
+
musicURL = await retry(0);
|
|
153
192
|
}
|
|
154
|
-
else if (body.err === 0)
|
|
155
|
-
musicURL = body.data?.[128];
|
|
156
|
-
else
|
|
157
|
-
throw new lapse.Lapse('This song could not be found', 'ERROR_MUSIC_NOT_FOUND', response.status, body);
|
|
158
193
|
if (!musicURL || !musicURL.length)
|
|
159
194
|
throw new lapse.Lapse('Streaming URL not found', 'ERROR_STREAM_URL_NOT_FOUND');
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const lapse$1 = new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD',
|
|
163
|
-
|
|
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);
|
|
164
199
|
});
|
|
165
|
-
return
|
|
200
|
+
return source;
|
|
166
201
|
}
|
|
167
202
|
catch (error) {
|
|
168
203
|
if (error instanceof lapse.Lapse)
|
|
@@ -170,84 +205,216 @@ class ZingClient {
|
|
|
170
205
|
throw new lapse.Lapse('Failed to fetch music stream', 'ERROR_MUSIC_FETCH', axios.isAxiosError(error) ? error.response?.status : void 0, error);
|
|
171
206
|
}
|
|
172
207
|
}
|
|
173
|
-
|
|
174
|
-
const music = new node_stream.PassThrough({ highWaterMark: this.
|
|
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);
|
|
175
227
|
void this.music(musicID)
|
|
176
228
|
.then((source) => {
|
|
229
|
+
copySource = source;
|
|
177
230
|
source.once('error', (error) => {
|
|
178
231
|
const lapse$1 = error instanceof lapse.Lapse ? error : new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD', void 0, error);
|
|
179
232
|
music.destroy(lapse$1);
|
|
180
233
|
});
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
music.once('close', destroy);
|
|
186
|
-
music.once('error', destroy);
|
|
234
|
+
if (closed || music.destroyed) {
|
|
235
|
+
destroy();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
187
238
|
source.pipe(music);
|
|
188
239
|
})
|
|
189
240
|
.catch((error) => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
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);
|
|
193
243
|
});
|
|
194
244
|
return music;
|
|
195
245
|
}
|
|
196
|
-
async
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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');
|
|
200
273
|
try {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const response = await this.instance.get(
|
|
274
|
+
aliasID = isURL(value) ? Client.getIDFromURL(value) : value;
|
|
275
|
+
void await this.ensureCookies();
|
|
276
|
+
const response = await this.instance.get(Client.API_ARTIST_PATH, {
|
|
204
277
|
params: {
|
|
205
|
-
|
|
206
|
-
sig: encrypt.createSignature(
|
|
278
|
+
alias: aliasID,
|
|
279
|
+
sig: encrypt.createSignature(Client.API_ARTIST_PATH, 'ctime=' + this.ctime + 'version=' + Client.VERSION_URL_V1, Client.SECRET_KEY_V1)
|
|
207
280
|
}
|
|
208
281
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return
|
|
214
|
-
id: song.encodeId,
|
|
215
|
-
name: song.title,
|
|
216
|
-
alias: song.alias,
|
|
217
|
-
isOffical: song.isOffical,
|
|
218
|
-
username: song.username,
|
|
219
|
-
artists: song.artists.map((artist) => ({
|
|
220
|
-
id: artist.id,
|
|
221
|
-
name: artist.name,
|
|
222
|
-
alias: artist.alias,
|
|
223
|
-
thumbnail: {
|
|
224
|
-
w240: artist.thumbnailM,
|
|
225
|
-
w360: artist.thumbnail
|
|
226
|
-
}
|
|
227
|
-
})),
|
|
228
|
-
thumbnail: {
|
|
229
|
-
w94: song.thumbnailM,
|
|
230
|
-
w240: song.thumbnail
|
|
231
|
-
},
|
|
232
|
-
duration: song.duration,
|
|
233
|
-
releaseDate: song.releaseDate
|
|
234
|
-
}));
|
|
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);
|
|
235
287
|
}
|
|
236
288
|
catch (error) {
|
|
237
289
|
if (error instanceof lapse.Lapse)
|
|
238
290
|
throw error;
|
|
239
|
-
throw new lapse.Lapse('Failed to
|
|
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);
|
|
240
413
|
}
|
|
241
414
|
}
|
|
242
415
|
}
|
|
243
|
-
const
|
|
244
|
-
maxRate: [
|
|
245
|
-
100 * 1024,
|
|
246
|
-
16 * 1024
|
|
247
|
-
]
|
|
248
|
-
};
|
|
249
|
-
const client = new ZingClient(clientOptions);
|
|
416
|
+
const client = new Client();
|
|
250
417
|
|
|
251
|
-
exports.
|
|
418
|
+
exports.Client = Client;
|
|
252
419
|
exports.default = client;
|
|
253
420
|
//# sourceMappingURL=index.cjs.map
|