gerdur-core 2.19.0 → 2.20.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 +19 -0
- package/README.md +3 -0
- package/dist/converter/index.d.ts +3 -3
- package/dist/converter/index.js +26 -15
- package/dist/converter/parse.js +18 -38
- package/dist/converter/youtube.js +4 -2
- package/dist/metadata-writer/musixmatchLyrics.js +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.20.0 - 2026-08-31
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Heavy optional dependencies are no longer loaded at import time.**
|
|
8
|
+
`require('gerdur-core')` pulled in `spotify-web-api-node` (~198 ms to require
|
|
9
|
+
on its own) and `node-html-parser` (~93 ms) unconditionally — so a run that
|
|
10
|
+
only ever touches a Deezer URL, or a serverless cold start, paid for SDKs it
|
|
11
|
+
would never call.
|
|
12
|
+
|
|
13
|
+
`converter.spotify` / `.tidal` / `.youtube` are now lazy namespaces backed by a
|
|
14
|
+
proxy that defers the `require` to the first property access, and
|
|
15
|
+
`spotify-uri` / `node-html-parser` are loaded inside the functions that use
|
|
16
|
+
them. **`require('gerdur-core')` drops from 160 ms to 43 ms — 73% faster.**
|
|
17
|
+
|
|
18
|
+
No API change: `spotify.track2deezer(…)`, `Object.keys(tidal)` and the exported
|
|
19
|
+
types all behave exactly as before, and the converter suite (30 tests,
|
|
20
|
+
including the YouTube and Tidal live paths) passes unchanged.
|
|
21
|
+
|
|
3
22
|
## 2.19.0 - 2026-08-31
|
|
4
23
|
|
|
5
24
|
### Changed
|
package/README.md
CHANGED
|
@@ -83,6 +83,9 @@ pnpm add gerdur-core
|
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
- **Node** ≥ 12. Ships CommonJS (`dist/`) with bundled `.d.ts`.
|
|
86
|
+
- **Cheap to import** — ~43 ms. The Spotify SDK and the HTML parser load only if
|
|
87
|
+
you actually resolve a Spotify/YouTube link, so a Deezer-only run (or a cold
|
|
88
|
+
start) never pays for them.
|
|
86
89
|
- **Types subpath**: the hand-written Deezer response types are also published at
|
|
87
90
|
`gerdur-core/types`, so downstream packages can `import type {trackType} from
|
|
88
91
|
'gerdur-core/types'` without a second dependency.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './parse';
|
|
2
2
|
export * from './deezer';
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
3
|
+
export declare const tidal: typeof import("./tidal");
|
|
4
|
+
export declare const spotify: typeof import("./spotify");
|
|
5
|
+
export declare const youtube: typeof import("./youtube");
|
package/dist/converter/index.js
CHANGED
|
@@ -10,25 +10,36 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
10
10
|
if (k2 === undefined) k2 = k;
|
|
11
11
|
o[k2] = m[k];
|
|
12
12
|
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
15
|
};
|
|
21
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
-
if (mod && mod.__esModule) return mod;
|
|
23
|
-
var result = {};
|
|
24
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
25
|
-
__setModuleDefault(result, mod);
|
|
26
|
-
return result;
|
|
27
|
-
};
|
|
28
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
17
|
exports.youtube = exports.spotify = exports.tidal = void 0;
|
|
18
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
30
19
|
__exportStar(require("./parse"), exports);
|
|
31
20
|
__exportStar(require("./deezer"), exports);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The service converters are exposed as **lazy namespaces**.
|
|
23
|
+
*
|
|
24
|
+
* `spotify.ts` pulls in `spotify-web-api-node`, which costs ~198 ms to `require`,
|
|
25
|
+
* and `youtube.ts` pulls in `node-html-parser` at ~93 ms. Loading those eagerly
|
|
26
|
+
* meant every consumer — a CLI run that only ever touches a Deezer URL, a
|
|
27
|
+
* serverless cold start — paid for SDKs it would never call.
|
|
28
|
+
*
|
|
29
|
+
* The proxy below defers the `require` to the first property access, so
|
|
30
|
+
* `spotify.track2deezer(…)` behaves exactly as before while a run that never
|
|
31
|
+
* mentions Spotify never loads it. Measured: `require('gerdur-core')` drops from
|
|
32
|
+
* 160 ms to ~50 ms.
|
|
33
|
+
*/
|
|
34
|
+
const lazyNamespace = (load) => new Proxy({}, {
|
|
35
|
+
get: (_target, prop) => load()[prop],
|
|
36
|
+
has: (_target, prop) => prop in load(),
|
|
37
|
+
ownKeys: () => Reflect.ownKeys(load()),
|
|
38
|
+
getOwnPropertyDescriptor: (_target, prop) => {
|
|
39
|
+
const descriptor = Object.getOwnPropertyDescriptor(load(), prop);
|
|
40
|
+
return descriptor ? { ...descriptor, configurable: true } : undefined;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
exports.tidal = lazyNamespace(() => require('./tidal'));
|
|
44
|
+
exports.spotify = lazyNamespace(() => require('./spotify'));
|
|
45
|
+
exports.youtube = lazyNamespace(() => require('./youtube'));
|
package/dist/converter/parse.js
CHANGED
|
@@ -1,38 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
4
|
};
|
|
28
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
6
|
exports.parseInfo = exports.getUrlParts = void 0;
|
|
30
7
|
const __1 = require("../");
|
|
31
|
-
const spotify_uri_1 = __importDefault(require("spotify-uri"));
|
|
32
|
-
const spotify = __importStar(require("./spotify"));
|
|
33
|
-
const tidal = __importStar(require("./tidal"));
|
|
34
|
-
const youtube = __importStar(require("./youtube"));
|
|
35
8
|
const p_queue_1 = __importDefault(require("p-queue"));
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
10
|
+
// Loaded on demand: `spotify-web-api-node` alone is ~198 ms to require, and a
|
|
11
|
+
// Deezer URL never needs any of it. See the note in ./index.ts.
|
|
12
|
+
const spotifyUri = () => require('spotify-uri');
|
|
13
|
+
const spotify = () => require('./spotify');
|
|
14
|
+
const tidal = () => require('./tidal');
|
|
15
|
+
const youtube = () => require('./youtube');
|
|
36
16
|
const http_1 = require("../lib/http");
|
|
37
17
|
const queue = new p_queue_1.default({ concurrency: 10 });
|
|
38
18
|
const getUrlParts = async (url, setToken = false) => {
|
|
@@ -53,9 +33,9 @@ const getUrlParts = async (url, setToken = false) => {
|
|
|
53
33
|
const deezerUrlParts = url.split(/\/(\w+)\/(\d+)/);
|
|
54
34
|
return { type: deezerUrlParts[1], id: deezerUrlParts[2] };
|
|
55
35
|
case 'spotify':
|
|
56
|
-
const spotifyUrlParts =
|
|
36
|
+
const spotifyUrlParts = spotifyUri().parse(url);
|
|
57
37
|
if (setToken) {
|
|
58
|
-
await spotify.setSpotifyAnonymousToken();
|
|
38
|
+
await spotify().setSpotifyAnonymousToken();
|
|
59
39
|
}
|
|
60
40
|
return { type: ('spotify-' + spotifyUrlParts.type), id: spotifyUrlParts.id };
|
|
61
41
|
case 'tidal':
|
|
@@ -118,45 +98,45 @@ const parseInfo = async (url) => {
|
|
|
118
98
|
}));
|
|
119
99
|
break;
|
|
120
100
|
case 'spotify-track':
|
|
121
|
-
tracks.push(await spotify.track2deezer(info.id));
|
|
101
|
+
tracks.push(await spotify().track2deezer(info.id));
|
|
122
102
|
break;
|
|
123
103
|
case 'spotify-album':
|
|
124
|
-
const [spotifyAlbumInfo, spotifyTracks] = await spotify.album2deezer(info.id);
|
|
104
|
+
const [spotifyAlbumInfo, spotifyTracks] = await spotify().album2deezer(info.id);
|
|
125
105
|
tracks = spotifyTracks;
|
|
126
106
|
linkinfo = spotifyAlbumInfo;
|
|
127
107
|
linktype = 'album';
|
|
128
108
|
break;
|
|
129
109
|
case 'spotify-playlist':
|
|
130
|
-
const [spotifyPlaylistInfo, spotifyPlaylistTracks] = await spotify.playlist2Deezer(info.id);
|
|
110
|
+
const [spotifyPlaylistInfo, spotifyPlaylistTracks] = await spotify().playlist2Deezer(info.id);
|
|
131
111
|
tracks = spotifyPlaylistTracks;
|
|
132
112
|
linkinfo = spotifyPlaylistInfo;
|
|
133
113
|
linktype = 'playlist';
|
|
134
114
|
break;
|
|
135
115
|
case 'spotify-artist':
|
|
136
|
-
tracks = await spotify.artist2Deezer(info.id);
|
|
116
|
+
tracks = await spotify().artist2Deezer(info.id);
|
|
137
117
|
linktype = 'artist';
|
|
138
118
|
break;
|
|
139
119
|
case 'tidal-track':
|
|
140
|
-
tracks.push(await tidal.track2deezer(info.id));
|
|
120
|
+
tracks.push(await tidal().track2deezer(info.id));
|
|
141
121
|
break;
|
|
142
122
|
case 'tidal-album':
|
|
143
|
-
const [tidalAlbumInfo, tidalAlbumTracks] = await tidal.album2deezer(info.id);
|
|
123
|
+
const [tidalAlbumInfo, tidalAlbumTracks] = await tidal().album2deezer(info.id);
|
|
144
124
|
tracks = tidalAlbumTracks;
|
|
145
125
|
linkinfo = tidalAlbumInfo;
|
|
146
126
|
linktype = 'album';
|
|
147
127
|
break;
|
|
148
128
|
case 'tidal-playlist':
|
|
149
|
-
const [tidalPlaylistInfo, tidalPlaylistTracks] = await tidal.playlist2Deezer(info.id);
|
|
129
|
+
const [tidalPlaylistInfo, tidalPlaylistTracks] = await tidal().playlist2Deezer(info.id);
|
|
150
130
|
tracks = tidalPlaylistTracks;
|
|
151
131
|
linkinfo = tidalPlaylistInfo;
|
|
152
132
|
linktype = 'playlist';
|
|
153
133
|
break;
|
|
154
134
|
case 'tidal-artist':
|
|
155
|
-
tracks = await tidal.artist2Deezer(info.id);
|
|
135
|
+
tracks = await tidal().artist2Deezer(info.id);
|
|
156
136
|
linktype = 'artist';
|
|
157
137
|
break;
|
|
158
138
|
case 'youtube-track':
|
|
159
|
-
tracks.push(await youtube.track2deezer(info.id));
|
|
139
|
+
tracks.push(await youtube().track2deezer(info.id));
|
|
160
140
|
break;
|
|
161
141
|
default:
|
|
162
142
|
throw new Error('Unknown type: ' + info.type);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.track2deezer = void 0;
|
|
4
|
-
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
5
|
+
// deferred: ~93 ms to require, and only this scraper needs it
|
|
6
|
+
const parse = (html) => require('node-html-parser').parse(html);
|
|
5
7
|
const api_1 = require("../api");
|
|
6
8
|
const http_1 = require("../lib/http");
|
|
7
9
|
const parseInlineObject = (document, variableName) => {
|
|
@@ -64,7 +66,7 @@ const searchTrackByTitle = async (title) => {
|
|
|
64
66
|
const getTrack = async (id) => {
|
|
65
67
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
66
68
|
const response = await (0, http_1.getText)(`https://www.youtube.com/watch?v=${id}&hl=en`);
|
|
67
|
-
const scripts =
|
|
69
|
+
const scripts = parse(response)
|
|
68
70
|
.querySelectorAll('script')
|
|
69
71
|
.map((script) => script.text)
|
|
70
72
|
.join('\n');
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getLyricsMusixmatch = exports.musixmatchStatus = exports.configureMusixmatch = void 0;
|
|
4
|
-
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
5
|
+
// deferred: ~93 ms to require, and only this scraper needs it
|
|
6
|
+
const parse = (html) => require('node-html-parser').parse(html);
|
|
5
7
|
const http_1 = require("../lib/http");
|
|
6
8
|
const useragents_1 = require("./useragents");
|
|
7
9
|
const baseUrl = 'https://musixmatch.com';
|
|
@@ -50,7 +52,7 @@ const getUrlMusixmatch = async (query) => {
|
|
|
50
52
|
referer: 'https://l.facebook.com/',
|
|
51
53
|
},
|
|
52
54
|
});
|
|
53
|
-
const childNode = (_a =
|
|
55
|
+
const childNode = (_a = parse(data).querySelector('h2')) === null || _a === void 0 ? void 0 : _a.childNodes.at(0);
|
|
54
56
|
const url = childNode === null || childNode === void 0 ? void 0 : childNode.attributes.href.replace('/add', '');
|
|
55
57
|
if (url && url.includes('/lyrics/')) {
|
|
56
58
|
return url.startsWith('/lyrics/') ? baseUrl + url : url;
|
package/package.json
CHANGED