gerdur-core 0.0.0-probe → 1.0.0
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/CHANGELOG.md +10 -0
- package/LICENSE +1 -0
- package/README.md +188 -1
- package/dist/api/api.d.ts +78 -0
- package/dist/api/api.js +182 -0
- package/dist/api/cache.d.ts +3 -0
- package/dist/api/cache.js +12 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +18 -0
- package/dist/api/request.d.ts +23 -0
- package/dist/api/request.js +88 -0
- package/dist/converter/deezer.d.ts +3 -0
- package/dist/converter/deezer.js +45 -0
- package/dist/converter/index.d.ts +5 -0
- package/dist/converter/index.js +34 -0
- package/dist/converter/parse.d.ts +18 -0
- package/dist/converter/parse.js +176 -0
- package/dist/converter/spotify.d.ts +40 -0
- package/dist/converter/spotify.js +154 -0
- package/dist/converter/tidal.d.ts +146 -0
- package/dist/converter/tidal.js +199 -0
- package/dist/converter/youtube.d.ts +6 -0
- package/dist/converter/youtube.js +102 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +24 -0
- package/dist/lib/decrypt.d.ts +9 -0
- package/dist/lib/decrypt.js +63 -0
- package/dist/lib/fast-lru.d.ts +63 -0
- package/dist/lib/fast-lru.js +121 -0
- package/dist/lib/get-url.d.ts +16 -0
- package/dist/lib/get-url.js +154 -0
- package/dist/lib/http.d.ts +66 -0
- package/dist/lib/http.js +235 -0
- package/dist/lib/metaflac-js.d.ts +124 -0
- package/dist/lib/metaflac-js.js +343 -0
- package/dist/lib/request.d.ts +18 -0
- package/dist/lib/request.js +86 -0
- package/dist/metadata-writer/abumCover.d.ts +10 -0
- package/dist/metadata-writer/abumCover.js +37 -0
- package/dist/metadata-writer/flacmetata.d.ts +3 -0
- package/dist/metadata-writer/flacmetata.js +80 -0
- package/dist/metadata-writer/getTrackLyrics.d.ts +2 -0
- package/dist/metadata-writer/getTrackLyrics.js +26 -0
- package/dist/metadata-writer/id3.d.ts +3 -0
- package/dist/metadata-writer/id3.js +119 -0
- package/dist/metadata-writer/index.d.ts +9 -0
- package/dist/metadata-writer/index.js +44 -0
- package/dist/metadata-writer/musixmatchLyrics.d.ts +1 -0
- package/dist/metadata-writer/musixmatchLyrics.js +39 -0
- package/dist/metadata-writer/useragents.d.ts +1 -0
- package/dist/metadata-writer/useragents.js +86 -0
- package/dist/types/album.d.ts +123 -0
- package/dist/types/album.js +2 -0
- package/dist/types/artist.d.ts +36 -0
- package/dist/types/artist.js +2 -0
- package/dist/types/channel.d.ts +33 -0
- package/dist/types/channel.js +2 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.js +26 -0
- package/dist/types/playlist-channel.d.ts +81 -0
- package/dist/types/playlist-channel.js +2 -0
- package/dist/types/playlist.d.ts +49 -0
- package/dist/types/playlist.js +2 -0
- package/dist/types/profile.d.ts +29 -0
- package/dist/types/profile.js +2 -0
- package/dist/types/radio.d.ts +7 -0
- package/dist/types/radio.js +2 -0
- package/dist/types/search.d.ts +79 -0
- package/dist/types/search.js +2 -0
- package/dist/types/show.d.ts +61 -0
- package/dist/types/show.js +2 -0
- package/dist/types/tracks.d.ts +144 -0
- package/dist/types/tracks.js +2 -0
- package/dist/types/user.d.ts +16 -0
- package/dist/types/user.js +2 -0
- package/package.json +67 -5
- package/types/index.d.ts +1 -0
- package/types/index.js +3 -0
- package/types/package.json +4 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getTrackDownloadUrl = exports.GeoBlocked = exports.WrongLicense = void 0;
|
|
7
|
+
const decrypt_1 = require("../lib/decrypt");
|
|
8
|
+
const http_1 = require("../lib/http");
|
|
9
|
+
const request_1 = __importDefault(require("../lib/request"));
|
|
10
|
+
class WrongLicense extends Error {
|
|
11
|
+
constructor(format) {
|
|
12
|
+
super();
|
|
13
|
+
this.name = 'WrongLicense';
|
|
14
|
+
this.message = `Your account can't stream ${format} tracks`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.WrongLicense = WrongLicense;
|
|
18
|
+
class GeoBlocked extends Error {
|
|
19
|
+
constructor(country) {
|
|
20
|
+
super();
|
|
21
|
+
this.name = 'GeoBlocked';
|
|
22
|
+
this.message = `This track is not available in your country (${country})`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.GeoBlocked = GeoBlocked;
|
|
26
|
+
let user_data = null;
|
|
27
|
+
const getTrackFileSize = (track, quality) => {
|
|
28
|
+
switch (quality) {
|
|
29
|
+
case 9:
|
|
30
|
+
return Number(track.FILESIZE_FLAC);
|
|
31
|
+
case 3:
|
|
32
|
+
return Number(track.FILESIZE_MP3_320);
|
|
33
|
+
case 1:
|
|
34
|
+
return Number(track.FILESIZE_MP3_128);
|
|
35
|
+
default:
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const dzAuthenticate = async () => {
|
|
40
|
+
const { data } = await request_1.default.get('https://www.deezer.com/ajax/gw-light.php', {
|
|
41
|
+
params: {
|
|
42
|
+
method: 'deezer.getUserData',
|
|
43
|
+
api_version: '1.0',
|
|
44
|
+
api_token: 'null',
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
user_data = {
|
|
48
|
+
license_token: data.results.USER.OPTIONS.license_token,
|
|
49
|
+
can_stream_lossless: data.results.USER.OPTIONS.web_lossless || data.results.USER.OPTIONS.mobile_loseless,
|
|
50
|
+
can_stream_hq: data.results.USER.OPTIONS.web_hq || data.results.USER.OPTIONS.mobile_hq,
|
|
51
|
+
country: data.results.COUNTRY,
|
|
52
|
+
};
|
|
53
|
+
return user_data;
|
|
54
|
+
};
|
|
55
|
+
const getTrackUrlFromServer = async (track_token, format) => {
|
|
56
|
+
const user = user_data ? user_data : await dzAuthenticate();
|
|
57
|
+
if ((format === 'FLAC' && !user.can_stream_lossless) || (format === 'MP3_320' && !user.can_stream_hq)) {
|
|
58
|
+
throw new WrongLicense(format);
|
|
59
|
+
}
|
|
60
|
+
const { data } = await request_1.default.post('https://media.deezer.com/v1/get_url', {
|
|
61
|
+
license_token: user.license_token,
|
|
62
|
+
media: [
|
|
63
|
+
{
|
|
64
|
+
type: 'FULL',
|
|
65
|
+
formats: [{ format, cipher: 'BF_CBC_STRIPE' }],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
track_tokens: [track_token],
|
|
69
|
+
});
|
|
70
|
+
if (data.data.length > 0) {
|
|
71
|
+
if (data.data[0].errors) {
|
|
72
|
+
if (data.data[0].errors[0].code === 2002) {
|
|
73
|
+
throw new GeoBlocked(user.country);
|
|
74
|
+
}
|
|
75
|
+
throw new Error(Object.entries(data.data[0].errors[0]).join(', '));
|
|
76
|
+
}
|
|
77
|
+
return data.data[0].media.length > 0 ? data.data[0].media[0].sources[0].url : null;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* @param track Track info json returned from `getTrackInfo`
|
|
83
|
+
* @param quality 1 = 128kbps, 3 = 320kbps and 9 = flac (around 1411kbps)
|
|
84
|
+
*/
|
|
85
|
+
const getTrackDownloadUrl = async (track, quality) => {
|
|
86
|
+
let wrongLicense = null;
|
|
87
|
+
let geoBlocked = null;
|
|
88
|
+
let formatName;
|
|
89
|
+
switch (quality) {
|
|
90
|
+
case 9:
|
|
91
|
+
formatName = 'FLAC';
|
|
92
|
+
break;
|
|
93
|
+
case 3:
|
|
94
|
+
formatName = 'MP3_320';
|
|
95
|
+
break;
|
|
96
|
+
case 1:
|
|
97
|
+
formatName = 'MP3_128';
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
throw new Error(`Unknown quality ${quality}`);
|
|
101
|
+
}
|
|
102
|
+
// Get URL with the official API
|
|
103
|
+
try {
|
|
104
|
+
const url = await getTrackUrlFromServer(track.TRACK_TOKEN, formatName);
|
|
105
|
+
if (url) {
|
|
106
|
+
return {
|
|
107
|
+
trackUrl: url,
|
|
108
|
+
isEncrypted: url.includes('/mobile/') || url.includes('/media/'),
|
|
109
|
+
fileSize: getTrackFileSize(track, quality),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
if (err instanceof WrongLicense) {
|
|
115
|
+
wrongLicense = err;
|
|
116
|
+
}
|
|
117
|
+
else if (err instanceof GeoBlocked) {
|
|
118
|
+
geoBlocked = err;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Fallback to the old method
|
|
125
|
+
if (track.MD5_ORIGIN) {
|
|
126
|
+
const filename = (0, decrypt_1.getSongFileName)(track, quality); // encrypted file name
|
|
127
|
+
const url = `https://e-cdns-proxy-${track.MD5_ORIGIN[0]}.dzcdn.net/mobile/1/${filename}`;
|
|
128
|
+
const fileSize = await testUrl(url);
|
|
129
|
+
if (fileSize > 0) {
|
|
130
|
+
return {
|
|
131
|
+
trackUrl: url,
|
|
132
|
+
isEncrypted: url.includes('/mobile/') || url.includes('/media/'),
|
|
133
|
+
fileSize: fileSize,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (wrongLicense) {
|
|
138
|
+
throw wrongLicense;
|
|
139
|
+
}
|
|
140
|
+
if (geoBlocked) {
|
|
141
|
+
throw geoBlocked;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
};
|
|
145
|
+
exports.getTrackDownloadUrl = getTrackDownloadUrl;
|
|
146
|
+
const testUrl = async (url) => {
|
|
147
|
+
try {
|
|
148
|
+
const { headers } = await (0, http_1.headRequest)(url);
|
|
149
|
+
return Number(headers['content-length']);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { IncomingHttpHeaders } from 'http';
|
|
4
|
+
type HttpMethod = 'GET' | 'POST' | 'HEAD';
|
|
5
|
+
type ResponseType = 'buffer' | 'json' | 'text';
|
|
6
|
+
type QueryValue = string | number | boolean | null | undefined;
|
|
7
|
+
type QueryInput = QueryValue | QueryValue[];
|
|
8
|
+
export type HttpHeaders = Record<string, string>;
|
|
9
|
+
export type HttpQuery = Record<string, QueryInput>;
|
|
10
|
+
export interface HttpRequestConfig {
|
|
11
|
+
headers?: HttpHeaders;
|
|
12
|
+
params?: HttpQuery;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
responseType?: ResponseType;
|
|
15
|
+
}
|
|
16
|
+
interface RequestDescriptor {
|
|
17
|
+
data?: unknown;
|
|
18
|
+
headers?: HttpHeaders;
|
|
19
|
+
method: HttpMethod;
|
|
20
|
+
params?: HttpQuery;
|
|
21
|
+
responseType: ResponseType;
|
|
22
|
+
timeout: number;
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
export interface HttpResponse<T> {
|
|
26
|
+
config: RequestDescriptor;
|
|
27
|
+
data: T;
|
|
28
|
+
headers: IncomingHttpHeaders;
|
|
29
|
+
request: {
|
|
30
|
+
res: {
|
|
31
|
+
responseUrl: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
status: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class HttpStatusError extends Error {
|
|
37
|
+
body: Buffer;
|
|
38
|
+
headers: IncomingHttpHeaders;
|
|
39
|
+
statusCode: number;
|
|
40
|
+
constructor(statusCode: number, headers: IncomingHttpHeaders, body: Buffer);
|
|
41
|
+
}
|
|
42
|
+
export declare class HttpClient {
|
|
43
|
+
defaults: {
|
|
44
|
+
baseURL?: string;
|
|
45
|
+
headers: HttpHeaders;
|
|
46
|
+
maxRedirects: number;
|
|
47
|
+
params: HttpQuery;
|
|
48
|
+
timeout: number;
|
|
49
|
+
};
|
|
50
|
+
constructor(defaults?: {
|
|
51
|
+
baseURL?: string;
|
|
52
|
+
headers?: HttpHeaders;
|
|
53
|
+
maxRedirects?: number;
|
|
54
|
+
params?: HttpQuery;
|
|
55
|
+
timeout?: number;
|
|
56
|
+
});
|
|
57
|
+
get<T = unknown>(url: string, config?: HttpRequestConfig): Promise<HttpResponse<T>>;
|
|
58
|
+
post<T = unknown>(url: string, body?: unknown, config?: HttpRequestConfig): Promise<HttpResponse<T>>;
|
|
59
|
+
head(url: string, config?: Omit<HttpRequestConfig, 'responseType'>): Promise<HttpResponse<Buffer>>;
|
|
60
|
+
private request;
|
|
61
|
+
}
|
|
62
|
+
export declare const getBuffer: (url: string, config?: HttpRequestConfig) => Promise<Buffer>;
|
|
63
|
+
export declare const getJson: <T = unknown>(url: string, config?: HttpRequestConfig) => Promise<T>;
|
|
64
|
+
export declare const getText: (url: string, config?: HttpRequestConfig) => Promise<string>;
|
|
65
|
+
export declare const headRequest: (url: string, config?: Omit<HttpRequestConfig, 'responseType'>) => Promise<HttpResponse<Buffer>>;
|
|
66
|
+
export {};
|
package/dist/lib/http.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.headRequest = exports.getText = exports.getJson = exports.getBuffer = exports.HttpClient = exports.HttpStatusError = void 0;
|
|
4
|
+
const http_1 = require("http");
|
|
5
|
+
const https_1 = require("https");
|
|
6
|
+
const zlib_1 = require("zlib");
|
|
7
|
+
const httpAgent = new http_1.Agent({ keepAlive: true });
|
|
8
|
+
const httpsAgent = new https_1.Agent({ keepAlive: true });
|
|
9
|
+
class HttpStatusError extends Error {
|
|
10
|
+
constructor(statusCode, headers, body) {
|
|
11
|
+
super(`Request failed with status code ${statusCode}`);
|
|
12
|
+
this.name = 'HttpStatusError';
|
|
13
|
+
this.statusCode = statusCode;
|
|
14
|
+
this.headers = headers;
|
|
15
|
+
this.body = body;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.HttpStatusError = HttpStatusError;
|
|
19
|
+
class HttpClient {
|
|
20
|
+
constructor(defaults = {}) {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
this.defaults = {
|
|
23
|
+
baseURL: defaults.baseURL,
|
|
24
|
+
headers: defaults.headers ? normalizeHeaders(defaults.headers) : {},
|
|
25
|
+
maxRedirects: (_a = defaults.maxRedirects) !== null && _a !== void 0 ? _a : 5,
|
|
26
|
+
params: defaults.params ? { ...defaults.params } : {},
|
|
27
|
+
timeout: (_b = defaults.timeout) !== null && _b !== void 0 ? _b : 15000,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
get(url, config = {}) {
|
|
31
|
+
return this.request('GET', url, undefined, config);
|
|
32
|
+
}
|
|
33
|
+
post(url, body, config = {}) {
|
|
34
|
+
return this.request('POST', url, body, config);
|
|
35
|
+
}
|
|
36
|
+
head(url, config = {}) {
|
|
37
|
+
return this.request('HEAD', url, undefined, { ...config, responseType: 'buffer' });
|
|
38
|
+
}
|
|
39
|
+
async request(method, url, body, config = {}) {
|
|
40
|
+
var _a, _b;
|
|
41
|
+
const responseType = (_a = config.responseType) !== null && _a !== void 0 ? _a : 'json';
|
|
42
|
+
const timeout = (_b = config.timeout) !== null && _b !== void 0 ? _b : this.defaults.timeout;
|
|
43
|
+
const headers = normalizeHeaders({ ...this.defaults.headers, ...config.headers });
|
|
44
|
+
const params = { ...this.defaults.params, ...config.params };
|
|
45
|
+
const rawResponse = await requestRaw({
|
|
46
|
+
baseURL: this.defaults.baseURL,
|
|
47
|
+
body,
|
|
48
|
+
headers,
|
|
49
|
+
maxRedirects: this.defaults.maxRedirects,
|
|
50
|
+
method,
|
|
51
|
+
params,
|
|
52
|
+
timeout,
|
|
53
|
+
url,
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
config: {
|
|
57
|
+
data: body,
|
|
58
|
+
headers,
|
|
59
|
+
method,
|
|
60
|
+
params,
|
|
61
|
+
responseType,
|
|
62
|
+
timeout,
|
|
63
|
+
url,
|
|
64
|
+
},
|
|
65
|
+
data: parseResponseBody(rawResponse, responseType),
|
|
66
|
+
headers: rawResponse.headers,
|
|
67
|
+
request: {
|
|
68
|
+
res: {
|
|
69
|
+
responseUrl: rawResponse.url,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
status: rawResponse.status,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.HttpClient = HttpClient;
|
|
77
|
+
const requestRaw = async (config, redirectCount = 0) => {
|
|
78
|
+
const requestUrl = buildUrl(config.url, config.baseURL, config.params);
|
|
79
|
+
const headers = normalizeHeaders(config.headers);
|
|
80
|
+
const serializedBody = serializeBody(config.body, headers);
|
|
81
|
+
if (serializedBody && !headers['content-length']) {
|
|
82
|
+
headers['content-length'] = String(serializedBody.length);
|
|
83
|
+
}
|
|
84
|
+
return await new Promise((resolve, reject) => {
|
|
85
|
+
var _a;
|
|
86
|
+
const isHttps = requestUrl.protocol === 'https:';
|
|
87
|
+
const requestFn = isHttps ? https_1.request : http_1.request;
|
|
88
|
+
const req = requestFn({
|
|
89
|
+
agent: isHttps ? httpsAgent : httpAgent,
|
|
90
|
+
headers,
|
|
91
|
+
hostname: requestUrl.hostname,
|
|
92
|
+
method: config.method,
|
|
93
|
+
path: requestUrl.pathname + requestUrl.search,
|
|
94
|
+
port: requestUrl.port,
|
|
95
|
+
protocol: requestUrl.protocol,
|
|
96
|
+
}, (response) => {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
const status = (_a = response.statusCode) !== null && _a !== void 0 ? _a : 0;
|
|
99
|
+
const locationHeader = response.headers.location;
|
|
100
|
+
const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
|
|
101
|
+
if (location && isRedirectStatus(status) && redirectCount < ((_b = config.maxRedirects) !== null && _b !== void 0 ? _b : 5)) {
|
|
102
|
+
response.resume();
|
|
103
|
+
const redirectedMethod = status === 303 || ((status === 301 || status === 302) && config.method === 'POST') ? 'GET' : config.method;
|
|
104
|
+
const redirectedHeaders = { ...headers };
|
|
105
|
+
if (redirectedMethod === 'GET' || redirectedMethod === 'HEAD') {
|
|
106
|
+
delete redirectedHeaders['content-length'];
|
|
107
|
+
delete redirectedHeaders['content-type'];
|
|
108
|
+
}
|
|
109
|
+
requestRaw({
|
|
110
|
+
...config,
|
|
111
|
+
baseURL: undefined,
|
|
112
|
+
body: redirectedMethod === 'POST' ? config.body : undefined,
|
|
113
|
+
headers: redirectedHeaders,
|
|
114
|
+
method: redirectedMethod,
|
|
115
|
+
url: new URL(location, requestUrl).toString(),
|
|
116
|
+
}, redirectCount + 1)
|
|
117
|
+
.then(resolve)
|
|
118
|
+
.catch(reject);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const chunks = [];
|
|
122
|
+
response.on('data', (chunk) => {
|
|
123
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
124
|
+
});
|
|
125
|
+
response.on('end', () => {
|
|
126
|
+
try {
|
|
127
|
+
const body = decodeResponseBuffer(Buffer.concat(chunks), response.headers['content-encoding']);
|
|
128
|
+
if (status < 200 || status >= 300) {
|
|
129
|
+
reject(new HttpStatusError(status, response.headers, body));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
resolve({
|
|
133
|
+
body,
|
|
134
|
+
headers: response.headers,
|
|
135
|
+
status,
|
|
136
|
+
url: requestUrl.toString(),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
reject(err);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
req.on('error', reject);
|
|
145
|
+
req.setTimeout((_a = config.timeout) !== null && _a !== void 0 ? _a : 15000, () => {
|
|
146
|
+
var _a;
|
|
147
|
+
req.destroy(new Error(`Request timed out after ${(_a = config.timeout) !== null && _a !== void 0 ? _a : 15000}ms`));
|
|
148
|
+
});
|
|
149
|
+
if (serializedBody) {
|
|
150
|
+
req.write(serializedBody);
|
|
151
|
+
}
|
|
152
|
+
req.end();
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const buildUrl = (url, baseURL, params) => {
|
|
156
|
+
const parsedUrl = new URL(resolveUrl(url, baseURL));
|
|
157
|
+
if (params) {
|
|
158
|
+
for (const [key, input] of Object.entries(params)) {
|
|
159
|
+
if (Array.isArray(input)) {
|
|
160
|
+
for (const value of input) {
|
|
161
|
+
if (value !== undefined && value !== null) {
|
|
162
|
+
parsedUrl.searchParams.append(key, String(value));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else if (input !== undefined && input !== null) {
|
|
167
|
+
parsedUrl.searchParams.set(key, String(input));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return parsedUrl;
|
|
172
|
+
};
|
|
173
|
+
const resolveUrl = (url, baseURL) => {
|
|
174
|
+
if (!baseURL || isAbsoluteUrl(url)) {
|
|
175
|
+
return url;
|
|
176
|
+
}
|
|
177
|
+
return `${baseURL.replace(/\/+$/, '')}/${url.replace(/^\/+/, '')}`;
|
|
178
|
+
};
|
|
179
|
+
const normalizeHeaders = (headers = {}) => {
|
|
180
|
+
const normalizedHeaders = {};
|
|
181
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
182
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
183
|
+
}
|
|
184
|
+
return normalizedHeaders;
|
|
185
|
+
};
|
|
186
|
+
const serializeBody = (body, headers) => {
|
|
187
|
+
if (body === undefined) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
if (Buffer.isBuffer(body)) {
|
|
191
|
+
return body;
|
|
192
|
+
}
|
|
193
|
+
if (typeof body === 'string') {
|
|
194
|
+
return Buffer.from(body);
|
|
195
|
+
}
|
|
196
|
+
if (!headers['content-type']) {
|
|
197
|
+
headers['content-type'] = 'application/json; charset=UTF-8';
|
|
198
|
+
}
|
|
199
|
+
return Buffer.from(JSON.stringify(body));
|
|
200
|
+
};
|
|
201
|
+
const decodeResponseBuffer = (buffer, contentEncoding) => {
|
|
202
|
+
var _a, _b;
|
|
203
|
+
const encoding = (_b = (_a = (Array.isArray(contentEncoding) ? contentEncoding[0] : contentEncoding)) === null || _a === void 0 ? void 0 : _a.split(',')[0]) === null || _b === void 0 ? void 0 : _b.trim();
|
|
204
|
+
switch (encoding) {
|
|
205
|
+
case 'br':
|
|
206
|
+
return (0, zlib_1.brotliDecompressSync)(buffer);
|
|
207
|
+
case 'deflate':
|
|
208
|
+
return (0, zlib_1.inflateSync)(buffer);
|
|
209
|
+
case 'gzip':
|
|
210
|
+
return (0, zlib_1.gunzipSync)(buffer);
|
|
211
|
+
default:
|
|
212
|
+
return buffer;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const parseResponseBody = (response, responseType) => {
|
|
216
|
+
if (responseType === 'buffer') {
|
|
217
|
+
return response.body;
|
|
218
|
+
}
|
|
219
|
+
const text = response.body.toString('utf-8');
|
|
220
|
+
if (responseType === 'text') {
|
|
221
|
+
return text;
|
|
222
|
+
}
|
|
223
|
+
return JSON.parse(text);
|
|
224
|
+
};
|
|
225
|
+
const isRedirectStatus = (status) => [301, 302, 303, 307, 308].includes(status);
|
|
226
|
+
const isAbsoluteUrl = (url) => /^[a-z][a-z\d+\-.]*:\/\//i.test(url);
|
|
227
|
+
const defaultClient = new HttpClient();
|
|
228
|
+
const getBuffer = async (url, config = {}) => (await defaultClient.get(url, { ...config, responseType: 'buffer' })).data;
|
|
229
|
+
exports.getBuffer = getBuffer;
|
|
230
|
+
const getJson = async (url, config = {}) => (await defaultClient.get(url, { ...config, responseType: 'json' })).data;
|
|
231
|
+
exports.getJson = getJson;
|
|
232
|
+
const getText = async (url, config = {}) => (await defaultClient.get(url, { ...config, responseType: 'text' })).data;
|
|
233
|
+
exports.getText = getText;
|
|
234
|
+
const headRequest = async (url, config = {}) => await defaultClient.head(url, config);
|
|
235
|
+
exports.headRequest = headRequest;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
declare class Metaflac {
|
|
3
|
+
buffer: Buffer;
|
|
4
|
+
marker: string;
|
|
5
|
+
streamInfo: any;
|
|
6
|
+
blocks: any[];
|
|
7
|
+
padding: any;
|
|
8
|
+
vorbisComment: any;
|
|
9
|
+
vendorString: string;
|
|
10
|
+
tags: any;
|
|
11
|
+
pictures: Buffer[];
|
|
12
|
+
picturesSpecs: object[];
|
|
13
|
+
picturesDatas: string[];
|
|
14
|
+
framesOffset: number;
|
|
15
|
+
constructor(flac: Buffer);
|
|
16
|
+
init(): void;
|
|
17
|
+
parseVorbisComment(): void;
|
|
18
|
+
parsePictureBlock(): void;
|
|
19
|
+
getPicturesSpecs(): object[];
|
|
20
|
+
/**
|
|
21
|
+
* Get the MD5 signature from the STREAMINFO block.
|
|
22
|
+
*/
|
|
23
|
+
getMd5sum(): any;
|
|
24
|
+
/**
|
|
25
|
+
* Get the minimum block size from the STREAMINFO block.
|
|
26
|
+
*/
|
|
27
|
+
getMinBlocksize(): any;
|
|
28
|
+
/**
|
|
29
|
+
* Get the maximum block size from the STREAMINFO block.
|
|
30
|
+
*/
|
|
31
|
+
getMaxBlocksize(): any;
|
|
32
|
+
/**
|
|
33
|
+
* Get the minimum frame size from the STREAMINFO block.
|
|
34
|
+
*/
|
|
35
|
+
getMinFramesize(): any;
|
|
36
|
+
/**
|
|
37
|
+
* Get the maximum frame size from the STREAMINFO block.
|
|
38
|
+
*/
|
|
39
|
+
getMaxFramesize(): any;
|
|
40
|
+
/**
|
|
41
|
+
* Get the sample rate from the STREAMINFO block.
|
|
42
|
+
*/
|
|
43
|
+
getSampleRate(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Get the number of channels from the STREAMINFO block.
|
|
46
|
+
*/
|
|
47
|
+
getChannels(): number;
|
|
48
|
+
/**
|
|
49
|
+
* Get the # of bits per sample from the STREAMINFO block.
|
|
50
|
+
*/
|
|
51
|
+
getBps(): number;
|
|
52
|
+
/**
|
|
53
|
+
* Get the total # of samples from the STREAMINFO block.
|
|
54
|
+
*/
|
|
55
|
+
getTotalSamples(): number;
|
|
56
|
+
/**
|
|
57
|
+
* Show the vendor string from the VORBIS_COMMENT block.
|
|
58
|
+
*/
|
|
59
|
+
getVendorTag(): string;
|
|
60
|
+
/**
|
|
61
|
+
* Get all tags where the the field name matches NAME.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} name
|
|
64
|
+
*/
|
|
65
|
+
getTag(name: string): any;
|
|
66
|
+
/**
|
|
67
|
+
* Remove all tags whose field name is NAME.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} name
|
|
70
|
+
*/
|
|
71
|
+
removeTag(name: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Remove first tag whose field name is NAME.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} name
|
|
76
|
+
*/
|
|
77
|
+
removeFirstTag(name: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Remove all tags, leaving only the vendor string.
|
|
80
|
+
*/
|
|
81
|
+
removeAllTags(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Add a tag.
|
|
84
|
+
* The FIELD must comply with the Vorbis comment spec, of the form NAME=VALUE. If there is currently no tag block, one will be created.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} field
|
|
87
|
+
*/
|
|
88
|
+
setTag(field: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Import a picture and store it in a PICTURE metadata block.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} filename
|
|
93
|
+
*/
|
|
94
|
+
importPicture(picture: Buffer, dimension: number, mime: 'image/jpeg' | 'image/png'): void;
|
|
95
|
+
/**
|
|
96
|
+
* Return all tags.
|
|
97
|
+
*/
|
|
98
|
+
getAllTags(): any;
|
|
99
|
+
buildSpecification(spec?: {}): {
|
|
100
|
+
type: number;
|
|
101
|
+
mime: string;
|
|
102
|
+
description: string;
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
depth: number;
|
|
106
|
+
colors: number;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Build a picture block.
|
|
110
|
+
*
|
|
111
|
+
* @param {Buffer} picture
|
|
112
|
+
* @param {Object} specification
|
|
113
|
+
* @returns {Buffer}
|
|
114
|
+
*/
|
|
115
|
+
buildPictureBlock(picture: Buffer, specification?: any): Buffer;
|
|
116
|
+
buildMetadataBlock(type: number, block: Buffer, isLast?: boolean): Buffer;
|
|
117
|
+
buildMetadata(): Buffer[];
|
|
118
|
+
buildStream(): Buffer[];
|
|
119
|
+
/**
|
|
120
|
+
* Save changes to buffer and return changed buffer
|
|
121
|
+
*/
|
|
122
|
+
getBuffer(): Buffer;
|
|
123
|
+
}
|
|
124
|
+
export default Metaflac;
|