@khang07/zing-mp3-api 1.3.6 → 1.3.7

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 CHANGED
@@ -1,328 +1,388 @@
1
- # `@khang07/zing-mp3-api`
2
-
3
- The basic APIs provide the features of ZingMp3.
4
-
5
- ## Features
6
-
7
- * Search songs, videos, playlists, and artists
8
- * Fetch playlist, artist, and song details
9
- * Get readable streams for music and video
10
- * Accept raw resource tokens or `URL` values for resource-based methods
11
- * Provide a cookie jar utility through a public subpath export
12
- * Ship ESM and CommonJS entry points
13
-
14
- ## Installation
15
-
16
- ```bash
17
- npm install @khang07/zing-mp3-api
18
- ```
19
-
20
- ## Public Exports
21
-
22
- ### Root export
23
-
24
- #### Runtime exports
25
-
26
- * `default`: a pre-created `Client` instance
27
- * `Client`: the client class
28
-
29
- #### Type exports
30
-
31
- * `ClientOptions`
32
- * `PlayList`
33
- * `Artist`
34
- * `Media`
35
- * `SearhMedia`
36
- * `SearchPlayList`
37
- * `Cookies` *(type-only export from the root entry)*
38
-
39
- ### Public subpath exports
40
-
41
- ```ts
42
- import { Cookies } from "@khang07/zing-mp3-api/utils/cookies";
43
- import { createSignature } from "@khang07/zing-mp3-api/utils/encrypt";
44
- import { Lapse } from "@khang07/zing-mp3-api/utils/lapse";
45
- ```
46
-
47
- ## Import
48
-
49
- ### ESM
50
-
51
- ```ts
52
- import client, { Client } from "@khang07/zing-mp3-api";
53
- ```
54
-
55
- ### CommonJS
56
-
57
- ```js
58
- const zing = require("@khang07/zing-mp3-api");
59
-
60
- const client = zing.default;
61
- const { Client } = zing;
62
- ```
63
-
64
- ## Basic Usage
65
-
66
- ### Use the default client
67
-
68
- ```ts
69
- import client from "@khang07/zing-mp3-api";
70
-
71
- const items = await client.searchMusic("Do For Love Bray");
72
- console.log(items[0]);
73
- ```
74
-
75
- ### Create a client
76
-
77
- ```ts
78
- import { Client } from "@khang07/zing-mp3-api";
79
- import { Cookies } from "@khang07/zing-mp3-api/utils/cookies";
80
-
81
- const client = new Client({
82
- maxLoad: 16 * 1024,
83
- maxHighWaterMark: 16 * 1024,
84
- userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
85
- jar: new Cookies()
86
- });
87
- ```
88
-
89
- ### Fetch a playlist from a URL
90
-
91
- ```ts
92
- import client from "@khang07/zing-mp3-api";
93
-
94
- const playlist = await client.playlist("https://zingmp3.vn/album/example/ZWZB9WAB.html");
95
- console.log(playlist.name);
96
- console.log(playlist.mediaCount);
97
- ```
98
-
99
- ### Fetch artist details
100
-
101
- ```ts
102
- import client from "@khang07/zing-mp3-api";
103
-
104
- const artist = await client.artist("https://zingmp3.vn/Obito");
105
- console.log(artist.name);
106
- console.log(artist.followCount);
107
- ```
108
-
109
- ### Download a music stream
110
-
111
- ```ts
112
- import { createWriteStream } from "node:fs";
113
- import client from "@khang07/zing-mp3-api";
114
-
115
- const items = await client.searchMusic("Do For Love Bray");
116
- const stream = await client.music(items[0].id);
117
-
118
- stream.pipe(createWriteStream("music.mp3"));
119
- ```
120
-
121
- ### Download a video stream
122
-
123
- ```ts
124
- import { createWriteStream } from "node:fs";
125
- import client from "@khang07/zing-mp3-api";
126
-
127
- const items = await client.searchVideo("Do For Love Bray");
128
- const stream = await client.video(items[0].id);
129
-
130
- stream.pipe(createWriteStream("video.ts"));
131
- ```
132
-
133
- ### Use the immediate stream-returning methods
134
-
135
- ```ts
136
- import { createWriteStream } from "node:fs";
137
- import client from "@khang07/zing-mp3-api";
138
-
139
- const stream = client.musicSync("ZWZB9WAB");
140
- stream.pipe(createWriteStream("music.mp3"));
141
- ```
142
-
143
- ### Handle library errors
144
-
145
- ```ts
146
- import client from "@khang07/zing-mp3-api";
147
- import { Lapse } from "@khang07/zing-mp3-api/utils/lapse";
148
-
149
- try {
150
- await client.playlist("");
151
- } catch (error) {
152
- if (error instanceof Lapse) {
153
- console.error(error.name);
154
- console.error(error.code);
155
- console.error(error.status);
156
- }
157
- }
158
- ```
159
-
160
- ## API Reference
161
-
162
- ## `new Client(options?)`
163
-
164
- Creates a new client instance.
165
-
166
- ### `ClientOptions`
167
-
168
- | Field | Type | Default |
169
- | ------------------ | --------- | -------------------------------------------------------------------------------------------------------------------- |
170
- | `maxLoad` | `number` | `1024 * 1024` |
171
- | `maxHighWaterMark` | `number` | `16 * 1024` |
172
- | `userAgent` | `string` | `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3` |
173
- | `jar` | `Cookies` | `new Cookies()` |
174
-
175
- `maxLoad` is passed to Axios as `maxRate`.
176
-
177
- `maxHighWaterMark` is used as the `highWaterMark` when `musicSync()` and `videoSync()` create a `PassThrough` stream.
178
-
179
- ## `Client.getIDFromURL(url)`
180
-
181
- ```ts
182
- static getIDFromURL(url: string): string
183
- ```
184
-
185
- Extracts a resource token from a ZingMp3 URL.
186
-
187
- It throws `ERROR_INVALID_URL` when the input is not a non-empty string or when no token can be extracted.
188
-
189
- ## Instance Methods
190
-
191
- | Method | Returns | Description |
192
- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
193
- | `video(videoID)` | `Promise<Readable>` | Fetches a video stream. Accepts a raw token, URL string, or `URL`. |
194
- | `videoSync(videoID)` | `Readable` | Returns a `PassThrough` immediately and pipes the fetched video stream into it. |
195
- | `music(musicID)` | `Promise<Readable>` | Fetches a music stream. Accepts a raw token, URL string, or `URL`. |
196
- | `musicSync(musicID)` | `Readable` | Returns a `PassThrough` immediately and pipes the fetched music stream into it. |
197
- | `playlist(playlistID)` | `Promise<PlayList>` | Fetches playlist details. Accepts a raw token, URL string, or `URL`. |
198
- | `artist(aliasID)` | `Promise<Artist>` | Fetches artist details. Accepts an alias token, URL string, or `URL`. |
199
- | `mediaDetails(mediaID)` | `Promise<Media>` | Fetches song details. Accepts a raw token, URL string, or `URL`. |
200
- | `searchMusic(query)` | `Promise<SearhMedia[]>` | Searches songs. |
201
- | `searchVideo(query)` | `Promise<SearhMedia[]>` | Searches videos. |
202
- | `searchList(query)` | `Promise<SearchPlayList[]>` | Searches playlists. |
203
- | `searchArtist(query)` | `Promise<SearchArtist[]>` | Searches artists. |
204
-
205
- ### Method notes
206
-
207
- * `video()` selects `720p` first, then falls back to `360p`.
208
- * `music()` selects `320` first when available and not equal to `"VIP"`, then falls back to `128`.
209
- * When the music API returns `err === -1150`, `music()` retries the extra music endpoint up to 4 times and throws `ERROR_MUSIC_VIP_ONLY` if it still cannot resolve a playable URL.
210
- * `searchMusic()`, `searchVideo()`, `searchList()`, and `searchArtist()` always request `page: 1` and `count: 20`.
211
-
212
- ## Public Types:
213
- - ### `Artist`
214
- - ### `PlayList`
215
- - ### `Media`
216
- - ### `SearchMedia`
217
- - ### `SearchPlayList`
218
- - ### `SearchArtist`
219
- - ### `Cookies`
220
-
221
- ### Methods
222
-
223
- | Method | Returns |
224
- | -------------------------------------- | ------------------------ |
225
- | `setCookie(setCookie, requestUrl)` | `void` |
226
- | `setCookies(setCookies, requestUrl)` | `void` |
227
- | `getCookies(requestUrl)` | `CookieRecord[]` |
228
- | `getCookieHeader(requestUrl)` | `string` |
229
- | `applyToHeaders(requestUrl, headers?)` | `Record<string, string>` |
230
- | `deleteCookie(domain, path, name)` | `void` |
231
- | `cleanup()` | `void` |
232
- | `toJSON()` | `CookieRecord[]` |
233
- | `fromJSON(cookies)` | `void` |
234
-
235
- ### Cookie record shape
236
-
237
- ```ts
238
- interface CookieRecord {
239
- name: string;
240
- value: string;
241
- domain: string;
242
- path: string;
243
- expiresAt?: number;
244
- secure: boolean;
245
- httpOnly: boolean;
246
- sameSite?: "Strict" | "Lax" | "None";
247
- hostOnly: boolean;
248
- }
249
- ```
250
-
251
- ### Example
252
-
253
- ```ts
254
- import { Cookies } from "@khang07/zing-mp3-api/utils/cookies";
255
-
256
- const jar = new Cookies();
257
- jar.setCookie("sid=abc; Path=/; HttpOnly", "https://zingmp3.vn/");
258
-
259
- console.log(jar.getCookieHeader("https://zingmp3.vn/"));
260
- ```
261
-
262
- ## `createSignature(uri, params, secret)`
263
-
264
- Import from `@khang07/zing-mp3-api/utils/encrypt`.
265
-
266
- ```ts
267
- function createSignature(uri: string, params: string, secret: string): string
268
- ```
269
-
270
- Generates the request signature used by the client.
271
-
272
- ## `Lapse`
273
-
274
- Import from `@khang07/zing-mp3-api/utils/lapse`.
275
-
276
- ```ts
277
- class Lapse extends Error {
278
- code: string;
279
- status?: number;
280
- cause?: unknown;
281
- }
282
- ```
283
-
284
- ### Constructor
285
-
286
- ```ts
287
- new Lapse(message: string, code: string, status?: number, cause?: unknown)
288
- ```
289
-
290
- ### Properties
291
-
292
- * `name`: always set to `"ZING_MP3_ERROR"`
293
- * `message`: inherited from `Error`
294
- * `code`: library error code
295
- * `status`: optional status or API error value
296
- * `cause`: optional original error or response payload
297
-
298
- ## Error Codes
299
-
300
- | Code | Used by |
301
- | ---------------------------- | ------------------------------------------------------------------ |
302
- | `ERROR_INVALID_URL` | `Client.getIDFromURL()` |
303
- | `ERROR_INVALID_ID` | `video()`, `music()`, `playlist()`, `artist()`, `mediaDetails()` |
304
- | `ERROR_INVALID_QUERY` | `searchMusic()`, `searchVideo()`, `searchList()`, `searchArtist()` |
305
- | `ERROR_VIDEO_NOT_FOUND` | `video()` |
306
- | `ERROR_VIDEO_FETCH` | `video()`, `videoSync()` |
307
- | `ERROR_MUSIC_VIP_ONLY` | `music()` |
308
- | `ERROR_MUSIC_FETCH` | `music()`, `musicSync()` |
309
- | `ERROR_PLAYLIST_NOT_FOUND` | `playlist()` |
310
- | `ERROR_PLAYLIST_FETCH` | `playlist()` |
311
- | `ERROR_ARTIST_NOT_FOUND` | `artist()` |
312
- | `ERROR_ARTIST_FETCH` | `artist()` |
313
- | `ERROR_MEDIA_NOT_FOUND` | `mediaDetails()` |
314
- | `ERROR_MEDIA_FETCH` | `mediaDetails()` |
315
- | `ERROR_SEARCH_FAILED` | `searchMusic()`, `searchVideo()`, `searchList()`, `searchArtist()` |
316
- | `ERROR_SEARCH_FETCH` | `searchMusic()`, `searchVideo()`, `searchList()`, `searchArtist()` |
317
- | `ERROR_STREAM_URL_NOT_FOUND` | `video()`, `music()` |
318
- | `ERROR_STREAM_DOWNLOAD` | `video()`, `videoSync()`, `music()`, `musicSync()` |
319
-
320
- ## Notes
321
-
322
- * The root entry exports `Cookies` as a type only. To construct a cookie jar, import `Cookies` from `@khang07/zing-mp3-api/utils/cookies`.
323
- * The root entry exports the type name `SearhMedia` exactly as written in source.
324
- * `searchArtist()` is a public method, but its `SearchArtist` type is not re-exported from the root entry.
325
- * No test, example, or demo files were included in the provided source bundle.
326
-
327
- ## License
328
- [MIT](https://github.com/GiaKhang1810/zing-mp3-api?tab=MIT-1-ov-file)
1
+ # @khang07/zing-mp3-api
2
+
3
+ A TypeScript library for working with Zing MP3 resources from Node.js.
4
+ It can search songs, videos, playlists, and artists; fetch detailed metadata;
5
+ and return readable streams for music and video playback or download.
6
+
7
+ For the source code, releases, and package metadata, visit the
8
+ [GitHub repository](https://github.com/GiaKhang1810/zing-mp3-api) and the
9
+ [npm package](https://www.npmjs.com/package/@khang07/zing-mp3-api).
10
+ Report bugs or request features in the
11
+ [issue tracker](https://github.com/GiaKhang1810/zing-mp3-api/issues).
12
+
13
+
14
+ ## Table of contents
15
+
16
+ - [Requirement](#requirements)
17
+ - [Installation](#installation)
18
+ - [Configuration](#configuration)
19
+ - [Usage](#usage)
20
+ - [API reference](#api-reference)
21
+ - [Public exports](#public-exports)
22
+ - [Error handling](#error-handling)
23
+ - [Troubleshooting & FAQ](#troubleshooting--faq)
24
+ - [Maintainers](#maintainers)
25
+ - [License](#license)
26
+
27
+
28
+ ## Requirements
29
+
30
+ This package has no special runtime requirements beyond a modern Node.js
31
+ environment and network access to [Zing MP3](https://zingmp3.vn).
32
+
33
+ Project dependencies:
34
+
35
+ - [axios](https://www.npmjs.com/package/axios)
36
+ - [m3u8stream](https://www.npmjs.com/package/m3u8stream)
37
+
38
+ Development tooling used by the project:
39
+
40
+ - [TypeScript](https://www.npmjs.com/package/typescript)
41
+ - [Rollup](https://www.npmjs.com/package/rollup)
42
+ - [Mocha](https://www.npmjs.com/package/mocha)
43
+
44
+
45
+ ## Installation
46
+
47
+ Install the package with npm:
48
+
49
+ ```bash
50
+ npm install @khang07/zing-mp3-api
51
+ ```
52
+
53
+ Build the project from source:
54
+
55
+ ```bash
56
+ npm run build
57
+ ```
58
+
59
+ Run tests:
60
+
61
+ ```bash
62
+ npm test
63
+ ```
64
+
65
+ Run live integration tests:
66
+
67
+ ```bash
68
+ ZING_MP3_LIVE=1 npm test
69
+ ```
70
+
71
+
72
+ ## Configuration
73
+
74
+ The library works out of the box with the default exported client.
75
+ If you need more control, create a `Client` instance with custom options.
76
+
77
+ ```ts
78
+ import { Client } from "@khang07/zing-mp3-api";
79
+ import { Cookies } from "@khang07/zing-mp3-api/utils/cookies";
80
+
81
+ const client = new Client({
82
+ maxLoad: 1024 * 1024,
83
+ maxHighWaterMark: 16 * 1024,
84
+ userAgent:
85
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
86
+ "(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
87
+ jar: new Cookies()
88
+ });
89
+ ```
90
+
91
+ `ClientOptions` fields:
92
+
93
+ - `maxLoad`: request rate limit passed to Axios as `maxRate`
94
+ - `maxHighWaterMark`: stream buffer size used by `musicSync()` and `videoSync()`
95
+ - `userAgent`: custom request user-agent string
96
+ - `jar`: custom cookie jar instance
97
+
98
+ There is no additional post-install configuration required.
99
+ The client automatically fetches and stores cookies before protected requests.
100
+
101
+
102
+ ## Usage
103
+
104
+ ### ESM
105
+
106
+ ```ts
107
+ import client, { Client } from "@khang07/zing-mp3-api";
108
+ ```
109
+
110
+ ### CommonJS
111
+
112
+ ```js
113
+ const zing = require("@khang07/zing-mp3-api");
114
+
115
+ const client = zing.default;
116
+ const { Client } = zing;
117
+ ```
118
+
119
+ ### Search for songs
120
+
121
+ ```ts
122
+ import client from "@khang07/zing-mp3-api";
123
+
124
+ const items = await client.searchMusic("Skyfall");
125
+ console.log(items[0]);
126
+ ```
127
+
128
+ ### Fetch playlist details
129
+
130
+ ```ts
131
+ import client from "@khang07/zing-mp3-api";
132
+
133
+ const playlist = await client.playlist(
134
+ "https://zingmp3.vn/album/example/ZWZB9WAB.html"
135
+ );
136
+
137
+ console.log(playlist.name);
138
+ console.log(playlist.mediaCount);
139
+ ```
140
+
141
+ ### Fetch artist details
142
+
143
+ ```ts
144
+ import client from "@khang07/zing-mp3-api";
145
+
146
+ const artist = await client.artist("https://zingmp3.vn/Obito");
147
+
148
+ console.log(artist.name);
149
+ console.log(artist.followCount);
150
+ ```
151
+
152
+ ### Download a music stream
153
+
154
+ ```ts
155
+ import { createWriteStream } from "node:fs";
156
+ import client from "@khang07/zing-mp3-api";
157
+
158
+ const items = await client.searchMusic("Example");
159
+ const stream = await client.music(items[0].id);
160
+
161
+ stream.pipe(createWriteStream("music.mp3"));
162
+ ```
163
+
164
+ ### Download a video stream
165
+
166
+ ```ts
167
+ import { createWriteStream } from "node:fs";
168
+ import client from "@khang07/zing-mp3-api";
169
+
170
+ const items = await client.searchVideo("Example");
171
+ const stream = await client.video(items[0].id);
172
+
173
+ stream.pipe(createWriteStream("video.ts"));
174
+ ```
175
+
176
+ ### Use the immediate stream APIs
177
+
178
+ ```ts
179
+ import { createWriteStream } from "node:fs";
180
+ import client from "@khang07/zing-mp3-api";
181
+
182
+ const stream = client.musicSync("Example ID");
183
+ stream.pipe(createWriteStream("music.mp3"));
184
+ ```
185
+
186
+ ### Handle library errors
187
+
188
+ ```ts
189
+ import client from "@khang07/zing-mp3-api";
190
+ import { Lapse } from "@khang07/zing-mp3-api/utils/lapse";
191
+
192
+ try {
193
+ await client.playlist("");
194
+ } catch (error) {
195
+ if (error instanceof Lapse) {
196
+ console.error(error.name);
197
+ console.error(error.code);
198
+ console.error(error.status);
199
+ console.error(error.cause);
200
+ }
201
+ }
202
+ ```
203
+
204
+
205
+ ## API reference
206
+
207
+ ### `new Client(options?)`
208
+
209
+ Creates a new client instance.
210
+
211
+ ### `Client.getIDFromURL(url)`
212
+
213
+ Extracts a resource token or alias from a Zing MP3 URL.
214
+ Throws `ERROR_INVALID_URL` when the input is empty or unsupported.
215
+
216
+ ### Instance methods
217
+
218
+ | Method | Returns | Description |
219
+ | --- | --- | --- |
220
+ | `video(videoID)` | `Promise<Readable>` | Fetch a video stream from a raw ID, URL string, or `URL` object. |
221
+ | `videoSync(videoID)` | `Readable` | Return a `PassThrough` immediately and pipe the resolved video stream into it. |
222
+ | `music(musicID)` | `Promise<Readable>` | Fetch a music stream from a raw ID, URL string, or `URL` object. |
223
+ | `musicSync(musicID)` | `Readable` | Return a `PassThrough` immediately and pipe the resolved music stream into it. |
224
+ | `playlist(playlistID)` | `Promise<PlayList>` | Fetch playlist details. |
225
+ | `artist(aliasID)` | `Promise<Artist>` | Fetch artist details. |
226
+ | `mediaDetails(mediaID)` | `Promise<Media>` | Fetch song details. |
227
+ | `searchMusic(query)` | `Promise<SearchMedia[]>` | Search songs. |
228
+ | `searchVideo(query)` | `Promise<SearchMedia[]>` | Search videos. |
229
+ | `searchList(query)` | `Promise<SearchPlayList[]>` | Search playlists. |
230
+ | `searchArtist(query)` | `Promise<SearchArtist[]>` | Search artists. |
231
+
232
+ ### Method notes
233
+
234
+ - `video()` prefers `720p` and falls back to `360p`
235
+ - `music()` prefers `320` when available and falls back to `128`
236
+ - If the main music endpoint returns a VIP-only response, `music()` retries the
237
+ extra music endpoint before throwing `ERROR_MUSIC_VIP_ONLY`
238
+ - Search methods currently request the first page with `count: 20`
239
+ - `musicSync()` and `videoSync()` forward stream failures to the returned
240
+ `PassThrough` and destroy the source stream when the output is closed
241
+
242
+ ### Data types
243
+
244
+ Public type exports from the root entry:
245
+
246
+ - `ClientOptions`
247
+ - `Artist`
248
+ - `Media`
249
+ - `PlayList`
250
+ - `SearchArtist`
251
+ - `SearchMedia`
252
+ - `SearchPlayList`
253
+
254
+
255
+ ## Public exports
256
+
257
+ ### Root export
258
+
259
+ Runtime exports:
260
+
261
+ - `default`: a ready-to-use `Client` instance
262
+ - `Client`: the client class
263
+
264
+ Type exports:
265
+
266
+ - `ClientOptions`
267
+ - `Artist`
268
+ - `Media`
269
+ - `PlayList`
270
+ - `SearchArtist`
271
+ - `SearchMedia`
272
+ - `SearchPlayList`
273
+
274
+ ### Public utility subpaths
275
+
276
+ ```ts
277
+ import { Cookies } from "@khang07/zing-mp3-api/utils/cookies";
278
+ import { createSignature } from "@khang07/zing-mp3-api/utils/encrypt";
279
+ import { Lapse } from "@khang07/zing-mp3-api/utils/lapse";
280
+ ```
281
+
282
+ ### `Cookies` helper
283
+
284
+ Methods:
285
+
286
+ - `setCookie(setCookie, requestUrl)`
287
+ - `setCookies(setCookies, requestUrl)`
288
+ - `getCookies(requestUrl)`
289
+ - `getCookieHeader(requestUrl)`
290
+ - `applyToHeaders(requestUrl, headers?)`
291
+ - `deleteCookie(domain, path, name)`
292
+ - `cleanup()`
293
+ - `toJSON()`
294
+ - `fromJSON(cookies)`
295
+
296
+ Cookie record shape:
297
+
298
+ ```ts
299
+ interface CookieRecord {
300
+ name: string;
301
+ value: string;
302
+ domain: string;
303
+ path: string;
304
+ expiresAt?: number;
305
+ secure: boolean;
306
+ httpOnly: boolean;
307
+ sameSite?: "Strict" | "Lax" | "None";
308
+ hostOnly: boolean;
309
+ }
310
+ ```
311
+
312
+ Example:
313
+
314
+ ```ts
315
+ import { Cookies } from "@khang07/zing-mp3-api/utils/cookies";
316
+
317
+ const jar = new Cookies();
318
+ jar.setCookie("sid=abc; Path=/; HttpOnly", "https://zingmp3.vn/");
319
+
320
+ console.log(jar.getCookieHeader("https://zingmp3.vn/"));
321
+ ```
322
+
323
+
324
+ ## Error handling
325
+
326
+ The library throws `Lapse`, a custom error type with the following fields:
327
+
328
+ - `name`
329
+ - `message`
330
+ - `code`
331
+ - `status`
332
+ - `cause`
333
+
334
+ Known library error codes:
335
+
336
+ - `ERROR_ARTIST_FETCH`
337
+ - `ERROR_ARTIST_NOT_FOUND`
338
+ - `ERROR_INVALID_ID`
339
+ - `ERROR_INVALID_QUERY`
340
+ - `ERROR_INVALID_URL`
341
+ - `ERROR_MEDIA_FETCH`
342
+ - `ERROR_MEDIA_NOT_FOUND`
343
+ - `ERROR_MUSIC_FETCH`
344
+ - `ERROR_MUSIC_VIP_ONLY`
345
+ - `ERROR_PLAYLIST_FETCH`
346
+ - `ERROR_PLAYLIST_NOT_FOUND`
347
+ - `ERROR_SEARCH_FAILED`
348
+ - `ERROR_SEARCH_FETCH`
349
+ - `ERROR_STREAM_DOWNLOAD`
350
+ - `ERROR_STREAM_URL_NOT_FOUND`
351
+ - `ERROR_VIDEO_FETCH`
352
+ - `ERROR_VIDEO_NOT_FOUND`
353
+
354
+
355
+ ## Troubleshooting & FAQ
356
+
357
+ ### `video()` returns a stream, but saved files do not open as MP4
358
+
359
+ Video playback is fetched from an HLS source via `m3u8stream`. If you write the
360
+ stream directly to disk, save it as a transport-stream style output such as
361
+ `.ts`, or post-process it with your own media pipeline.
362
+
363
+ ### Music lookup fails with `ERROR_MUSIC_VIP_ONLY`
364
+
365
+ Some tracks are restricted. The client already retries the fallback music
366
+ endpoint, but a playable URL may still be unavailable.
367
+
368
+ ### I want to reuse cookies between sessions
369
+
370
+ Use `jar.toJSON()` to persist cookies and `jar.fromJSON()` to restore them.
371
+ This is useful if you want to avoid starting from an empty cookie jar each time.
372
+
373
+ ### Input URLs fail with `ERROR_INVALID_URL`
374
+
375
+ Use either a standard Zing MP3 HTML URL such as
376
+ `https://zingmp3.vn/bai-hat/test/ZWZB9WAB.html` or a root-style artist URL such
377
+ as `https://zingmp3.vn/mono`.
378
+
379
+
380
+ ## Maintainers
381
+
382
+ - GiaKhang - [GiaKhang1810](https://github.com/GiaKhang1810)
383
+
384
+
385
+ ## License
386
+
387
+ This project is licensed under the MIT License.
388
+ See the [LICENSE](./LICENSE) file for details.