trackparse 0.4.0 → 0.5.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 +434 -139
- package/dist/{chunk-OY536C3I.js → chunk-F7PBCPFT.js} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/tracklist.cjs +1 -1
- package/dist/tracklist.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
# trackparse
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
(
|
|
3
|
+
[](https://www.npmjs.com/package/trackparse)
|
|
4
|
+
[](https://www.npmjs.com/package/trackparse)
|
|
5
|
+
[](https://github.com/4matic/trackparse/actions/workflows/js.yml)
|
|
6
|
+
[](https://github.com/4matic/trackparse/blob/main/LICENSE)
|
|
5
7
|
|
|
6
|
-
[
|
|
7
|
-
[](https://github.com/4matic/trackparse/actions/workflows/js.yml)
|
|
8
|
-
[](https://github.com/4matic/trackparse/blob/main/LICENSE)
|
|
8
|
+
**[Documentation](https://4matic.github.io/trackparse/)** · **[Getting started](https://4matic.github.io/trackparse/start/getting-started/)** · **[Playground](https://4matic.github.io/trackparse/playground/)**
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**Turn messy track names into structured data: who made it, who's featured, who remixed it, and
|
|
11
|
+
what's just noise.**
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
```text
|
|
14
|
+
01. Wilkinson ft. Becky Hill & Tom Cane - Afterglow (Sub Focus VIP Remix) [Official Video]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Most title parsers split that into `"Wilkinson ft. Becky Hill & Tom Cane"` and
|
|
18
|
+
`"Afterglow (Sub Focus VIP Remix)"` and stop. Others "clean" it and throw the feat and the remix
|
|
19
|
+
away. trackparse keeps all of it. Everything it recognizes goes into a field, and anything it
|
|
20
|
+
doesn't recognize stays in the title, word for word.
|
|
21
|
+
|
|
22
|
+
This is the JavaScript and TypeScript package of
|
|
23
|
+
[trackparse](https://github.com/4matic/trackparse), and the reference implementation. Its behaviour
|
|
24
|
+
is defined by a [shared spec](https://github.com/4matic/trackparse/blob/main/spec/SPEC.md) and
|
|
25
|
+
pinned by 13,130 fixtures. It passes all of them, and the
|
|
26
|
+
[Python and Rust packages](#other-languages) return the same output for every one.
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
- Node 18 or later, browsers and other runtimes (no Node-specific APIs)
|
|
29
|
+
- No dependencies
|
|
30
|
+
- ESM and CommonJS, TypeScript types included
|
|
31
|
+
- Never throws
|
|
20
32
|
|
|
21
33
|
## Install
|
|
22
34
|
|
|
@@ -25,15 +37,13 @@ npm install trackparse
|
|
|
25
37
|
# or: pnpm add trackparse / yarn add trackparse / bun add trackparse
|
|
26
38
|
```
|
|
27
39
|
|
|
28
|
-
Node 18 or later. No Node-specific APIs are used, so it also runs in browsers and other runtimes.
|
|
29
|
-
|
|
30
40
|
## Quick start
|
|
31
41
|
|
|
32
42
|
**Docs:** [Getting started](https://4matic.github.io/trackparse/start/getting-started/)
|
|
33
43
|
|
|
34
44
|
```js
|
|
35
|
-
import { parse } from "trackparse";
|
|
36
|
-
// const { parse } = require("trackparse");
|
|
45
|
+
import { format, parse } from "trackparse";
|
|
46
|
+
// const { format, parse } = require("trackparse");
|
|
37
47
|
|
|
38
48
|
const track = parse(
|
|
39
49
|
"01. Wilkinson ft. Becky Hill & Tom Cane - Afterglow (Sub Focus VIP Remix) [Official Video]",
|
|
@@ -48,24 +58,184 @@ track.versions[0].artists[0].name; // 'Sub Focus'
|
|
|
48
58
|
track.versions[0].modifiers; // [ 'vip' ]
|
|
49
59
|
track.junk; // [ { raw: 'Official Video', kind: 'video' } ]
|
|
50
60
|
track.position; // { raw: '01', number: 1 }
|
|
51
|
-
track.mode; // 'youtube' (
|
|
61
|
+
track.mode; // 'youtube' (detected from the junk group)
|
|
62
|
+
|
|
63
|
+
format(track, { joiners: "canonical" });
|
|
64
|
+
// 'Wilkinson feat. Becky Hill & Tom Cane - Afterglow (Sub Focus VIP Remix)'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The result is a plain object, so `JSON.stringify(track)` gives the spec's JSON form. Every key is
|
|
68
|
+
always present:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
console.log(JSON.stringify(track, null, 2));
|
|
52
72
|
```
|
|
53
73
|
|
|
74
|
+
<details>
|
|
75
|
+
<summary>Output</summary>
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"input": "01. Wilkinson ft. Becky Hill & Tom Cane - Afterglow (Sub Focus VIP Remix) [Official Video]",
|
|
80
|
+
"mode": "youtube",
|
|
81
|
+
"position": { "raw": "01", "number": 1 },
|
|
82
|
+
"timestamp": null,
|
|
83
|
+
"artists": [
|
|
84
|
+
{ "name": "Wilkinson", "role": "primary", "joiner": null, "source": "artist" },
|
|
85
|
+
{ "name": "Becky Hill", "role": "featured", "joiner": "ft.", "source": "artist" },
|
|
86
|
+
{ "name": "Tom Cane", "role": "featured", "joiner": "&", "source": "artist" }
|
|
87
|
+
],
|
|
88
|
+
"title": "Afterglow",
|
|
89
|
+
"fullTitle": "Afterglow (Sub Focus VIP Remix)",
|
|
90
|
+
"versions": [
|
|
91
|
+
{
|
|
92
|
+
"type": "remix",
|
|
93
|
+
"raw": "Sub Focus VIP Remix",
|
|
94
|
+
"artists": [{ "name": "Sub Focus", "role": "remixer", "joiner": null, "source": "version" }],
|
|
95
|
+
"modifiers": ["vip"],
|
|
96
|
+
"descriptor": null,
|
|
97
|
+
"year": null,
|
|
98
|
+
"unknownArtist": false,
|
|
99
|
+
"delimiter": "("
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"year": null,
|
|
103
|
+
"flags": { "explicit": false, "clean": false, "unknownArtist": false, "unknownTitle": false },
|
|
104
|
+
"junk": [{ "raw": "Official Video", "kind": "video" }],
|
|
105
|
+
"warnings": []
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
(Reformatted for width; the keys and values are exactly what `JSON.stringify` prints.)
|
|
110
|
+
|
|
111
|
+
</details>
|
|
112
|
+
|
|
54
113
|
## API
|
|
55
114
|
|
|
56
115
|
**Docs:** [parse and ParsedTrack](https://4matic.github.io/trackparse/reference/parse/) · [Other functions](https://4matic.github.io/trackparse/reference/functions/)
|
|
57
116
|
|
|
58
|
-
|
|
117
|
+
```ts
|
|
118
|
+
parse(input: string, options?: ParseOptions): ParsedTrack
|
|
119
|
+
parseArtists(input: string, options?: ParseOptions): Artist[]
|
|
120
|
+
format(track: ParsedTrack, options?: FormatOptions): string
|
|
121
|
+
normalize(input: string): string
|
|
122
|
+
allArtists(track: ParsedTrack): Artist[]
|
|
123
|
+
createParser(options?: ParseOptions): Parser // parser.parse(...), parser.parseArtists(...)
|
|
124
|
+
SPEC_VERSION: string
|
|
125
|
+
|
|
126
|
+
// from "trackparse/tracklist"
|
|
127
|
+
parseTracklist(text: string, options?: TracklistOptions): Tracklist
|
|
128
|
+
applyEdits(text: string, edits: Edit[], options?: TracklistOptions): EditResult
|
|
129
|
+
```
|
|
59
130
|
|
|
60
|
-
|
|
131
|
+
### `parse(input, options?)`
|
|
61
132
|
|
|
62
|
-
|
|
133
|
+
**Docs:** [parse and ParsedTrack](https://4matic.github.io/trackparse/reference/parse/) · [Options](https://4matic.github.io/trackparse/reference/options/)
|
|
63
134
|
|
|
64
|
-
|
|
135
|
+
Parses one track string.
|
|
65
136
|
|
|
66
|
-
|
|
137
|
+
| Option | Default | |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `mode` | `"auto"` | `"clean"`, `"youtube"`, `"filename"`, `"tracklist"`, or `"auto"` to detect from the string. See [Modes](#modes) |
|
|
140
|
+
| `uploader` | – | YouTube channel name, used as the artist when there's no separator (`- Topic` and `VEVO` are dropped) |
|
|
141
|
+
| `knownArtists` | `[]` | Names that are never split. There is no built-in list |
|
|
142
|
+
| `splitAnd` | `"auto"` | `"auto"` splits on `and` only after a comma (`A, B and C`), so `Simon and Garfunkel` stays whole. Also `"always"`, `"never"` |
|
|
143
|
+
| `keywords` | – | Extra vocabulary, added to the built-in lists (see below) |
|
|
67
144
|
|
|
68
145
|
```js
|
|
146
|
+
parse("Chase & Status - Baddadan").artists.map((a) => a.name);
|
|
147
|
+
// [ 'Chase', 'Status' ]
|
|
148
|
+
parse("Chase & Status - Baddadan", { knownArtists: ["Chase & Status"] }).artists.map((a) => a.name);
|
|
149
|
+
// [ 'Chase & Status' ]
|
|
150
|
+
|
|
151
|
+
parse("Simon and Garfunkel - Mrs. Robinson", { splitAnd: "always" }).artists.map((a) => a.name);
|
|
152
|
+
// [ 'Simon', 'Garfunkel' ]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`keywords` is an object with any of these keys:
|
|
156
|
+
|
|
157
|
+
| Key | Type | |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `versionHeads` | `Record<string, VersionType>` | Word that sets a version type (`{ rerub: "rework" }`) |
|
|
160
|
+
| `descriptors` | `string[]` | Words kept as a version's `descriptor` |
|
|
161
|
+
| `genres` | `string[]` | Genre words |
|
|
162
|
+
| `junk` | `Record<string, JunkKind>` | Phrase to junk kind (`{ "some label rip": "label" }`) |
|
|
163
|
+
| `featMarkers` | `string[]` | Extra feat markers |
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
parse("Artist - Song [Some Label Rip]").title;
|
|
167
|
+
// 'Song [Some Label Rip]'
|
|
168
|
+
parse("Artist - Song [Some Label Rip]", { keywords: { junk: { "some label rip": "label" } } }).junk;
|
|
169
|
+
// [ { raw: 'Some Label Rip', kind: 'label' } ]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### `ParsedTrack`
|
|
173
|
+
|
|
174
|
+
**Docs:** [ParsedTrack fields](https://4matic.github.io/trackparse/reference/parse/#parsedtrack)
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
interface ParsedTrack {
|
|
178
|
+
input: string; // the original string, untouched
|
|
179
|
+
mode: "clean" | "youtube" | "filename" | "tracklist"; // resolved mode
|
|
180
|
+
position: { raw: string; number: number } | null; // track number: "01." gives 1
|
|
181
|
+
timestamp: { raw: string; seconds: number } | null; // leading cue time: "[12:34]" gives 754
|
|
182
|
+
artists: Artist[]; // primary, featured and producer credits (not remixers)
|
|
183
|
+
title: string; // clean title; brackets it doesn't recognize stay in
|
|
184
|
+
fullTitle: string; // title with the versions re-attached
|
|
185
|
+
versions: Version[]; // remixes and other versions, left to right
|
|
186
|
+
year: number | null; // from "(2015)" or "- 2015"
|
|
187
|
+
flags: { explicit: boolean; clean: boolean; unknownArtist: boolean; unknownTitle: boolean };
|
|
188
|
+
junk: { raw: string; kind: JunkKind }[]; // what was taken out, each with a kind
|
|
189
|
+
warnings: Warning[]; // heuristics that fired
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
interface Artist {
|
|
193
|
+
name: string;
|
|
194
|
+
role: "primary" | "featured" | "remixer" | "producer";
|
|
195
|
+
joiner: string | null; // "&", ",", "x", "ft.", "prod. by" as written; null for the first name
|
|
196
|
+
source: "artist" | "title" | "version"; // left of the dash, right of it, or inside a version
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
interface Version {
|
|
200
|
+
type: VersionType; // remix, vip, bootleg, extended, radio, live, remaster...
|
|
201
|
+
raw: string; // "Sub Focus VIP Remix"
|
|
202
|
+
artists: Artist[]; // remixers
|
|
203
|
+
modifiers: string[]; // type words that didn't become the type: ["vip"]
|
|
204
|
+
descriptor: string | null; // leftover words: "Taylor's", "at Wembley"
|
|
205
|
+
year: number | null;
|
|
206
|
+
unknownArtist: boolean; // "ID Remix"
|
|
207
|
+
delimiter: "(" | "[" | "{" | "-"; // the bracket it was in, or "-" for a dash suffix
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
parse("Pendulum - Watercolour (Extended VIP Mix)").versions[0];
|
|
213
|
+
// { type: 'vip', raw: 'Extended VIP Mix', artists: [], modifiers: [ 'extended' ],
|
|
214
|
+
// descriptor: null, year: null, unknownArtist: false, delimiter: '(' }
|
|
215
|
+
|
|
216
|
+
parse("Queen - Bohemian Rhapsody (Live at Wembley 1986)").versions[0];
|
|
217
|
+
// { type: 'live', raw: 'Live at Wembley 1986', artists: [], modifiers: [],
|
|
218
|
+
// descriptor: 'at Wembley', year: 1986, unknownArtist: false, delimiter: '(' }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
- `VersionType`: `remix`, `bootleg`, `vip`, `edit`, `flip`, `refix`, `rework`, `mashup`, `blend`,
|
|
222
|
+
`dub`, `mix`, `extended`, `radio`, `club`, `original`, `instrumental`, `acapella`, `live`,
|
|
223
|
+
`acoustic`, `remaster`, `demo`, `reprise`, `cover`, `version`, `spedUp`, `slowed`, `nightcore`.
|
|
224
|
+
- `JunkKind`: `video`, `audio`, `lyrics`, `quality`, `promo`, `platform`, `label`, `genre`, `other`.
|
|
225
|
+
- `Warning`: see [Warnings](#warnings).
|
|
226
|
+
|
|
227
|
+
The same shape is published as a JSON Schema,
|
|
228
|
+
[`parsed-track.schema.json`](https://github.com/4matic/trackparse/blob/main/spec/schema/parsed-track.schema.json).
|
|
229
|
+
|
|
230
|
+
### `parseArtists(input, options?)`
|
|
231
|
+
|
|
232
|
+
**Docs:** [parseArtists](https://4matic.github.io/trackparse/reference/functions/#parseartistsinput-options) · [Artists and credits](https://4matic.github.io/trackparse/guides/artists/)
|
|
233
|
+
|
|
234
|
+
Splits a bare artist string (no title). Takes the same options as `parse`.
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
import { parseArtists } from "trackparse";
|
|
238
|
+
|
|
69
239
|
parseArtists("Sub Focus, Wilkinson & Dimension feat. Kojo");
|
|
70
240
|
// [
|
|
71
241
|
// { name: 'Sub Focus', role: 'primary', joiner: null, source: 'artist' },
|
|
@@ -75,60 +245,72 @@ parseArtists("Sub Focus, Wilkinson & Dimension feat. Kojo");
|
|
|
75
245
|
// ]
|
|
76
246
|
```
|
|
77
247
|
|
|
78
|
-
### `format(track, options?)
|
|
248
|
+
### `format(track, options?)`
|
|
79
249
|
|
|
80
250
|
**Docs:** [format](https://4matic.github.io/trackparse/reference/functions/#formattrack-options) · [Formatting](https://4matic.github.io/trackparse/guides/formatting/)
|
|
81
251
|
|
|
82
|
-
Renders a `ParsedTrack` back to `Artists - Title (versions) (prod. ...)`.
|
|
252
|
+
Renders a `ParsedTrack` back to a string: `Artists - Title (versions) (prod. ...)`.
|
|
253
|
+
|
|
254
|
+
| Option | Default | |
|
|
255
|
+
|---|---|---|
|
|
256
|
+
| `feat` | `"source"` | Where featured artists go: `"source"` (where they were), `"artist"`, `"title"` (`(feat. X)` after the title) or `"omit"` |
|
|
257
|
+
| `featMarker` | – | Marker for featured artists, such as `"featuring"` |
|
|
258
|
+
| `joiners` | `"original"` | `"original"` keeps the joiners as written. `"canonical"` writes `and` as `&`, `vs` as `vs.` and every feat marker as `feat.` |
|
|
259
|
+
| `versions` | `"all"` | `"all"` or `"none"` |
|
|
260
|
+
| `producers` | `true` | Include `(prod. ...)` credits |
|
|
261
|
+
| `position` | `false` | Include the track number (`01. `) |
|
|
83
262
|
|
|
84
263
|
```js
|
|
264
|
+
import { format, parse } from "trackparse";
|
|
265
|
+
|
|
85
266
|
const t = parse("Wilkinson ft. Becky Hill - Afterglow (Sub Focus Remix)");
|
|
86
267
|
|
|
87
268
|
format(t); // 'Wilkinson ft. Becky Hill - Afterglow (Sub Focus Remix)'
|
|
88
269
|
format(t, { feat: "title", joiners: "canonical" }); // 'Wilkinson - Afterglow (feat. Becky Hill) (Sub Focus Remix)'
|
|
89
270
|
format(t, { feat: "omit", versions: "none" }); // 'Wilkinson - Afterglow'
|
|
271
|
+
format(t, { featMarker: "featuring" }); // 'Wilkinson featuring Becky Hill - Afterglow (Sub Focus Remix)'
|
|
90
272
|
```
|
|
91
273
|
|
|
92
|
-
|
|
93
|
-
|---|---|---|
|
|
94
|
-
| `feat` | `"source"` | `"source"` (where they were found), `"artist"`, `"title"` (`(feat. X)` after the title) or `"omit"`. |
|
|
95
|
-
| `featMarker` | – | Override the feat marker (`"featuring"`). |
|
|
96
|
-
| `joiners` | `"original"` | `"canonical"`: `and` → `&`, `vs` → `vs.`, feat markers → `feat.`. |
|
|
97
|
-
| `versions` | `"all"` | `"none"` drops versions. |
|
|
98
|
-
| `producers` | `true` | Append `(prod. by X)`. |
|
|
99
|
-
| `position` | `false` | Prefix the track number (`01. `). |
|
|
100
|
-
|
|
101
|
-
### `normalize(input) → string`
|
|
274
|
+
### `normalize(input)`
|
|
102
275
|
|
|
103
276
|
**Docs:** [normalize](https://4matic.github.io/trackparse/reference/functions/#normalizeinput)
|
|
104
277
|
|
|
105
|
-
The Unicode cleanup
|
|
106
|
-
brackets, smart quotes and dash variants mapped to ASCII, whitespace collapsed.
|
|
278
|
+
The Unicode cleanup every function runs first: NFC, zero-width spaces, BOM and soft hyphens
|
|
279
|
+
removed, fullwidth brackets, smart quotes and dash variants mapped to ASCII, whitespace collapsed.
|
|
280
|
+
Running it twice changes nothing.
|
|
107
281
|
|
|
108
282
|
```js
|
|
109
|
-
normalize
|
|
283
|
+
import { normalize } from "trackparse";
|
|
284
|
+
|
|
285
|
+
normalize(" Artist\u200b — Title (Remix) “Quoted” ");
|
|
110
286
|
// 'Artist - Title (Remix) "Quoted"'
|
|
111
287
|
```
|
|
112
288
|
|
|
113
|
-
### `allArtists(track)
|
|
289
|
+
### `allArtists(track)`
|
|
114
290
|
|
|
115
291
|
**Docs:** [allArtists](https://4matic.github.io/trackparse/reference/functions/#allartiststrack)
|
|
116
292
|
|
|
117
|
-
|
|
293
|
+
Credits plus every remixer, deduplicated.
|
|
118
294
|
|
|
119
295
|
```js
|
|
120
|
-
|
|
296
|
+
import { allArtists, parse } from "trackparse";
|
|
297
|
+
|
|
298
|
+
allArtists(parse("Wilkinson ft. Becky Hill - Afterglow (Sub Focus Remix)"))
|
|
121
299
|
.map((a) => `${a.name} (${a.role})`);
|
|
122
|
-
// [ '
|
|
300
|
+
// [ 'Wilkinson (primary)', 'Becky Hill (featured)', 'Sub Focus (remixer)' ]
|
|
123
301
|
```
|
|
124
302
|
|
|
125
|
-
### `createParser(options)
|
|
303
|
+
### `createParser(options?)`
|
|
126
304
|
|
|
127
305
|
**Docs:** [createParser](https://4matic.github.io/trackparse/reference/functions/#createparseroptions)
|
|
128
306
|
|
|
129
|
-
Compiles options,
|
|
307
|
+
Compiles the options once, for many calls. Per-call options are merged over the parser's: a value
|
|
308
|
+
passed per call replaces the base one (`knownArtists` included), `undefined` leaves it alone, and
|
|
309
|
+
`keywords` objects are merged.
|
|
130
310
|
|
|
131
311
|
```js
|
|
312
|
+
import { createParser } from "trackparse";
|
|
313
|
+
|
|
132
314
|
const parser = createParser({
|
|
133
315
|
knownArtists: ["Chase & Status", "Camo & Krooked"],
|
|
134
316
|
keywords: { versionHeads: { rerub: "rework" }, junk: { "full album": "other" } },
|
|
@@ -143,92 +325,75 @@ r.junk; // [ { raw: 'Full Album', kind: 'other' } ]
|
|
|
143
325
|
|
|
144
326
|
### `SPEC_VERSION`
|
|
145
327
|
|
|
146
|
-
The spec version this build implements,
|
|
328
|
+
The spec version this build implements, `"0.5.0"` in this release. The npm package, the PyPI
|
|
329
|
+
package, the Rust crate and the spec are released together and always share one version number.
|
|
147
330
|
|
|
148
|
-
##
|
|
331
|
+
## Modes
|
|
149
332
|
|
|
150
|
-
**Docs:** [
|
|
333
|
+
**Docs:** [Modes](https://4matic.github.io/trackparse/guides/modes/)
|
|
151
334
|
|
|
152
|
-
|
|
|
335
|
+
| Mode | For | Adds |
|
|
153
336
|
|---|---|---|
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
337
|
+
| `clean` | Store metadata, tags | Only a spaced dash separates artist and title |
|
|
338
|
+
| `youtube` | Video titles, scrobbles | Pipe segments, `"Title" by Artist`, `Artist: "Title"`, unspaced dashes, trailing junk, uploader fallback |
|
|
339
|
+
| `filename` | Files | Strips the extension, `_` becomes a space, `01-Track` numbering |
|
|
340
|
+
| `auto` | Anything | `filename` for known extensions, `youtube` when it sees junk, pipes or platform suffixes, `clean` otherwise |
|
|
341
|
+
| `tracklist` | One line of a tracklist | The first spaced dash always separates; `_-_` too. Never picked by `auto` |
|
|
159
342
|
|
|
160
343
|
```js
|
|
161
|
-
parse("
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
344
|
+
let t = parse('"Numb" by Linkin Park', { mode: "youtube" });
|
|
345
|
+
[t.artists.map((a) => a.name), t.title, t.warnings];
|
|
346
|
+
// [ [ 'Linkin Park' ], 'Numb', [ 'bySplit' ] ]
|
|
347
|
+
|
|
348
|
+
t = parse("07_Burial_-_Archangel.mp3");
|
|
349
|
+
[t.mode, t.artists.map((a) => a.name), t.title, t.position];
|
|
350
|
+
// [ 'filename', [ 'Burial' ], 'Archangel', { raw: '07', number: 7 } ]
|
|
166
351
|
|
|
167
|
-
parse("
|
|
352
|
+
parse("Shelter", { mode: "youtube", uploader: "Porter Robinson" }).artists;
|
|
353
|
+
// [ { name: 'Porter Robinson', role: 'primary', joiner: null, source: 'artist' } ]
|
|
354
|
+
|
|
355
|
+
parse("Jay-Z-Numb").title; // 'Jay-Z-Numb' (clean: no split)
|
|
168
356
|
parse("Jay-Z-Numb", { mode: "youtube" }).artists.map((a) => a.name); // [ 'Jay-Z' ]
|
|
169
357
|
```
|
|
170
358
|
|
|
171
|
-
##
|
|
359
|
+
## Warnings
|
|
172
360
|
|
|
173
|
-
|
|
361
|
+
**Docs:** [Warnings](https://4matic.github.io/trackparse/reference/warnings/)
|
|
174
362
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
input: string; // original, untouched
|
|
178
|
-
mode: "clean" | "youtube" | "filename" | "tracklist"; // resolved mode
|
|
179
|
-
position: { raw: string; number: number } | null;
|
|
180
|
-
timestamp: { raw: string; seconds: number } | null;
|
|
181
|
-
artists: Artist[]; // primary, featured, producer (not remixers)
|
|
182
|
-
title: string; // clean title
|
|
183
|
-
fullTitle: string; // title + versions with their brackets
|
|
184
|
-
versions: Version[];
|
|
185
|
-
year: number | null;
|
|
186
|
-
flags: { explicit: boolean; clean: boolean; unknownArtist: boolean; unknownTitle: boolean };
|
|
187
|
-
junk: { raw: string; kind: JunkKind }[];
|
|
188
|
-
warnings: Warning[];
|
|
189
|
-
}
|
|
363
|
+
The parser never fails, but it says when it had to guess. `track.warnings` is an array of strings,
|
|
364
|
+
empty when every decision was unambiguous.
|
|
190
365
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
366
|
+
| Warning | Means |
|
|
367
|
+
|---|---|
|
|
368
|
+
| `noSeparator` | No artist/title separator was found. The whole string is the title |
|
|
369
|
+
| `ambiguousSeparator` | A two-part pipe title with no dash was read as artist and title |
|
|
370
|
+
| `unspacedDashSplit` | Split at a dash without spaces around it |
|
|
371
|
+
| `asymmetricDashSplit` | Split at a dash with a space on one side only |
|
|
372
|
+
| `bySplit` | Read `"Title" by Artist` |
|
|
373
|
+
| `quotedTitleSplit` | Took a quoted span as the title and the text before it as the artist |
|
|
374
|
+
| `ambiguousMixCredit` | `<Name> Mix` was read as a remix, but the name is long enough to be a style |
|
|
375
|
+
| `unbalancedBrackets` | A bracket was opened and never closed |
|
|
197
376
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
modifiers: string[]; // ["vip"]
|
|
203
|
-
descriptor: string | null; // "Taylor's", "at Wembley", genres
|
|
204
|
-
year: number | null;
|
|
205
|
-
unknownArtist: boolean; // "ID Remix"
|
|
206
|
-
delimiter: "(" | "[" | "{" | "-";
|
|
207
|
-
}
|
|
208
|
-
```
|
|
377
|
+
```js
|
|
378
|
+
const t = parse("Around the World");
|
|
379
|
+
[t.artists, t.title, t.warnings];
|
|
380
|
+
// [ [], 'Around the World', [ 'noSeparator' ] ]
|
|
209
381
|
|
|
210
|
-
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- `JunkKind`: `video`, `audio`, `lyrics`, `quality`, `promo`, `platform`, `label`, `genre`, `other`.
|
|
214
|
-
- `Warning`: `noSeparator`, `ambiguousSeparator`, `unspacedDashSplit`, `asymmetricDashSplit`,
|
|
215
|
-
`bySplit`, `quotedTitleSplit`, `ambiguousMixCredit`, `unbalancedBrackets`.
|
|
382
|
+
parse("Noisia - Stigma (VIP").warnings;
|
|
383
|
+
// [ 'unbalancedBrackets' ]
|
|
384
|
+
```
|
|
216
385
|
|
|
217
|
-
|
|
218
|
-
[
|
|
219
|
-
[options](https://4matic.github.io/trackparse/reference/options/), [warnings](https://4matic.github.io/trackparse/reference/warnings/),
|
|
220
|
-
[what it understands](https://4matic.github.io/trackparse/guides/what-it-understands/). Exact behaviour is defined by the
|
|
221
|
-
[spec](https://github.com/4matic/trackparse/blob/main/spec/SPEC.md) and its
|
|
222
|
-
[fixtures](https://github.com/4matic/trackparse/tree/main/spec/fixtures).
|
|
386
|
+
Tracklists have their own warnings, with line numbers. They are listed under
|
|
387
|
+
[Tracklists](#tracklists).
|
|
223
388
|
|
|
224
389
|
## Tracklists
|
|
225
390
|
|
|
226
|
-
**Docs:** [Tracklists](https://4matic.github.io/trackparse/guides/tracklists/) · [Editing tracklists](https://4matic.github.io/trackparse/guides/editing-tracklists/)
|
|
391
|
+
**Docs:** [Tracklists](https://4matic.github.io/trackparse/guides/tracklists/) · [Editing tracklists](https://4matic.github.io/trackparse/guides/editing-tracklists/) · [Tracklist API](https://4matic.github.io/trackparse/reference/tracklist/) · [Warnings](https://4matic.github.io/trackparse/reference/warnings/#tracklist-warnings)
|
|
227
392
|
|
|
228
|
-
`trackparse/tracklist` parses a whole tracklist
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
gzipped), so you only
|
|
393
|
+
`trackparse/tracklist` parses a whole tracklist, one entry per line, and checks it. Each entry
|
|
394
|
+
keeps its line exactly as written plus its own line ending, so
|
|
395
|
+
`list.entries.map((e) => e.raw + e.eol).join("")` is always the input, CRLF or not. It is a
|
|
396
|
+
separate entry point (about 8.4 KiB gzipped), so you only load it if you import it.
|
|
232
397
|
|
|
233
398
|
```js
|
|
234
399
|
import { applyEdits, parseTracklist } from "trackparse/tracklist";
|
|
@@ -250,9 +415,14 @@ const list = parseTracklist(text);
|
|
|
250
415
|
|
|
251
416
|
list.entries.map((e) => e.kind);
|
|
252
417
|
// [ 'header', 'track', 'track', 'track', 'track', 'marker', 'track', 'track', 'track' ]
|
|
253
|
-
|
|
254
|
-
list.entries[2]
|
|
255
|
-
|
|
418
|
+
|
|
419
|
+
const e = list.entries[2];
|
|
420
|
+
e.time; // { raw: '52:47', seconds: 3167 }
|
|
421
|
+
e.parts;
|
|
422
|
+
// { lead: '', time: '52:47', gap: ' ', rest: 'Mefjus & Kasra - Bandwagon (Phace Remix)' }
|
|
423
|
+
[e.track.artists.map((a) => a.name), e.track.title, e.track.versions[0].type];
|
|
424
|
+
// [ [ 'Mefjus', 'Kasra' ], 'Bandwagon', 'remix' ]
|
|
425
|
+
list.entries[3].track.flags.unknownTitle; // true (the ID)
|
|
256
426
|
|
|
257
427
|
list.warnings;
|
|
258
428
|
// [
|
|
@@ -260,12 +430,15 @@ list.warnings;
|
|
|
260
430
|
// { code: 'time-out-of-order', line: 8, severity: 'warning',
|
|
261
431
|
// suggestion: { line: 8, raw: '01:11:02', seconds: 4262 } }
|
|
262
432
|
// ]
|
|
433
|
+
|
|
263
434
|
list.stats;
|
|
264
435
|
// { lines: 9, tracks: 7, markers: 1, timed: 8, firstTime: 2900, lastTime: 4536 }
|
|
265
436
|
```
|
|
266
437
|
|
|
267
|
-
|
|
268
|
-
|
|
438
|
+
The suggestion exists because exactly one time, one digit away from `00:11:02`, fits between its
|
|
439
|
+
neighbours. With several candidates or none, `suggestion` is `null`.
|
|
440
|
+
|
|
441
|
+
`applyEdits` takes an array of edit objects and only rewrites the lines they name:
|
|
269
442
|
|
|
270
443
|
```js
|
|
271
444
|
const fix = list.warnings[1].suggestion;
|
|
@@ -274,20 +447,38 @@ const result = applyEdits(text, [
|
|
|
274
447
|
{ op: "shift", seconds: 45, from: 5 },
|
|
275
448
|
]);
|
|
276
449
|
|
|
277
|
-
result.changed;
|
|
278
|
-
result.warnings;
|
|
279
|
-
result.text.split("\r\n")[4];
|
|
450
|
+
result.changed; // [ 5, 6, 7, 8, 9 ]
|
|
451
|
+
result.warnings; // [ { code: 'style-upgraded', edit: 1, line: 5 } ]
|
|
452
|
+
result.text.split("\r\n")[4]; // '01:00:16 Alix Perez - Fade Away' (was 59:31)
|
|
453
|
+
|
|
454
|
+
applyEdits(text, [{ op: "shift", seconds: -3000, lines: [2, 3] }]).warnings;
|
|
455
|
+
// [ { code: 'time-clamped', edit: 0, line: 2 } ]
|
|
280
456
|
```
|
|
281
457
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
458
|
+
A rewritten time keeps its line's style: `52:47` stays `MM:SS`, and a time that crosses the hour
|
|
459
|
+
gets hours like the rest of the list, with a `style-upgraded` warning. A time pushed below zero
|
|
460
|
+
stops at zero and warns `time-clamped`. A bad edit changes nothing and warns `edit-invalid`.
|
|
461
|
+
|
|
462
|
+
Both functions take the options `mode` (default `"tracklist"`), `knownArtists`, `splitAnd` and
|
|
463
|
+
`keywords`, as `parse` does, plus `audioSeconds` (the length of the audio, which turns on the
|
|
464
|
+
`time-after-audio-end` check) and `markerWords` (extra words that make a line a marker):
|
|
465
|
+
|
|
466
|
+
```js
|
|
467
|
+
parseTracklist("00:00 Noisia - Stigma\n1:02:03 Mefjus - Suicide Bassline", { audioSeconds: 3600 })
|
|
468
|
+
.warnings;
|
|
469
|
+
// [ { code: 'time-after-audio-end', line: 2, severity: 'warning', suggestion: null } ]
|
|
470
|
+
```
|
|
287
471
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
`
|
|
472
|
+
| Result | Fields |
|
|
473
|
+
|---|---|
|
|
474
|
+
| `Tracklist` | `specVersion`, `entries`, `warnings`, `stats` |
|
|
475
|
+
| `TracklistEntry` | `line` (1-based), `kind` (`track`, `marker`, `header`, `link`, `note`, `blank`), `eol`, `raw`, `parts`, `time`, `track` (`ParsedTrack` or `null`), `layered` (the track text after any time starts with `w/`) |
|
|
476
|
+
| `EntryParts` | `lead`, `time`, `gap`, `rest`, joined they give `raw` |
|
|
477
|
+
| `TracklistWarning` | `code`, `line`, `severity` (`info` or `warning`), `suggestion` |
|
|
478
|
+
| `TracklistTime` | `raw`, `seconds` (`entry.time`, or `null` on untimed lines) |
|
|
479
|
+
| `TimeSuggestion` | `line`, `raw`, `seconds` (`warning.suggestion`, or `null`) |
|
|
480
|
+
| `TracklistStats` | `lines`, `tracks`, `markers`, `timed`, `firstTime`, `lastTime` |
|
|
481
|
+
| `EditResult` | `text`, `changed`, `warnings` (`EditWarning`: `code`, `edit`, `line`) |
|
|
291
482
|
|
|
292
483
|
Lists pasted with list bullets or Markdown emphasis (`* 14:21 Artist - Title`,
|
|
293
484
|
`**[**14:21**] Artist - Title**`) read like plain lines; the decoration stays in `raw` and `parts`.
|
|
@@ -296,29 +487,133 @@ previous one
|
|
|
296
487
|
([Markdown](https://4matic.github.io/trackparse/guides/tracklists/#markdown),
|
|
297
488
|
[layered tracks](https://4matic.github.io/trackparse/guides/tracklists/#layered-tracks)).
|
|
298
489
|
|
|
299
|
-
|
|
300
|
-
`
|
|
301
|
-
|
|
490
|
+
Warning codes: `time-missing`, `time-out-of-order`, `time-duplicate`, `no-start-at-zero`,
|
|
491
|
+
`time-after-audio-end` (needs `audioSeconds`), `invalid-time`, `position-gap`,
|
|
492
|
+
`position-duplicate`, `line-unparsed`. Edits: `set-time {line, seconds}`,
|
|
493
|
+
`shift {seconds, lines? | from?}` (all timed lines when neither is given), `sort-by-time {}`,
|
|
494
|
+
`set-rest {line, rest}`, `insert {after, text}` (`after: 0` inserts at the top), `delete {line}`.
|
|
495
|
+
The [Tracklist API reference](https://4matic.github.io/trackparse/reference/tracklist/) explains
|
|
496
|
+
each one, and the rules are in
|
|
497
|
+
[SPEC R13](https://github.com/4matic/trackparse/blob/main/spec/SPEC.md#r13-tracklists).
|
|
498
|
+
`JSON.stringify(list)` matches
|
|
499
|
+
[`tracklist.schema.json`](https://github.com/4matic/trackparse/blob/main/spec/schema/tracklist.schema.json).
|
|
302
500
|
|
|
303
|
-
|
|
304
|
-
and title. `parse()`
|
|
501
|
+
Every line is parsed with `mode: "tracklist"`, where the first spaced dash always separates artist
|
|
502
|
+
and title. `parse()` accepts it too:
|
|
305
503
|
|
|
306
504
|
```js
|
|
307
|
-
parse("Electrosoul System - Liquid Metal").
|
|
308
|
-
|
|
505
|
+
parse("Electrosoul System - Liquid Metal").junk;
|
|
506
|
+
// [ { raw: 'Liquid Metal', kind: 'genre' } ]
|
|
507
|
+
parse("Electrosoul System - Liquid Metal", { mode: "tracklist" }).title;
|
|
508
|
+
// 'Liquid Metal'
|
|
309
509
|
```
|
|
310
510
|
|
|
311
|
-
|
|
312
|
-
|
|
511
|
+
## Errors
|
|
512
|
+
|
|
513
|
+
No function throws. `parse` returns a complete `ParsedTrack` for any string, `""` included, and a
|
|
514
|
+
value that isn't a string is read as `""`. An option of the wrong type or with an unknown value is
|
|
515
|
+
ignored and the default is used. `applyEdits` reports a bad edit as an `edit-invalid` warning and
|
|
516
|
+
leaves the text alone.
|
|
517
|
+
|
|
518
|
+
Recognition is done by hand-written scanners, so there is no regex backtracking. A
|
|
519
|
+
10,000-code-point input finishes in under 100 ms.
|
|
520
|
+
|
|
521
|
+
## TypeScript
|
|
522
|
+
|
|
523
|
+
**Docs:** [Types](https://4matic.github.io/trackparse/reference/parse/#types) · [Tracklist types](https://4matic.github.io/trackparse/reference/tracklist/#types)
|
|
524
|
+
|
|
525
|
+
The types ship with the package for both ESM and CommonJS. Nothing else needs installing.
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
import type { Artist, ParsedTrack, ParseOptions, VersionType } from "trackparse";
|
|
529
|
+
import type { Edit, Tracklist, TracklistWarning } from "trackparse/tracklist";
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
- `trackparse` exports `ParsedTrack`, `Artist`, `ArtistRole`, `ArtistSource`, `Version`,
|
|
533
|
+
`VersionType`, `VersionDelimiter`, `Junk`, `JunkKind`, `Flags`, `Position`, `Timestamp`,
|
|
534
|
+
`Warning`, `Mode`, `ModeOption`, `ParseOptions`, `KeywordOptions`, `FormatOptions` and `Parser`.
|
|
535
|
+
- `trackparse/tracklist` exports `Tracklist`, `TracklistEntry`, `EntryKind`, `EntryParts`, `Eol`,
|
|
536
|
+
`TracklistTime`, `TracklistWarning`, `TracklistWarningCode`, `TimeSuggestion`, `TracklistStats`,
|
|
537
|
+
`TracklistOptions`, `Edit`, `EditResult`, `EditWarning` and `EditWarningCode`.
|
|
538
|
+
|
|
539
|
+
Version types, junk kinds, warnings and edit operations are string literal unions, so the compiler
|
|
540
|
+
catches a misspelt name.
|
|
541
|
+
|
|
542
|
+
## How it is tested
|
|
543
|
+
|
|
544
|
+
**Docs:** [How it's tested](https://4matic.github.io/trackparse/project/testing/)
|
|
545
|
+
|
|
546
|
+
- 13,130 shared fixtures: 1,203 written by hand and 11,927 generated from the rules. Every output
|
|
547
|
+
is also validated against the JSON Schema.
|
|
548
|
+
- Property tests with [fast-check](https://github.com/dubzzz/fast-check) for the spec's invariants:
|
|
549
|
+
no throw on any input, `normalize` is idempotent, whitespace and dash noise doesn't change the
|
|
550
|
+
result, and `parse(format(parse(s)))` round-trips.
|
|
551
|
+
- The published build is run against every fixture on Node 18 and 20, as ESM and as CommonJS.
|
|
552
|
+
- [`compare-ports.mjs`](https://github.com/4matic/trackparse/blob/main/scripts/compare-ports.mjs)
|
|
553
|
+
runs every fixture input through this package, the Python package and the Rust crate and diffs
|
|
554
|
+
the complete JSON output. The difference count is zero.
|
|
555
|
+
|
|
556
|
+
## Versioning
|
|
557
|
+
|
|
558
|
+
**Changelog:** [js/CHANGELOG.md](https://github.com/4matic/trackparse/blob/main/js/CHANGELOG.md)
|
|
559
|
+
|
|
560
|
+
The package version is the spec version (`SPEC_VERSION`). While it is 0.x, a release that changes
|
|
561
|
+
the output for some input bumps the minor version, and a patch release only fixes bugs. If you
|
|
562
|
+
store parsed results, pin the minor version (`"trackparse": "~0.5.0"`) and read the changelog
|
|
563
|
+
before moving up.
|
|
564
|
+
|
|
565
|
+
## Other languages
|
|
566
|
+
|
|
567
|
+
**Docs:** [Ports](https://4matic.github.io/trackparse/project/ports/)
|
|
568
|
+
|
|
569
|
+
The same parser exists for Python and Rust. All three implement one spec, pass the same fixtures,
|
|
570
|
+
are released together under one version number, and return identical JSON for every fixture input.
|
|
571
|
+
You can parse in one language and read the result in another.
|
|
572
|
+
|
|
573
|
+
| Language | Package | Install |
|
|
574
|
+
|---|---|---|
|
|
575
|
+
| JavaScript / TypeScript | [trackparse on npm](https://www.npmjs.com/package/trackparse) | `npm install trackparse` |
|
|
576
|
+
| Python | [trackparse on PyPI](https://pypi.org/project/trackparse/) | `pip install trackparse` |
|
|
577
|
+
| Rust | [trackparse on crates.io](https://crates.io/crates/trackparse) | `cargo add trackparse` |
|
|
313
578
|
|
|
314
579
|
## Limitations
|
|
315
580
|
|
|
316
581
|
**Docs:** [Limitations and roadmap](https://4matic.github.io/trackparse/project/roadmap/)
|
|
317
582
|
|
|
318
|
-
|
|
319
|
-
start times only
|
|
320
|
-
|
|
583
|
+
- `parse()` takes one track string. Whole tracklists go through `trackparse/tracklist`, which reads
|
|
584
|
+
start times only: no durations, trailing times or time ranges.
|
|
585
|
+
- Catalog numbers, key and BPM are not fields. They stay in the title.
|
|
586
|
+
- There is no artist database. Pass `knownArtists` for names that contain a joiner
|
|
587
|
+
(`Earth, Wind & Fire`).
|
|
588
|
+
|
|
589
|
+
## Development
|
|
590
|
+
|
|
591
|
+
From the root of the [repository](https://github.com/4matic/trackparse), with Node 22 or later and
|
|
592
|
+
[pnpm](https://pnpm.io/):
|
|
593
|
+
|
|
594
|
+
```sh
|
|
595
|
+
pnpm install
|
|
596
|
+
pnpm --filter trackparse test # every shared fixture, properties, API
|
|
597
|
+
pnpm --filter trackparse typecheck
|
|
598
|
+
pnpm lint # Biome
|
|
599
|
+
pnpm --filter trackparse conformance # pass rate per fixture file and per rule
|
|
600
|
+
pnpm --filter trackparse build # js/dist, ESM and CommonJS
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
The Python and Rust ports mirror this package's modules one to one. Behaviour changes start in the
|
|
604
|
+
spec, not here: see [AGENTS.md](https://github.com/4matic/trackparse/blob/main/AGENTS.md).
|
|
605
|
+
|
|
606
|
+
## Links
|
|
607
|
+
|
|
608
|
+
- [Documentation](https://4matic.github.io/trackparse/), with every example in JavaScript, Python
|
|
609
|
+
and Rust, and a [playground](https://4matic.github.io/trackparse/playground/) that runs the
|
|
610
|
+
parser in your browser
|
|
611
|
+
- [Spec](https://github.com/4matic/trackparse/blob/main/spec/SPEC.md)
|
|
612
|
+
- [Changelog](https://github.com/4matic/trackparse/blob/main/js/CHANGELOG.md)
|
|
613
|
+
- [Project README](https://github.com/4matic/trackparse#readme)
|
|
614
|
+
- [GitHub repository](https://github.com/4matic/trackparse)
|
|
615
|
+
- [PyPI package](https://pypi.org/project/trackparse/) · [Rust crate](https://crates.io/crates/trackparse)
|
|
321
616
|
|
|
322
617
|
## License
|
|
323
618
|
|
|
324
|
-
MIT
|
|
619
|
+
[MIT](https://github.com/4matic/trackparse/blob/main/LICENSE)
|
package/dist/index.cjs
CHANGED
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { P as ParsedTrack, A as Artist, F as FormatOptions, a as ParseOptions } from './types-DqJSn18b.cjs';
|
|
2
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';
|
|
3
3
|
|
|
4
|
-
declare const SPEC_VERSION = "0.
|
|
4
|
+
declare const SPEC_VERSION = "0.5.1";
|
|
5
5
|
|
|
6
6
|
declare function format(track: ParsedTrack, options?: FormatOptions): string;
|
|
7
7
|
/** All credited names: track artists plus every version's remixers, deduped by R0.4 key. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { P as ParsedTrack, A as Artist, F as FormatOptions, a as ParseOptions } from './types-DqJSn18b.js';
|
|
2
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';
|
|
3
3
|
|
|
4
|
-
declare const SPEC_VERSION = "0.
|
|
4
|
+
declare const SPEC_VERSION = "0.5.1";
|
|
5
5
|
|
|
6
6
|
declare function format(track: ParsedTrack, options?: FormatOptions): string;
|
|
7
7
|
/** All credited names: track artists plus every version's remixers, deduped by R0.4 key. */
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getDefaultTables, renderVersion, dedupKey, wordMatches } from './chunk-
|
|
2
|
-
export { SPEC_VERSION, createParser, normalize, parse, parseArtists } from './chunk-
|
|
1
|
+
import { getDefaultTables, renderVersion, dedupKey, wordMatches } from './chunk-F7PBCPFT.js';
|
|
2
|
+
export { SPEC_VERSION, createParser, normalize, parse, parseArtists } from './chunk-F7PBCPFT.js';
|
|
3
3
|
|
|
4
4
|
// src/format.ts
|
|
5
5
|
function resolve(o) {
|
package/dist/tracklist.cjs
CHANGED
package/dist/tracklist.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { headerWordsData, layeredWordsData, createListParser, SPEC_VERSION, stringList, PhraseTable, markerWordsData, isWhitespace, isDigit, digitRun, inVocab, asciiLower, splitWords } from './chunk-
|
|
1
|
+
import { headerWordsData, layeredWordsData, createListParser, SPEC_VERSION, stringList, PhraseTable, markerWordsData, isWhitespace, isDigit, digitRun, inVocab, asciiLower, splitWords } from './chunk-F7PBCPFT.js';
|
|
2
2
|
|
|
3
3
|
// src/tracklist/split.ts
|
|
4
4
|
function splitLines(text) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trackparse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Spec-driven parser for music track strings: artists, feat credits, remixes/versions, junk and more from single track strings (store titles, YouTube titles, filenames) and multi-line tracklists.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Maksim Maksimov",
|