gerdur-core 1.0.4 → 2.1.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 +82 -0
- package/README.md +51 -9
- package/dist/api/api.js +4 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/search.d.ts +49 -0
- package/dist/api/search.js +110 -0
- package/dist/lib/metaflac-js.d.ts +1 -1
- package/dist/lib/metaflac-js.js +3 -1
- package/dist/metadata-writer/abumCover.d.ts +9 -5
- package/dist/metadata-writer/abumCover.js +28 -14
- package/dist/metadata-writer/contributors.d.ts +31 -0
- package/dist/metadata-writer/contributors.js +86 -0
- package/dist/metadata-writer/flacmetata.d.ts +11 -2
- package/dist/metadata-writer/flacmetata.js +89 -64
- package/dist/metadata-writer/id3.d.ts +8 -2
- package/dist/metadata-writer/id3.js +110 -103
- package/dist/metadata-writer/index.d.ts +60 -6
- package/dist/metadata-writer/index.js +96 -25
- package/dist/metadata-writer/lrc.d.ts +16 -0
- package/dist/metadata-writer/lrc.js +43 -0
- package/dist/metadata-writer/model.d.ts +75 -0
- package/dist/metadata-writer/model.js +103 -0
- package/dist/metadata-writer/rich-album.d.ts +39 -0
- package/dist/metadata-writer/rich-album.js +53 -0
- package/dist/types/album.d.ts +10 -2
- package/dist/types/search.d.ts +157 -0
- package/dist/types/tracks.d.ts +13 -12
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toLrc = void 0;
|
|
4
|
+
const stamp = (ms) => {
|
|
5
|
+
const cs = Math.round(ms / 10);
|
|
6
|
+
const m = Math.floor(cs / 6000);
|
|
7
|
+
const s = Math.floor((cs % 6000) / 100);
|
|
8
|
+
const c = cs % 100;
|
|
9
|
+
return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}.${String(c).padStart(2, '0')}`;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Render Deezer's `LYRICS_SYNC_JSON` as a standard LRC document.
|
|
13
|
+
*
|
|
14
|
+
* Deezer already gives each line an `lrc_timestamp` like `[00:52.64]`; we trust
|
|
15
|
+
* `milliseconds` and re-stamp so the output is well-formed even when a line has
|
|
16
|
+
* an empty timestamp (section breaks come through as `{line: ""}`).
|
|
17
|
+
*/
|
|
18
|
+
const toLrc = (sync, meta = {}) => {
|
|
19
|
+
if (!sync || !sync.length) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const head = [];
|
|
23
|
+
if (meta.artist)
|
|
24
|
+
head.push(`[ar:${meta.artist}]`);
|
|
25
|
+
if (meta.title)
|
|
26
|
+
head.push(`[ti:${meta.title}]`);
|
|
27
|
+
if (meta.album)
|
|
28
|
+
head.push(`[al:${meta.album}]`);
|
|
29
|
+
if (meta.writers)
|
|
30
|
+
head.push(`[au:${meta.writers}]`);
|
|
31
|
+
if (meta.length)
|
|
32
|
+
head.push(`[length:${stamp(meta.length * 1000)}]`);
|
|
33
|
+
head.push('[re:gerdur]');
|
|
34
|
+
const lines = sync
|
|
35
|
+
.filter((l) => l.line && l.line.trim().length)
|
|
36
|
+
.map((l) => {
|
|
37
|
+
const ms = Number(l.milliseconds);
|
|
38
|
+
const ts = l.milliseconds && Number.isFinite(ms) ? `[${stamp(ms)}]` : l.lrc_timestamp || '';
|
|
39
|
+
return `${ts}${l.line}`;
|
|
40
|
+
});
|
|
41
|
+
return [...head, ...lines].join('\n') + '\n';
|
|
42
|
+
};
|
|
43
|
+
exports.toLrc = toLrc;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import type { RichAlbum } from './rich-album';
|
|
3
|
+
import type { lyricsType, trackType, trackTypePublicApi } from '../types';
|
|
4
|
+
export interface TagModelInput {
|
|
5
|
+
track: trackType;
|
|
6
|
+
album?: RichAlbum;
|
|
7
|
+
/** public `/track/` payload — the only source of BPM */
|
|
8
|
+
publicTrack?: trackTypePublicApi | null;
|
|
9
|
+
lyrics?: lyricsType | null;
|
|
10
|
+
cover?: Buffer | null;
|
|
11
|
+
artistImage?: Buffer | null;
|
|
12
|
+
coverSize: number;
|
|
13
|
+
/** write `DEEZER_*_ID`, `URL_REWRITING` slug, provider/label ids */
|
|
14
|
+
deezerIds: boolean;
|
|
15
|
+
/** write popularity rank */
|
|
16
|
+
includeRank: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface Person {
|
|
19
|
+
role: string;
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
export interface TrackTagModel {
|
|
23
|
+
title: string;
|
|
24
|
+
subtitle?: string;
|
|
25
|
+
album: string;
|
|
26
|
+
artists: string[];
|
|
27
|
+
mainArtists: string[];
|
|
28
|
+
featuredArtists: string[];
|
|
29
|
+
albumArtist: string;
|
|
30
|
+
composers: string[];
|
|
31
|
+
lyricists: string[];
|
|
32
|
+
producers: string[];
|
|
33
|
+
engineers: Person[];
|
|
34
|
+
mixers: string[];
|
|
35
|
+
performers: Person[];
|
|
36
|
+
publishers: string[];
|
|
37
|
+
trackNumber: number;
|
|
38
|
+
trackTotal?: number;
|
|
39
|
+
discNumber: number;
|
|
40
|
+
discTotal?: number;
|
|
41
|
+
isrc?: string;
|
|
42
|
+
barcode?: string;
|
|
43
|
+
durationMs: number;
|
|
44
|
+
bpm?: number;
|
|
45
|
+
genres: string[];
|
|
46
|
+
label?: string;
|
|
47
|
+
releaseType?: string;
|
|
48
|
+
isCompilation: boolean;
|
|
49
|
+
date?: string;
|
|
50
|
+
year?: string;
|
|
51
|
+
originalDate?: string;
|
|
52
|
+
originalYear?: string;
|
|
53
|
+
copyright?: string;
|
|
54
|
+
producerLine?: string;
|
|
55
|
+
replayGainTrackGain?: string;
|
|
56
|
+
explicit: 'explicit' | 'clean' | 'unknown';
|
|
57
|
+
itunesAdvisory: number;
|
|
58
|
+
lyrics?: string;
|
|
59
|
+
lyricsSynced?: string;
|
|
60
|
+
lyricsWriters?: string;
|
|
61
|
+
lyricsCopyright?: string;
|
|
62
|
+
ids: {
|
|
63
|
+
deezerTrack?: string;
|
|
64
|
+
deezerAlbum?: string;
|
|
65
|
+
deezerArtist?: string;
|
|
66
|
+
slug?: string;
|
|
67
|
+
labelId?: string;
|
|
68
|
+
providerId?: string;
|
|
69
|
+
};
|
|
70
|
+
rank?: number;
|
|
71
|
+
cover?: Buffer | null;
|
|
72
|
+
coverSize: number;
|
|
73
|
+
artistImage?: Buffer | null;
|
|
74
|
+
}
|
|
75
|
+
export declare const buildTagModel: (input: TagModelInput) => TrackTagModel;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildTagModel = void 0;
|
|
4
|
+
const contributors_1 = require("./contributors");
|
|
5
|
+
const lrc_1 = require("./lrc");
|
|
6
|
+
const yearOf = (d) => (d && /^\d{4}/.test(d) ? d.slice(0, 4) : undefined);
|
|
7
|
+
const explicitFrom = (status) => {
|
|
8
|
+
// Deezer EXPLICIT_LYRICS_STATUS: 0 none · 1 explicit · 2 unknown · 3 edited/clean
|
|
9
|
+
// · 4 partially-explicit · 6 no-advice · 7 partially-no-advice
|
|
10
|
+
if (status === 1 || status === 4)
|
|
11
|
+
return { explicit: 'explicit', itunesAdvisory: 1 };
|
|
12
|
+
if (status === 3)
|
|
13
|
+
return { explicit: 'clean', itunesAdvisory: 2 };
|
|
14
|
+
return { explicit: 'unknown', itunesAdvisory: 0 };
|
|
15
|
+
};
|
|
16
|
+
const buildTagModel = (input) => {
|
|
17
|
+
var _a, _b, _c, _d, _e;
|
|
18
|
+
const { track, album, publicTrack, lyrics, coverSize } = input;
|
|
19
|
+
const c = (0, contributors_1.normalizeContributors)(track.SNG_CONTRIBUTORS);
|
|
20
|
+
// main artists: contributors first, else the ARTISTS[] list, else ART_NAME
|
|
21
|
+
const artistsAll = (track.ARTISTS || []).map((a) => a.ART_NAME).filter(Boolean);
|
|
22
|
+
const mainArtists = c.mainArtists.length ? c.mainArtists : artistsAll.length ? artistsAll : [track.ART_NAME];
|
|
23
|
+
const featuredArtists = c.featuring.length ? c.featuring : artistsAll.filter((n) => !mainArtists.includes(n));
|
|
24
|
+
const artists = [...new Set([...mainArtists, ...featuredArtists])];
|
|
25
|
+
const albMeta = album || { genres: [], isCompilation: false, isLive: false };
|
|
26
|
+
const isCompilation = albMeta.isCompilation || /^various/i.test(track.ART_NAME);
|
|
27
|
+
const date = albMeta.releaseDate ||
|
|
28
|
+
((publicTrack === null || publicTrack === void 0 ? void 0 : publicTrack.release_date) && /^\d{4}-\d{2}-\d{2}$/.test(publicTrack.release_date)
|
|
29
|
+
? publicTrack.release_date
|
|
30
|
+
: undefined);
|
|
31
|
+
const originalDate = albMeta.originalDate && albMeta.originalDate !== date ? albMeta.originalDate : undefined;
|
|
32
|
+
const gain = parseFloat((_c = (_a = track.GAIN) !== null && _a !== void 0 ? _a : (_b = publicTrack === null || publicTrack === void 0 ? void 0 : publicTrack.gain) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : '');
|
|
33
|
+
const bpmRaw = (_d = publicTrack === null || publicTrack === void 0 ? void 0 : publicTrack.bpm) !== null && _d !== void 0 ? _d : 0;
|
|
34
|
+
const explicit = explicitFrom((_e = track.EXPLICIT_TRACK_CONTENT) === null || _e === void 0 ? void 0 : _e.EXPLICIT_LYRICS_STATUS);
|
|
35
|
+
let releaseType = albMeta.recordType;
|
|
36
|
+
if (albMeta.isLive)
|
|
37
|
+
releaseType = 'live';
|
|
38
|
+
else if (isCompilation)
|
|
39
|
+
releaseType = 'compilation';
|
|
40
|
+
const version = (track.VERSION || (publicTrack === null || publicTrack === void 0 ? void 0 : publicTrack.title_version) || '').trim();
|
|
41
|
+
return {
|
|
42
|
+
title: track.SNG_TITLE,
|
|
43
|
+
subtitle: version || undefined,
|
|
44
|
+
album: track.ALB_TITLE || albMeta.title,
|
|
45
|
+
artists,
|
|
46
|
+
mainArtists,
|
|
47
|
+
featuredArtists,
|
|
48
|
+
albumArtist: albMeta.albumArtist || track.ART_NAME,
|
|
49
|
+
composers: c.composers,
|
|
50
|
+
lyricists: c.lyricists,
|
|
51
|
+
producers: c.producers,
|
|
52
|
+
engineers: c.engineers,
|
|
53
|
+
mixers: c.mixers,
|
|
54
|
+
performers: c.performers,
|
|
55
|
+
publishers: c.publishers,
|
|
56
|
+
trackNumber: Number(track.TRACK_NUMBER) || 0,
|
|
57
|
+
trackTotal: albMeta.trackTotal,
|
|
58
|
+
discNumber: Number(track.DISK_NUMBER) || 1,
|
|
59
|
+
discTotal: albMeta.discTotal,
|
|
60
|
+
isrc: track.ISRC || (publicTrack === null || publicTrack === void 0 ? void 0 : publicTrack.isrc) || undefined,
|
|
61
|
+
barcode: albMeta.upc,
|
|
62
|
+
durationMs: (Number(track.DURATION) || 0) * 1000,
|
|
63
|
+
bpm: bpmRaw > 0 ? bpmRaw : undefined,
|
|
64
|
+
genres: albMeta.genres || [],
|
|
65
|
+
label: albMeta.label,
|
|
66
|
+
releaseType,
|
|
67
|
+
isCompilation,
|
|
68
|
+
date,
|
|
69
|
+
year: yearOf(date),
|
|
70
|
+
originalDate,
|
|
71
|
+
originalYear: yearOf(originalDate),
|
|
72
|
+
copyright: albMeta.copyright,
|
|
73
|
+
producerLine: albMeta.producerLine,
|
|
74
|
+
replayGainTrackGain: Number.isFinite(gain) ? `${gain.toFixed(2)} dB` : undefined,
|
|
75
|
+
explicit: explicit.explicit,
|
|
76
|
+
itunesAdvisory: explicit.itunesAdvisory,
|
|
77
|
+
lyrics: (lyrics === null || lyrics === void 0 ? void 0 : lyrics.LYRICS_TEXT) || undefined,
|
|
78
|
+
lyricsSynced: (0, lrc_1.toLrc)(lyrics === null || lyrics === void 0 ? void 0 : lyrics.LYRICS_SYNC_JSON, {
|
|
79
|
+
title: track.SNG_TITLE,
|
|
80
|
+
artist: mainArtists.join(', '),
|
|
81
|
+
album: track.ALB_TITLE,
|
|
82
|
+
writers: lyrics === null || lyrics === void 0 ? void 0 : lyrics.LYRICS_WRITERS,
|
|
83
|
+
length: Number(track.DURATION) || undefined,
|
|
84
|
+
}) || undefined,
|
|
85
|
+
lyricsWriters: (lyrics === null || lyrics === void 0 ? void 0 : lyrics.LYRICS_WRITERS) || undefined,
|
|
86
|
+
lyricsCopyright: (lyrics === null || lyrics === void 0 ? void 0 : lyrics.LYRICS_COPYRIGHTS) || undefined,
|
|
87
|
+
ids: input.deezerIds
|
|
88
|
+
? {
|
|
89
|
+
deezerTrack: track.SNG_ID,
|
|
90
|
+
deezerAlbum: track.ALB_ID,
|
|
91
|
+
deezerArtist: track.ART_ID,
|
|
92
|
+
slug: track.URL_REWRITING || undefined,
|
|
93
|
+
labelId: albMeta.labelId,
|
|
94
|
+
providerId: track.PROVIDER_ID || undefined,
|
|
95
|
+
}
|
|
96
|
+
: {},
|
|
97
|
+
rank: input.includeRank && track.RANK ? Number(track.RANK) || undefined : undefined,
|
|
98
|
+
cover: input.cover,
|
|
99
|
+
coverSize,
|
|
100
|
+
artistImage: input.artistImage,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
exports.buildTagModel = buildTagModel;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { albumType, albumTypePublicApi } from '../types';
|
|
2
|
+
export interface RichAlbum {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
albumArtist: string;
|
|
6
|
+
/** © line — from gw `COPYRIGHT`, falls back to `PRODUCER_LINE` */
|
|
7
|
+
copyright?: string;
|
|
8
|
+
/** ℗ line — only set when it differs from `copyright` */
|
|
9
|
+
producerLine?: string;
|
|
10
|
+
/** best available release date, YYYY-MM-DD */
|
|
11
|
+
releaseDate?: string;
|
|
12
|
+
/** ORIGINAL_RELEASE_DATE — the true first release; only when it differs from releaseDate */
|
|
13
|
+
originalDate?: string;
|
|
14
|
+
upc?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
labelId?: string;
|
|
17
|
+
genres: string[];
|
|
18
|
+
/** album | single | ep | compile | live … */
|
|
19
|
+
recordType?: string;
|
|
20
|
+
isCompilation: boolean;
|
|
21
|
+
isLive: boolean;
|
|
22
|
+
trackTotal?: number;
|
|
23
|
+
discTotal?: number;
|
|
24
|
+
fans?: number;
|
|
25
|
+
/** the two raw payloads, for callers that want more */
|
|
26
|
+
gw?: albumType;
|
|
27
|
+
publicApi?: albumTypePublicApi;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* One merged album view from the two endpoints that actually carry the good
|
|
31
|
+
* fields: `album.getData` (gw — ©/℗, original release date, disc total,
|
|
32
|
+
* subtypes) and the public `/album/` (label, genres, record type). Both calls
|
|
33
|
+
* are memoised and in-flight-coalesced by the api layer, so calling this once
|
|
34
|
+
* per track of an album costs a single network round-trip for the whole album.
|
|
35
|
+
*
|
|
36
|
+
* The public endpoint 404s for some small-label albums; that path is optional
|
|
37
|
+
* and the gw data still comes through.
|
|
38
|
+
*/
|
|
39
|
+
export declare const getRichAlbum: (albId: string) => Promise<RichAlbum>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRichAlbum = void 0;
|
|
4
|
+
const api_1 = require("../api");
|
|
5
|
+
const pickDate = (s) => s && /^\d{4}-\d{2}-\d{2}$/.test(s) && !/^0000/.test(s) ? s : undefined;
|
|
6
|
+
/**
|
|
7
|
+
* One merged album view from the two endpoints that actually carry the good
|
|
8
|
+
* fields: `album.getData` (gw — ©/℗, original release date, disc total,
|
|
9
|
+
* subtypes) and the public `/album/` (label, genres, record type). Both calls
|
|
10
|
+
* are memoised and in-flight-coalesced by the api layer, so calling this once
|
|
11
|
+
* per track of an album costs a single network round-trip for the whole album.
|
|
12
|
+
*
|
|
13
|
+
* The public endpoint 404s for some small-label albums; that path is optional
|
|
14
|
+
* and the gw data still comes through.
|
|
15
|
+
*/
|
|
16
|
+
const getRichAlbum = async (albId) => {
|
|
17
|
+
var _a, _b, _c, _d, _e;
|
|
18
|
+
const [gwResult, publicResult] = await Promise.allSettled([(0, api_1.getAlbumInfo)(albId), (0, api_1.getAlbumInfoPublicApi)(albId)]);
|
|
19
|
+
const gw = gwResult.status === 'fulfilled' ? gwResult.value : undefined;
|
|
20
|
+
const pub = publicResult.status === 'fulfilled' ? publicResult.value : undefined;
|
|
21
|
+
const copyright = ((gw === null || gw === void 0 ? void 0 : gw.COPYRIGHT) || (gw === null || gw === void 0 ? void 0 : gw.PRODUCER_LINE) || '').trim() || undefined;
|
|
22
|
+
const producerLineRaw = ((gw === null || gw === void 0 ? void 0 : gw.PRODUCER_LINE) || '').trim() || undefined;
|
|
23
|
+
const producerLine = producerLineRaw && producerLineRaw !== copyright ? producerLineRaw : undefined;
|
|
24
|
+
const releaseDate = pickDate(gw === null || gw === void 0 ? void 0 : gw.DIGITAL_RELEASE_DATE) ||
|
|
25
|
+
pickDate(pub === null || pub === void 0 ? void 0 : pub.release_date) ||
|
|
26
|
+
pickDate(gw === null || gw === void 0 ? void 0 : gw.PHYSICAL_RELEASE_DATE) ||
|
|
27
|
+
pickDate(gw === null || gw === void 0 ? void 0 : gw.ORIGINAL_RELEASE_DATE);
|
|
28
|
+
const originalRaw = pickDate(gw === null || gw === void 0 ? void 0 : gw.ORIGINAL_RELEASE_DATE);
|
|
29
|
+
const originalDate = originalRaw && originalRaw !== releaseDate ? originalRaw : undefined;
|
|
30
|
+
const recordType = (pub === null || pub === void 0 ? void 0 : pub.record_type) || undefined;
|
|
31
|
+
return {
|
|
32
|
+
id: albId,
|
|
33
|
+
title: (gw === null || gw === void 0 ? void 0 : gw.ALB_TITLE) || (pub === null || pub === void 0 ? void 0 : pub.title) || '',
|
|
34
|
+
albumArtist: (gw === null || gw === void 0 ? void 0 : gw.ART_NAME) || ((_a = pub === null || pub === void 0 ? void 0 : pub.artist) === null || _a === void 0 ? void 0 : _a.name) || '',
|
|
35
|
+
copyright,
|
|
36
|
+
producerLine,
|
|
37
|
+
releaseDate,
|
|
38
|
+
originalDate,
|
|
39
|
+
upc: (gw === null || gw === void 0 ? void 0 : gw.UPC) || (pub === null || pub === void 0 ? void 0 : pub.upc) || undefined,
|
|
40
|
+
label: (pub === null || pub === void 0 ? void 0 : pub.label) || undefined,
|
|
41
|
+
labelId: (gw === null || gw === void 0 ? void 0 : gw.LABEL_ID) || undefined,
|
|
42
|
+
genres: (((_b = pub === null || pub === void 0 ? void 0 : pub.genres) === null || _b === void 0 ? void 0 : _b.data) || []).map((g) => g.name).filter(Boolean),
|
|
43
|
+
recordType,
|
|
44
|
+
isCompilation: Boolean((_c = gw === null || gw === void 0 ? void 0 : gw.SUBTYPES) === null || _c === void 0 ? void 0 : _c.isCompilation) || recordType === 'compile',
|
|
45
|
+
isLive: Boolean((_d = gw === null || gw === void 0 ? void 0 : gw.SUBTYPES) === null || _d === void 0 ? void 0 : _d.isLive),
|
|
46
|
+
trackTotal: (gw === null || gw === void 0 ? void 0 : gw.NUMBER_TRACK) ? Number(gw.NUMBER_TRACK) : pub === null || pub === void 0 ? void 0 : pub.nb_tracks,
|
|
47
|
+
discTotal: (gw === null || gw === void 0 ? void 0 : gw.NUMBER_DISK) ? Number(gw.NUMBER_DISK) : undefined,
|
|
48
|
+
fans: (_e = pub === null || pub === void 0 ? void 0 : pub.fans) !== null && _e !== void 0 ? _e : gw === null || gw === void 0 ? void 0 : gw.NB_FAN,
|
|
49
|
+
gw,
|
|
50
|
+
publicApi: pub,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
exports.getRichAlbum = getRichAlbum;
|
package/dist/types/album.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ export interface albumTypeMinimal {
|
|
|
21
21
|
}
|
|
22
22
|
export interface albumType {
|
|
23
23
|
ALB_CONTRIBUTORS: {
|
|
24
|
-
|
|
25
|
-
};
|
|
24
|
+
[role: string]: string[];
|
|
25
|
+
} | [];
|
|
26
26
|
ALB_ID: string;
|
|
27
27
|
ALB_PICTURE: string;
|
|
28
28
|
EXPLICIT_ALBUM_CONTENT: {
|
|
@@ -34,7 +34,9 @@ export interface albumType {
|
|
|
34
34
|
ART_ID: string;
|
|
35
35
|
ART_NAME: string;
|
|
36
36
|
ARTIST_IS_DUMMY: boolean;
|
|
37
|
+
COPYRIGHT?: string;
|
|
37
38
|
DIGITAL_RELEASE_DATE: string;
|
|
39
|
+
ORIGINAL_RELEASE_DATE?: string;
|
|
38
40
|
EXPLICIT_LYRICS?: string;
|
|
39
41
|
NB_FAN: number;
|
|
40
42
|
NUMBER_DISK: string;
|
|
@@ -45,6 +47,12 @@ export interface albumType {
|
|
|
45
47
|
RANK: string;
|
|
46
48
|
RANK_ART: string;
|
|
47
49
|
STATUS: string;
|
|
50
|
+
SUBTYPES?: {
|
|
51
|
+
isStudio: boolean;
|
|
52
|
+
isLive: boolean;
|
|
53
|
+
isCompilation: boolean;
|
|
54
|
+
isKaraoke: boolean;
|
|
55
|
+
};
|
|
48
56
|
TYPE: string;
|
|
49
57
|
UPC: string;
|
|
50
58
|
__TYPE__: 'album';
|
package/dist/types/search.d.ts
CHANGED
|
@@ -47,6 +47,163 @@ export interface discographyType {
|
|
|
47
47
|
start: number;
|
|
48
48
|
nb: number;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Deezer's advanced search operators. Compose these into one query string with
|
|
52
|
+
* `buildAdvancedQuery()` — e.g. `{artist: 'daft punk', durMin: 200}` becomes
|
|
53
|
+
* `artist:"daft punk" dur_min:200`.
|
|
54
|
+
*
|
|
55
|
+
* Deezer applies these as ranking hints, not hard filters, and honours `track:`
|
|
56
|
+
* only intermittently on their side — this type just mirrors the syntax they
|
|
57
|
+
* document.
|
|
58
|
+
*/
|
|
59
|
+
export interface advancedSearchFilters {
|
|
60
|
+
/** free-text terms, emitted first and bare */
|
|
61
|
+
query?: string;
|
|
62
|
+
artist?: string;
|
|
63
|
+
album?: string;
|
|
64
|
+
track?: string;
|
|
65
|
+
label?: string;
|
|
66
|
+
/** minimum duration, seconds */
|
|
67
|
+
durMin?: number;
|
|
68
|
+
/** maximum duration, seconds */
|
|
69
|
+
durMax?: number;
|
|
70
|
+
/** minimum beats-per-minute */
|
|
71
|
+
bpmMin?: number;
|
|
72
|
+
/** maximum beats-per-minute */
|
|
73
|
+
bpmMax?: number;
|
|
74
|
+
}
|
|
75
|
+
export type searchOrder = 'RANKING' | 'TRACK_ASC' | 'TRACK_DESC' | 'ARTIST_ASC' | 'ARTIST_DESC' | 'ALBUM_ASC' | 'ALBUM_DESC' | 'RATING_ASC' | 'RATING_DESC' | 'DURATION_ASC' | 'DURATION_DESC';
|
|
76
|
+
export type searchEntity = 'track' | 'album' | 'artist' | 'playlist' | 'user' | 'radio' | 'podcast';
|
|
77
|
+
export interface publicApiSearchOptions {
|
|
78
|
+
/** which index to hit — `track` (default), `album`, `artist`, `playlist`, `user`, `radio`, `podcast` */
|
|
79
|
+
type?: searchEntity;
|
|
80
|
+
order?: searchOrder;
|
|
81
|
+
/** send Deezer's `strict=on` — disables the fuzzy fallback */
|
|
82
|
+
strict?: boolean;
|
|
83
|
+
/** page size; Deezer caps this near 100 */
|
|
84
|
+
limit?: number;
|
|
85
|
+
/** offset into the result set */
|
|
86
|
+
index?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface publicApiSearchResponse<T> {
|
|
89
|
+
data: T[];
|
|
90
|
+
total: number;
|
|
91
|
+
/** absolute URL of the next page, when there is one */
|
|
92
|
+
next?: string;
|
|
93
|
+
prev?: string;
|
|
94
|
+
}
|
|
95
|
+
interface searchArtistRef {
|
|
96
|
+
id: number;
|
|
97
|
+
name: string;
|
|
98
|
+
link?: string;
|
|
99
|
+
picture?: string;
|
|
100
|
+
picture_small?: string;
|
|
101
|
+
picture_medium?: string;
|
|
102
|
+
picture_big?: string;
|
|
103
|
+
picture_xl?: string;
|
|
104
|
+
tracklist?: string;
|
|
105
|
+
type: 'artist';
|
|
106
|
+
}
|
|
107
|
+
interface searchAlbumRef {
|
|
108
|
+
id: number;
|
|
109
|
+
title: string;
|
|
110
|
+
cover?: string;
|
|
111
|
+
cover_small?: string;
|
|
112
|
+
cover_medium?: string;
|
|
113
|
+
cover_big?: string;
|
|
114
|
+
cover_xl?: string;
|
|
115
|
+
md5_image?: string;
|
|
116
|
+
tracklist?: string;
|
|
117
|
+
type: 'album';
|
|
118
|
+
}
|
|
119
|
+
export interface searchResultTrack {
|
|
120
|
+
id: number;
|
|
121
|
+
readable: boolean;
|
|
122
|
+
title: string;
|
|
123
|
+
title_short: string;
|
|
124
|
+
title_version?: string;
|
|
125
|
+
link: string;
|
|
126
|
+
duration: number;
|
|
127
|
+
rank: number;
|
|
128
|
+
explicit_lyrics: boolean;
|
|
129
|
+
explicit_content_lyrics: number;
|
|
130
|
+
explicit_content_cover: number;
|
|
131
|
+
preview: string;
|
|
132
|
+
md5_image: string;
|
|
133
|
+
isrc?: string;
|
|
134
|
+
artist: searchArtistRef;
|
|
135
|
+
album: searchAlbumRef;
|
|
136
|
+
type: 'track';
|
|
137
|
+
}
|
|
138
|
+
export interface searchResultAlbum {
|
|
139
|
+
id: number;
|
|
140
|
+
title: string;
|
|
141
|
+
link: string;
|
|
142
|
+
cover: string;
|
|
143
|
+
cover_small?: string;
|
|
144
|
+
cover_medium?: string;
|
|
145
|
+
cover_big?: string;
|
|
146
|
+
cover_xl?: string;
|
|
147
|
+
md5_image: string;
|
|
148
|
+
genre_id: number;
|
|
149
|
+
nb_tracks: number;
|
|
150
|
+
record_type: string;
|
|
151
|
+
explicit_lyrics: boolean;
|
|
152
|
+
artist: searchArtistRef;
|
|
153
|
+
type: 'album';
|
|
154
|
+
}
|
|
155
|
+
export interface searchResultArtist {
|
|
156
|
+
id: number;
|
|
157
|
+
name: string;
|
|
158
|
+
link: string;
|
|
159
|
+
picture: string;
|
|
160
|
+
picture_small?: string;
|
|
161
|
+
picture_medium?: string;
|
|
162
|
+
picture_big?: string;
|
|
163
|
+
picture_xl?: string;
|
|
164
|
+
nb_album: number;
|
|
165
|
+
nb_fan: number;
|
|
166
|
+
radio: boolean;
|
|
167
|
+
tracklist: string;
|
|
168
|
+
type: 'artist';
|
|
169
|
+
}
|
|
170
|
+
export interface searchResultPlaylist {
|
|
171
|
+
id: number;
|
|
172
|
+
title: string;
|
|
173
|
+
public: boolean;
|
|
174
|
+
nb_tracks: number;
|
|
175
|
+
link: string;
|
|
176
|
+
picture: string;
|
|
177
|
+
picture_small?: string;
|
|
178
|
+
picture_medium?: string;
|
|
179
|
+
picture_big?: string;
|
|
180
|
+
picture_xl?: string;
|
|
181
|
+
checksum?: string;
|
|
182
|
+
tracklist: string;
|
|
183
|
+
creation_date?: string;
|
|
184
|
+
user?: {
|
|
185
|
+
id: number;
|
|
186
|
+
name: string;
|
|
187
|
+
tracklist?: string;
|
|
188
|
+
type: 'user';
|
|
189
|
+
};
|
|
190
|
+
type: 'playlist';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* `deezer.suggest` — lightweight autocomplete off the internal gateway. Each
|
|
194
|
+
* per-type array is gw-shaped (uppercase keys), same as `searchMusic`'s.
|
|
195
|
+
*/
|
|
196
|
+
export interface suggestResult {
|
|
197
|
+
QUERY: string;
|
|
198
|
+
TOP_RESULT: unknown[];
|
|
199
|
+
ORDER: string[];
|
|
200
|
+
ALBUM?: albumTypeMinimal[];
|
|
201
|
+
ARTIST?: artistInfoTypeMinimal[];
|
|
202
|
+
TRACK?: trackType[];
|
|
203
|
+
PLAYLIST?: playlistInfoMinimal[];
|
|
204
|
+
SHOW?: unknown[];
|
|
205
|
+
RADIO?: radioType[];
|
|
206
|
+
}
|
|
50
207
|
export interface searchType {
|
|
51
208
|
QUERY: string;
|
|
52
209
|
FUZZINNESS: boolean;
|
package/dist/types/tracks.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ interface mediaType {
|
|
|
3
3
|
TYPE: 'preview';
|
|
4
4
|
HREF: string;
|
|
5
5
|
}
|
|
6
|
-
interface lyricsSync {
|
|
6
|
+
export interface lyricsSync {
|
|
7
7
|
lrc_timestamp: string;
|
|
8
8
|
milliseconds: string;
|
|
9
9
|
duration: string;
|
|
@@ -16,6 +16,17 @@ export interface lyricsType {
|
|
|
16
16
|
LYRICS_COPYRIGHTS?: string;
|
|
17
17
|
LYRICS_WRITERS?: string;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* `SNG_CONTRIBUTORS` as Deezer actually ships it: a bag of role -> names.
|
|
21
|
+
* The role keys are inconsistent across the catalogue — `main_artist` vs
|
|
22
|
+
* `mainartist` vs `artist`, `musicpublisher` vs `music publisher` — and the
|
|
23
|
+
* whole thing is sometimes an empty array. Normalise with
|
|
24
|
+
* `normalizeContributors()` from `metadata-writer/contributors` rather than
|
|
25
|
+
* reading keys directly.
|
|
26
|
+
*/
|
|
27
|
+
export type sngContributors = {
|
|
28
|
+
[role: string]: string[];
|
|
29
|
+
} | [];
|
|
19
30
|
interface songType {
|
|
20
31
|
ALB_ID: string;
|
|
21
32
|
ALB_TITLE: string;
|
|
@@ -40,17 +51,7 @@ interface songType {
|
|
|
40
51
|
SMARTRADIO: string;
|
|
41
52
|
SNG_ID: string;
|
|
42
53
|
SNG_TITLE: string;
|
|
43
|
-
SNG_CONTRIBUTORS?:
|
|
44
|
-
main_artist: string[];
|
|
45
|
-
author?: string[];
|
|
46
|
-
composer?: string[];
|
|
47
|
-
musicpublisher?: string[];
|
|
48
|
-
producer?: string[];
|
|
49
|
-
publisher: string[];
|
|
50
|
-
engineer?: string[];
|
|
51
|
-
writer?: string[];
|
|
52
|
-
mixer?: string[];
|
|
53
|
-
} | [];
|
|
54
|
+
SNG_CONTRIBUTORS?: sngContributors;
|
|
54
55
|
STATUS: number;
|
|
55
56
|
S_MOD: number;
|
|
56
57
|
S_PREMIUM: number;
|