@gondola-lyrics/kit 0.1.0 → 0.2.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/README.md +18 -14
- package/dist/formats.d.ts +54 -6
- package/dist/formats.js +1161 -23
- package/dist/formats.js.map +1 -1
- package/dist/generator.d.ts +3 -1
- package/dist/index.d.ts +36 -6
- package/dist/index.js +1 -1
- package/dist/parser.d.ts +34 -6
- package/dist/parser.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npm install @gondola-lyrics/kit
|
|
|
20
20
|
> This package is ESM-only.
|
|
21
21
|
> In your TS config, use `module: node20` / `module: nodenext` / `moduleResolution: bundler`.
|
|
22
22
|
|
|
23
|
-
Parsing returns a `runtime.Lyric
|
|
23
|
+
Parsing returns a canonical `runtime.Lyric` together with the diagnostics it produced, so the result is ready to hand to a player without a second conversion step.
|
|
24
24
|
|
|
25
25
|
### Parse
|
|
26
26
|
|
|
@@ -28,14 +28,15 @@ Parsing returns a `runtime.Lyric`, so the result is ready to validate and hand t
|
|
|
28
28
|
import { parse, parseLrc } from '@gondola-lyrics/kit'
|
|
29
29
|
|
|
30
30
|
const content = `[00:01.00]Like the first time tangled by the fishing line`
|
|
31
|
-
const
|
|
31
|
+
const parsed = parse(content)
|
|
32
32
|
|
|
33
|
-
lyric
|
|
34
|
-
lyric
|
|
33
|
+
parsed?.lyric.format // => 'lrc'
|
|
34
|
+
parsed?.lyric.lines[0]?.time?.start // => 1000
|
|
35
|
+
parsed?.diagnostics // => []
|
|
35
36
|
|
|
36
37
|
// `parse` recognizes the format and returns `null` when it cannot.
|
|
37
38
|
// `parseLrc` takes a known format and never returns `null`, so an unreadable source only carries `status: INVALID`.
|
|
38
|
-
parseLrc('no timestamps here').status // => LyricStatus.INVALID
|
|
39
|
+
parseLrc('no timestamps here').lyric.status // => LyricStatus.INVALID
|
|
39
40
|
|
|
40
41
|
// LRC keeps translations and romanizations in separate files, attached here by timestamp.
|
|
41
42
|
const annotated = parseLrc({
|
|
@@ -47,8 +48,8 @@ const annotated = parseLrc({
|
|
|
47
48
|
[00:03.00]tanguru do bai za rain`,
|
|
48
49
|
})
|
|
49
50
|
|
|
50
|
-
annotated.lines[0]?.annotation?.translations[0].text // => '就像第一次'
|
|
51
|
-
annotated.lines[0]?.annotation?.romans[0].text // => 'raiku za fasuto taimu'
|
|
51
|
+
annotated.lyric.lines[0]?.annotation?.translations[0].text // => '就像第一次'
|
|
52
|
+
annotated.lyric.lines[0]?.annotation?.romans[0].text // => 'raiku za fasuto taimu'
|
|
52
53
|
|
|
53
54
|
// An exact timestamp match is assigned before any fuzzy one, and rows matching no line are dropped.
|
|
54
55
|
// A drifting track attaches within a tolerance, where the nearest unclaimed line wins.
|
|
@@ -59,10 +60,12 @@ const drifted = {
|
|
|
59
60
|
[00:03.20]被渔线缠住那样`,
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
parseLrc(drifted).lines[0]?.annotation // => undefined
|
|
63
|
-
parseLrc(drifted, { annotationTolerance: 500 }).lines[0]?.annotation?.translations[0].text // => '就像第一次'
|
|
63
|
+
parseLrc(drifted).lyric.lines[0]?.annotation // => undefined
|
|
64
|
+
parseLrc(drifted, { annotationTolerance: 500 }).lyric.lines[0]?.annotation?.translations[0].text // => '就像第一次'
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
Every parser canonicalizes its lyric and validates the result before returning, so `lyric` is already in canonical order and `diagnostics` is exactly what `@gondola-lyrics/model` reports for it.
|
|
68
|
+
|
|
66
69
|
### Generate
|
|
67
70
|
|
|
68
71
|
`generate` takes the target format explicitly, so a lyric parsed from one format can be written out as another.
|
|
@@ -70,7 +73,7 @@ parseLrc(drifted, { annotationTolerance: 500 }).lines[0]?.annotation?.translatio
|
|
|
70
73
|
```ts
|
|
71
74
|
import { generate, parseLrc } from '@gondola-lyrics/kit'
|
|
72
75
|
|
|
73
|
-
const lyric = parseLrc('[00:01.00]Like the first time')
|
|
76
|
+
const { lyric } = parseLrc('[00:01.00]Like the first time')
|
|
74
77
|
|
|
75
78
|
// Separate tracks come back as separate fields, so the result parses again unchanged.
|
|
76
79
|
const { original, translations, romans } = generate(lyric, 'lrc')
|
|
@@ -93,14 +96,15 @@ Detection reads only the original text. It answers whether the source is recogni
|
|
|
93
96
|
|
|
94
97
|
### Validation
|
|
95
98
|
|
|
96
|
-
The kit does not
|
|
99
|
+
The kit does not invent diagnostics. It canonicalizes the lyric and hands back exactly what `@gondola-lyrics/model` reports for it.
|
|
97
100
|
|
|
98
101
|
```ts
|
|
99
|
-
|
|
102
|
+
const { lyric, diagnostics } = parseLrc(source)
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
// The same list, computed by the kit on the way out.
|
|
105
|
+
import { validateLyric } from '@gondola-lyrics/model/runtime'
|
|
102
106
|
|
|
103
|
-
validateLyric(
|
|
107
|
+
validateLyric(lyric) // deep-equals `diagnostics`
|
|
104
108
|
```
|
|
105
109
|
|
|
106
110
|
### Entries
|
package/dist/formats.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { Diagnostic } from '@gondola-lyrics/model/runtime';
|
|
1
2
|
import { runtime } from '@gondola-lyrics/model';
|
|
2
3
|
|
|
4
|
+
export declare const AMLL_HINT_PATTERN: RegExp;
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* Recognition reads only the original text; the annotation tracks never decide the format.
|
|
5
8
|
*
|
|
6
9
|
* Unrecognized text returns `undefined`.
|
|
7
10
|
*/
|
|
8
|
-
export declare const detectFormat: (input: unknown) => "lrc" | undefined;
|
|
11
|
+
export declare const detectFormat: (input: unknown) => "lrc" | "ttml" | undefined;
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* A single readable time tag is enough for recognition; metadata alone is not.
|
|
@@ -13,8 +16,20 @@ export declare const detectFormat: (input: unknown) => "lrc" | undefined;
|
|
|
13
16
|
*/
|
|
14
17
|
export declare const detectLrc: (input: unknown) => boolean;
|
|
15
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Recognition needs a profile hint, since plain TTML says nothing about which profile wrote it.
|
|
21
|
+
*
|
|
22
|
+
* A bare `tt` root also counts, which is what a hand-written document has.
|
|
23
|
+
* This is a format probe, not a validity check, so an input of another shape is simply not TTML.
|
|
24
|
+
*/
|
|
25
|
+
export declare const detectTtml: (input: unknown) => boolean;
|
|
26
|
+
|
|
27
|
+
export declare const FORMAT = "ttml";
|
|
28
|
+
|
|
16
29
|
/**
|
|
17
30
|
* Generates text in the requested format, which need not match `lyric.format`.
|
|
31
|
+
*
|
|
32
|
+
* A format without a writer has no branch here, so adding one to `LyricFormat` makes this fail to compile.
|
|
18
33
|
*/
|
|
19
34
|
export declare const generate: (lyric: runtime.Lyric, target: LyricFormat, options?: LrcGenerateOptions) => LrcGenerateResult;
|
|
20
35
|
|
|
@@ -25,6 +40,8 @@ export declare const generate: (lyric: runtime.Lyric, target: LyricFormat, optio
|
|
|
25
40
|
*/
|
|
26
41
|
export declare const generateLrc: (lyric: runtime.Lyric, options?: LrcGenerateOptions) => LrcGenerateResult;
|
|
27
42
|
|
|
43
|
+
export declare const ITUNES_HINT_PATTERN: RegExp;
|
|
44
|
+
|
|
28
45
|
declare interface LrcContent {
|
|
29
46
|
original: string;
|
|
30
47
|
translations?: string;
|
|
@@ -65,21 +82,52 @@ export declare interface LrcParseOptions {
|
|
|
65
82
|
/**
|
|
66
83
|
* Values match the lowercase format names stored in `Lyric.format`.
|
|
67
84
|
*/
|
|
68
|
-
export declare type LyricFormat = 'lrc';
|
|
85
|
+
export declare type LyricFormat = 'lrc' | 'ttml';
|
|
69
86
|
|
|
70
87
|
/**
|
|
71
|
-
* Unrecognized text returns `null`; a recognized format returns
|
|
88
|
+
* Unrecognized text returns `null`; a recognized format returns its canonical lyric and diagnostics.
|
|
72
89
|
* Annotation tracks travel with the source and are attached by the format's parser.
|
|
73
90
|
*/
|
|
74
|
-
export declare const parse: (input: unknown, options?: LrcParseOptions) =>
|
|
91
|
+
export declare const parse: (input: unknown, options?: LrcParseOptions & TtmlParseOptions) => ParseResult | null;
|
|
75
92
|
|
|
76
93
|
/**
|
|
77
94
|
* Source order, empty timed lines, and missing word times are preserved for model validation.
|
|
78
95
|
*
|
|
79
96
|
* Annotation tracks are read as lines too and attached by timestamp, and rows that match no line are dropped.
|
|
80
97
|
*
|
|
81
|
-
* `INVALID` means no readable timed lines;
|
|
98
|
+
* `INVALID` means no readable timed lines; the returned diagnostics carry whatever the canonical tree breaks.
|
|
99
|
+
*/
|
|
100
|
+
export declare const parseLrc: (input: string | LrcParseInput, options?: LrcParseOptions) => ParseResult;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A parsed lyric in canonical form, with every rule it breaks.
|
|
104
|
+
*/
|
|
105
|
+
export declare interface ParseResult {
|
|
106
|
+
lyric: runtime.Lyric;
|
|
107
|
+
diagnostics: Diagnostic[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Reads a TTML document into the model.
|
|
112
|
+
*
|
|
113
|
+
* The profile only changes where metadata comes from, so one reader serves both.
|
|
114
|
+
* A document that parses but holds no timed paragraph is `INVALID` rather than an error.
|
|
82
115
|
*/
|
|
83
|
-
export declare const
|
|
116
|
+
export declare const parseTtml: (input: string | TtmlContent, options?: TtmlParseOptions) => ParseResult;
|
|
117
|
+
|
|
118
|
+
export declare const readTtmlContent: (input: unknown) => TtmlContent | null;
|
|
119
|
+
|
|
120
|
+
export declare const readTtmlProfile: (source: string) => TtmlProfile;
|
|
121
|
+
|
|
122
|
+
export declare interface TtmlContent {
|
|
123
|
+
original: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export declare interface TtmlParseOptions {
|
|
127
|
+
/** Language tag the lyric declares; the document's own `xml:lang` wins when it states one. */
|
|
128
|
+
language?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export declare type TtmlProfile = 'amll' | 'itunes';
|
|
84
132
|
|
|
85
133
|
export { }
|