trackparse 0.2.1 → 0.3.1
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 +87 -5
- package/dist/chunk-3TS4DITR.js +2314 -0
- package/dist/index.cjs +73 -6
- package/dist/index.d.cts +4 -86
- package/dist/index.d.ts +4 -86
- package/dist/index.js +3 -2189
- package/dist/tracklist.cjs +3135 -0
- package/dist/tracklist.d.cts +114 -0
- package/dist/tracklist.d.ts +114 -0
- package/dist/tracklist.js +827 -0
- package/dist/types-DqJSn18b.d.cts +87 -0
- package/dist/types-DqJSn18b.d.ts +87 -0
- package/package.json +15 -4
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/data.generated.ts
|
|
4
|
-
var SPEC_VERSION = "0.
|
|
4
|
+
var SPEC_VERSION = "0.3.1";
|
|
5
5
|
var descriptorsData = {
|
|
6
6
|
"descriptors": [
|
|
7
7
|
"radio",
|
|
@@ -475,6 +475,19 @@ function isLetterAt(s, index) {
|
|
|
475
475
|
if (cp === void 0) return false;
|
|
476
476
|
return LETTER.test(String.fromCodePoint(cp));
|
|
477
477
|
}
|
|
478
|
+
function isLetterBefore(s, index) {
|
|
479
|
+
const low = s.charCodeAt(index - 1);
|
|
480
|
+
const high = s.charCodeAt(index - 2);
|
|
481
|
+
const pair = low >= 56320 && low <= 57343 && high >= 55296 && high <= 56319;
|
|
482
|
+
const start = pair ? index - 2 : index - 1;
|
|
483
|
+
return start >= 0 && isLetterAt(s, start);
|
|
484
|
+
}
|
|
485
|
+
var UPPER = /^\p{Lu}$/u;
|
|
486
|
+
function isUpperAt(s, index) {
|
|
487
|
+
const cp = s.codePointAt(index);
|
|
488
|
+
if (cp === void 0) return false;
|
|
489
|
+
return UPPER.test(String.fromCodePoint(cp));
|
|
490
|
+
}
|
|
478
491
|
function asciiLower(s) {
|
|
479
492
|
let out = "";
|
|
480
493
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -1687,7 +1700,7 @@ function prelude(normalized, requested, ctx) {
|
|
|
1687
1700
|
let str = stripPlatformSuffixes(normalized, ctx, junk);
|
|
1688
1701
|
const hadPlatform = junk.length > 0;
|
|
1689
1702
|
let mode;
|
|
1690
|
-
if (requested === "clean" || requested === "youtube" || requested === "filename") {
|
|
1703
|
+
if (requested === "clean" || requested === "youtube" || requested === "filename" || requested === "tracklist") {
|
|
1691
1704
|
mode = requested;
|
|
1692
1705
|
} else if (extensionOf(str, ctx)) {
|
|
1693
1706
|
mode = "filename";
|
|
@@ -1749,7 +1762,7 @@ function splitYoutubePipes(skel, ctx, junk) {
|
|
|
1749
1762
|
return { working: { text: "", base: 0 }, pipeTitle: null };
|
|
1750
1763
|
}
|
|
1751
1764
|
|
|
1752
|
-
// src/
|
|
1765
|
+
// src/digits.ts
|
|
1753
1766
|
function digitRun(s, from) {
|
|
1754
1767
|
let i = from;
|
|
1755
1768
|
while (isDigit(s[i])) i++;
|
|
@@ -1760,6 +1773,8 @@ function twoDigitsUpTo59(s, at) {
|
|
|
1760
1773
|
const n = Number(s.slice(at, at + 2));
|
|
1761
1774
|
return n <= 59 ? n : null;
|
|
1762
1775
|
}
|
|
1776
|
+
|
|
1777
|
+
// src/prefix.ts
|
|
1763
1778
|
function matchTime(s, at) {
|
|
1764
1779
|
const lead = digitRun(s, at);
|
|
1765
1780
|
if (lead < 1 || lead > 2 || s[at + lead] !== ":") return null;
|
|
@@ -1877,6 +1892,8 @@ function splitAt(s, cutStart, cutEnd) {
|
|
|
1877
1892
|
function findSeparator(input, ctx) {
|
|
1878
1893
|
const { skel, mode } = input;
|
|
1879
1894
|
const segments = splitAtSpacedDashes(skel, true);
|
|
1895
|
+
if (mode === "tracklist")
|
|
1896
|
+
return tracklistSeparator(skel, segments, input.listContext === true, ctx);
|
|
1880
1897
|
const { remaining, peeled } = peelSuffixes(segments, false, ctx);
|
|
1881
1898
|
if (remaining.length >= 2) {
|
|
1882
1899
|
const first = remaining[0];
|
|
@@ -1893,6 +1910,46 @@ function findSeparator(input, ctx) {
|
|
|
1893
1910
|
if (rest.text.length > 0 && artist === null) warn(ctx, "noSeparator");
|
|
1894
1911
|
return { artist, title: rest, split: false, peeled };
|
|
1895
1912
|
}
|
|
1913
|
+
function tracklistSeparator(skel, segments, listContext, ctx) {
|
|
1914
|
+
if (segments.length >= 2) {
|
|
1915
|
+
const first = segments[0];
|
|
1916
|
+
const second = segments[1];
|
|
1917
|
+
const lastSeg = segments[segments.length - 1];
|
|
1918
|
+
const artist = first.skel.text.length > 0 ? first.skel : null;
|
|
1919
|
+
const title = sliceSkel(skel, second.start, lastSeg.end);
|
|
1920
|
+
return { artist, title, split: artist !== null, peeled: [] };
|
|
1921
|
+
}
|
|
1922
|
+
const rest = segments[0].skel;
|
|
1923
|
+
const asym = asymmetricDash(rest.text);
|
|
1924
|
+
if (asym >= 0) {
|
|
1925
|
+
warn(ctx, "asymmetricDashSplit");
|
|
1926
|
+
const parts = splitAt(rest, asym, asym + 1);
|
|
1927
|
+
return { ...parts, split: parts.artist !== null, peeled: [] };
|
|
1928
|
+
}
|
|
1929
|
+
const underscore = rest.text.indexOf("_-_");
|
|
1930
|
+
if (underscore >= 0) {
|
|
1931
|
+
warn(ctx, "unspacedDashSplit");
|
|
1932
|
+
const parts = splitAt(rest, underscore, underscore + 3);
|
|
1933
|
+
return { ...parts, split: parts.artist !== null, peeled: [] };
|
|
1934
|
+
}
|
|
1935
|
+
if (listContext) {
|
|
1936
|
+
const tight = tightUpperDash(rest.text);
|
|
1937
|
+
if (tight >= 0) {
|
|
1938
|
+
warn(ctx, "unspacedDashSplit");
|
|
1939
|
+
const parts = splitAt(rest, tight, tight + 1);
|
|
1940
|
+
return { ...parts, split: parts.artist !== null, peeled: [] };
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (rest.text.length > 0) warn(ctx, "noSeparator");
|
|
1944
|
+
return { artist: null, title: rest, split: false, peeled: [] };
|
|
1945
|
+
}
|
|
1946
|
+
function tightUpperDash(t) {
|
|
1947
|
+
for (let i = t.length - 2; i >= 1; i--) {
|
|
1948
|
+
if (t[i] !== "-") continue;
|
|
1949
|
+
if (isLetterBefore(t, i) && isUpperAt(t, i + 1)) return i;
|
|
1950
|
+
}
|
|
1951
|
+
return -1;
|
|
1952
|
+
}
|
|
1896
1953
|
function splitWithoutSpacedDash(s, mode, ctx) {
|
|
1897
1954
|
const asym = asymmetricDash(s.text);
|
|
1898
1955
|
if (asym >= 0) {
|
|
@@ -2167,17 +2224,27 @@ function uploaderSkel(uploader) {
|
|
|
2167
2224
|
if (u.length === 0) return null;
|
|
2168
2225
|
return scan(u, UPLOADER_BASE).skel;
|
|
2169
2226
|
}
|
|
2170
|
-
function
|
|
2227
|
+
function stage(input, options, t) {
|
|
2171
2228
|
const ctx = makeCtx(t, options);
|
|
2172
2229
|
const norm = normalize(input);
|
|
2173
2230
|
const requested = options.mode ?? "auto";
|
|
2174
2231
|
const pre = prelude(norm, requested, ctx);
|
|
2175
2232
|
const track = emptyTrack(typeof input === "string" ? input : "", pre.mode);
|
|
2176
|
-
if (norm.length === 0)
|
|
2233
|
+
if (norm.length === 0) {
|
|
2234
|
+
return { ctx, options, pre, track, working: null };
|
|
2235
|
+
}
|
|
2177
2236
|
const prefix = stripPrefixes(pre.working.text, pre.working.base, pre.mode, ctx);
|
|
2178
2237
|
track.timestamp = prefix.timestamp;
|
|
2179
2238
|
track.position = prefix.position;
|
|
2180
2239
|
const working = skelOf({ text: prefix.rest, base: prefix.base }, ctx);
|
|
2240
|
+
return { ctx, options, pre, track, working };
|
|
2241
|
+
}
|
|
2242
|
+
function run(input, options, t, listContext = false) {
|
|
2243
|
+
return finish(stage(input, options, t), listContext);
|
|
2244
|
+
}
|
|
2245
|
+
function finish(st, listContext) {
|
|
2246
|
+
const { ctx, options, pre, track, working } = st;
|
|
2247
|
+
if (working === null) return track;
|
|
2181
2248
|
const out = emptyExtracted();
|
|
2182
2249
|
let artistSkel;
|
|
2183
2250
|
let titleSkel;
|
|
@@ -2188,7 +2255,7 @@ function run(input, options, t) {
|
|
|
2188
2255
|
relaxed = artistSkel !== null;
|
|
2189
2256
|
} else {
|
|
2190
2257
|
const sep = findSeparator(
|
|
2191
|
-
{ skel: working, mode: pre.mode, uploader: uploaderSkel(options.uploader) },
|
|
2258
|
+
{ skel: working, mode: pre.mode, uploader: uploaderSkel(options.uploader), listContext },
|
|
2192
2259
|
ctx
|
|
2193
2260
|
);
|
|
2194
2261
|
addPeeled(out, sep.peeled);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,89 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { P as ParsedTrack, A as Artist, F as FormatOptions, a as ParseOptions } from './types-DqJSn18b.cjs';
|
|
2
|
+
export { b as ArtistRole, c as ArtistSource, d as Flags, J as Junk, e as JunkKind, K as KeywordOptions, M as Mode, f as ModeOption, g as Position, T as Timestamp, V as Version, h as VersionDelimiter, i as VersionType, W as Warning } from './types-DqJSn18b.cjs';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
type Mode = "clean" | "youtube" | "filename";
|
|
5
|
-
type ModeOption = Mode | "auto";
|
|
6
|
-
type ArtistRole = "primary" | "featured" | "remixer" | "producer";
|
|
7
|
-
type ArtistSource = "artist" | "title" | "version";
|
|
8
|
-
interface Artist {
|
|
9
|
-
name: string;
|
|
10
|
-
role: ArtistRole;
|
|
11
|
-
/** Raw joiner / marker text that preceded the name in its credit list; `null` for the first name. */
|
|
12
|
-
joiner: string | null;
|
|
13
|
-
source: ArtistSource;
|
|
14
|
-
}
|
|
15
|
-
type VersionType = "remix" | "bootleg" | "vip" | "edit" | "flip" | "refix" | "rework" | "mashup" | "blend" | "dub" | "mix" | "extended" | "radio" | "club" | "original" | "instrumental" | "acapella" | "live" | "acoustic" | "remaster" | "demo" | "reprise" | "cover" | "version" | "spedUp" | "slowed" | "nightcore";
|
|
16
|
-
type VersionDelimiter = "(" | "[" | "{" | "-";
|
|
17
|
-
interface Version {
|
|
18
|
-
type: VersionType;
|
|
19
|
-
raw: string;
|
|
20
|
-
artists: Artist[];
|
|
21
|
-
modifiers: string[];
|
|
22
|
-
descriptor: string | null;
|
|
23
|
-
year: number | null;
|
|
24
|
-
unknownArtist: boolean;
|
|
25
|
-
delimiter: VersionDelimiter;
|
|
26
|
-
}
|
|
27
|
-
type JunkKind = "video" | "audio" | "lyrics" | "quality" | "promo" | "platform" | "label" | "genre" | "other";
|
|
28
|
-
interface Junk {
|
|
29
|
-
raw: string;
|
|
30
|
-
kind: JunkKind;
|
|
31
|
-
}
|
|
32
|
-
type Warning = "noSeparator" | "ambiguousSeparator" | "unspacedDashSplit" | "asymmetricDashSplit" | "bySplit" | "quotedTitleSplit" | "ambiguousMixCredit" | "unbalancedBrackets";
|
|
33
|
-
interface Flags {
|
|
34
|
-
explicit: boolean;
|
|
35
|
-
clean: boolean;
|
|
36
|
-
unknownArtist: boolean;
|
|
37
|
-
unknownTitle: boolean;
|
|
38
|
-
}
|
|
39
|
-
interface Position {
|
|
40
|
-
raw: string;
|
|
41
|
-
number: number;
|
|
42
|
-
}
|
|
43
|
-
interface Timestamp {
|
|
44
|
-
raw: string;
|
|
45
|
-
seconds: number;
|
|
46
|
-
}
|
|
47
|
-
interface ParsedTrack {
|
|
48
|
-
input: string;
|
|
49
|
-
mode: Mode;
|
|
50
|
-
position: Position | null;
|
|
51
|
-
timestamp: Timestamp | null;
|
|
52
|
-
artists: Artist[];
|
|
53
|
-
title: string;
|
|
54
|
-
fullTitle: string;
|
|
55
|
-
versions: Version[];
|
|
56
|
-
year: number | null;
|
|
57
|
-
flags: Flags;
|
|
58
|
-
junk: Junk[];
|
|
59
|
-
warnings: Warning[];
|
|
60
|
-
}
|
|
61
|
-
interface KeywordOptions {
|
|
62
|
-
/** Extra version heads: key → version type. */
|
|
63
|
-
versionHeads?: Record<string, VersionType>;
|
|
64
|
-
descriptors?: string[];
|
|
65
|
-
genres?: string[];
|
|
66
|
-
/** Extra junk phrases: phrase → kind. */
|
|
67
|
-
junk?: Record<string, JunkKind>;
|
|
68
|
-
featMarkers?: string[];
|
|
69
|
-
}
|
|
70
|
-
interface ParseOptions {
|
|
71
|
-
mode?: ModeOption;
|
|
72
|
-
/** youtube mode: channel name used as the artist when no separator is found (R6.8). */
|
|
73
|
-
uploader?: string;
|
|
74
|
-
/** Names that must never be split (R7.3a). */
|
|
75
|
-
knownArtists?: string[];
|
|
76
|
-
splitAnd?: "auto" | "always" | "never";
|
|
77
|
-
keywords?: KeywordOptions;
|
|
78
|
-
}
|
|
79
|
-
interface FormatOptions {
|
|
80
|
-
feat?: "source" | "artist" | "title" | "omit";
|
|
81
|
-
featMarker?: string;
|
|
82
|
-
joiners?: "original" | "canonical";
|
|
83
|
-
versions?: "all" | "none";
|
|
84
|
-
producers?: boolean;
|
|
85
|
-
position?: boolean;
|
|
86
|
-
}
|
|
4
|
+
declare const SPEC_VERSION = "0.3.1";
|
|
87
5
|
|
|
88
6
|
declare function format(track: ParsedTrack, options?: FormatOptions): string;
|
|
89
7
|
/** All credited names: track artists plus every version's remixers, deduped by R0.4 key. */
|
|
@@ -101,4 +19,4 @@ declare function createParser(base?: ParseOptions): Parser;
|
|
|
101
19
|
declare function parse(input: string, options?: ParseOptions): ParsedTrack;
|
|
102
20
|
declare function parseArtists(input: string, options?: ParseOptions): Artist[];
|
|
103
21
|
|
|
104
|
-
export {
|
|
22
|
+
export { Artist, FormatOptions, ParseOptions, ParsedTrack, type Parser, SPEC_VERSION, allArtists, createParser, format, normalize, parse, parseArtists };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,89 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { P as ParsedTrack, A as Artist, F as FormatOptions, a as ParseOptions } from './types-DqJSn18b.js';
|
|
2
|
+
export { b as ArtistRole, c as ArtistSource, d as Flags, J as Junk, e as JunkKind, K as KeywordOptions, M as Mode, f as ModeOption, g as Position, T as Timestamp, V as Version, h as VersionDelimiter, i as VersionType, W as Warning } from './types-DqJSn18b.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
type Mode = "clean" | "youtube" | "filename";
|
|
5
|
-
type ModeOption = Mode | "auto";
|
|
6
|
-
type ArtistRole = "primary" | "featured" | "remixer" | "producer";
|
|
7
|
-
type ArtistSource = "artist" | "title" | "version";
|
|
8
|
-
interface Artist {
|
|
9
|
-
name: string;
|
|
10
|
-
role: ArtistRole;
|
|
11
|
-
/** Raw joiner / marker text that preceded the name in its credit list; `null` for the first name. */
|
|
12
|
-
joiner: string | null;
|
|
13
|
-
source: ArtistSource;
|
|
14
|
-
}
|
|
15
|
-
type VersionType = "remix" | "bootleg" | "vip" | "edit" | "flip" | "refix" | "rework" | "mashup" | "blend" | "dub" | "mix" | "extended" | "radio" | "club" | "original" | "instrumental" | "acapella" | "live" | "acoustic" | "remaster" | "demo" | "reprise" | "cover" | "version" | "spedUp" | "slowed" | "nightcore";
|
|
16
|
-
type VersionDelimiter = "(" | "[" | "{" | "-";
|
|
17
|
-
interface Version {
|
|
18
|
-
type: VersionType;
|
|
19
|
-
raw: string;
|
|
20
|
-
artists: Artist[];
|
|
21
|
-
modifiers: string[];
|
|
22
|
-
descriptor: string | null;
|
|
23
|
-
year: number | null;
|
|
24
|
-
unknownArtist: boolean;
|
|
25
|
-
delimiter: VersionDelimiter;
|
|
26
|
-
}
|
|
27
|
-
type JunkKind = "video" | "audio" | "lyrics" | "quality" | "promo" | "platform" | "label" | "genre" | "other";
|
|
28
|
-
interface Junk {
|
|
29
|
-
raw: string;
|
|
30
|
-
kind: JunkKind;
|
|
31
|
-
}
|
|
32
|
-
type Warning = "noSeparator" | "ambiguousSeparator" | "unspacedDashSplit" | "asymmetricDashSplit" | "bySplit" | "quotedTitleSplit" | "ambiguousMixCredit" | "unbalancedBrackets";
|
|
33
|
-
interface Flags {
|
|
34
|
-
explicit: boolean;
|
|
35
|
-
clean: boolean;
|
|
36
|
-
unknownArtist: boolean;
|
|
37
|
-
unknownTitle: boolean;
|
|
38
|
-
}
|
|
39
|
-
interface Position {
|
|
40
|
-
raw: string;
|
|
41
|
-
number: number;
|
|
42
|
-
}
|
|
43
|
-
interface Timestamp {
|
|
44
|
-
raw: string;
|
|
45
|
-
seconds: number;
|
|
46
|
-
}
|
|
47
|
-
interface ParsedTrack {
|
|
48
|
-
input: string;
|
|
49
|
-
mode: Mode;
|
|
50
|
-
position: Position | null;
|
|
51
|
-
timestamp: Timestamp | null;
|
|
52
|
-
artists: Artist[];
|
|
53
|
-
title: string;
|
|
54
|
-
fullTitle: string;
|
|
55
|
-
versions: Version[];
|
|
56
|
-
year: number | null;
|
|
57
|
-
flags: Flags;
|
|
58
|
-
junk: Junk[];
|
|
59
|
-
warnings: Warning[];
|
|
60
|
-
}
|
|
61
|
-
interface KeywordOptions {
|
|
62
|
-
/** Extra version heads: key → version type. */
|
|
63
|
-
versionHeads?: Record<string, VersionType>;
|
|
64
|
-
descriptors?: string[];
|
|
65
|
-
genres?: string[];
|
|
66
|
-
/** Extra junk phrases: phrase → kind. */
|
|
67
|
-
junk?: Record<string, JunkKind>;
|
|
68
|
-
featMarkers?: string[];
|
|
69
|
-
}
|
|
70
|
-
interface ParseOptions {
|
|
71
|
-
mode?: ModeOption;
|
|
72
|
-
/** youtube mode: channel name used as the artist when no separator is found (R6.8). */
|
|
73
|
-
uploader?: string;
|
|
74
|
-
/** Names that must never be split (R7.3a). */
|
|
75
|
-
knownArtists?: string[];
|
|
76
|
-
splitAnd?: "auto" | "always" | "never";
|
|
77
|
-
keywords?: KeywordOptions;
|
|
78
|
-
}
|
|
79
|
-
interface FormatOptions {
|
|
80
|
-
feat?: "source" | "artist" | "title" | "omit";
|
|
81
|
-
featMarker?: string;
|
|
82
|
-
joiners?: "original" | "canonical";
|
|
83
|
-
versions?: "all" | "none";
|
|
84
|
-
producers?: boolean;
|
|
85
|
-
position?: boolean;
|
|
86
|
-
}
|
|
4
|
+
declare const SPEC_VERSION = "0.3.1";
|
|
87
5
|
|
|
88
6
|
declare function format(track: ParsedTrack, options?: FormatOptions): string;
|
|
89
7
|
/** All credited names: track artists plus every version's remixers, deduped by R0.4 key. */
|
|
@@ -101,4 +19,4 @@ declare function createParser(base?: ParseOptions): Parser;
|
|
|
101
19
|
declare function parse(input: string, options?: ParseOptions): ParsedTrack;
|
|
102
20
|
declare function parseArtists(input: string, options?: ParseOptions): Artist[];
|
|
103
21
|
|
|
104
|
-
export {
|
|
22
|
+
export { Artist, FormatOptions, ParseOptions, ParsedTrack, type Parser, SPEC_VERSION, allArtists, createParser, format, normalize, parse, parseArtists };
|