@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.
- package/README.md +442 -323
- package/dist/cjs/index.cjs +315 -116
- 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 +315 -116
- 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 +44 -35
- package/dist/types/types/index.d.ts +9 -0
- 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 +18 -4
- package/dist/.types/index.d.ts +0 -23
- package/dist/.types/types/fetch.d.ts +0 -24
- package/dist/.types/types/index.d.ts +0 -8
- package/dist/types/tsdoc-metadata.json +0 -11
- /package/dist/{.types → types}/types/cookie.d.ts +0 -0
- /package/dist/{.types → types}/utils/cookies.d.ts +0 -0
- /package/dist/{.types → types}/utils/encrypt.d.ts +0 -0
- /package/dist/{.types → types}/utils/lapse.d.ts +0 -0
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,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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
46
|
+
ctime = Math.floor(Date.now() / 1000).toString();
|
|
47
|
+
instance;
|
|
48
|
+
maxLoad;
|
|
49
|
+
maxHighWaterMark;
|
|
50
|
+
userAgent;
|
|
51
|
+
jar;
|
|
26
52
|
constructor(options = {}) {
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
|
|
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:
|
|
36
|
-
apiKey:
|
|
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':
|
|
66
|
+
'User-Agent': this.userAgent
|
|
45
67
|
}
|
|
46
|
-
};
|
|
47
|
-
this.instance
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
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, {
|
|
72
97
|
params: {
|
|
73
98
|
id: videoID,
|
|
74
|
-
sig: encrypt.createSignature(
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
88
|
-
|
|
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
|
-
|
|
110
|
+
source.destroy(lapse$1);
|
|
91
111
|
});
|
|
92
|
-
return
|
|
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
|
-
|
|
101
|
-
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);
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
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, {
|
|
132
168
|
params: {
|
|
133
169
|
id: musicID,
|
|
134
|
-
sig: encrypt.createSignature(
|
|
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
|
-
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
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, {
|
|
147
179
|
params: {
|
|
148
180
|
id: musicID,
|
|
149
|
-
api_key:
|
|
150
|
-
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),
|
|
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 (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
const lapse$1 = new lapse.Lapse('Stream download failed', 'ERROR_STREAM_DOWNLOAD',
|
|
177
|
-
|
|
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
|
|
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
|
-
|
|
188
|
-
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);
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
205
|
-
|
|
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
|
|
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.
|
|
418
|
+
exports.Client = Client;
|
|
220
419
|
exports.default = client;
|
|
221
420
|
//# sourceMappingURL=index.cjs.map
|