@concordance-wiki/plugin-reader-vtt 0.3.1 → 0.4.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 +1 -1
- package/dist/render.d.ts +13 -3
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +16 -2
- package/dist/render.js.map +1 -1
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +3 -3
- package/dist/text.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ Part of [Concordance](https://github.com/concordance-wiki/concordance), GNU GPL
|
|
|
80
80
|
<details>
|
|
81
81
|
<summary>Inside the package</summary>
|
|
82
82
|
|
|
83
|
-
The plugin contributes a `reader` for `.vtt` and `.srt`. `parseTranscript(text, format)` turns a transcript into its cues (index, start and end in seconds, speaker, text), the unique speakers in order of first appearance, the duration (end of the last cue) and, for VTT, the `Language:` header. `renderTranscript` produces static HTML: one `<article>` per run of consecutive cues of the same speaker, headed by the speaker's name, and one `<p id="t-<milliseconds>">` per cue whose timecode links to its own anchor, so that a timecode is shareable as a URL fragment. `transcriptText` gives the spoken text, one line per speaker turn, with the character range of every cue, so that a position in the text maps back to a timecode. The reader returns `{ metadata: { format, language, duration, cues, speakers }, text, units }`, where `units` (`transcriptUnits`) cuts the text into one addressable unit per speaker turn, `{ label, text, anchor, speaker? }`, labelled by the timecode of its first cue, named after its speaker when the cues name one and anchored like the rendering; the build reads them as the positions of the transcript, so that its text goes through term recognition like any other content and a mention in it cites `00:12:05` where a note cites a line. `rewriteTranscript` writes the file again with every speaker and every text passed through the substitution the pseudonymisation gives, timecodes kept, so that the file offered for download never carries a real name.
|
|
83
|
+
The plugin contributes a `reader` for `.vtt` and `.srt`. `parseTranscript(text, format)` turns a transcript into its cues (index, start and end in seconds, speaker, text), the unique speakers in order of first appearance, the duration (end of the last cue) and, for VTT, the `Language:` header. `renderTranscript` produces static HTML: one `<article>` per run of consecutive cues of the same speaker, headed by the speaker's name, and one `<p id="t-<milliseconds>">` per cue whose timecode links to its own anchor, so that a timecode is shareable as a URL fragment; a second cue starting at the same instant takes its rank as a suffix (`t-12000-2`), so that every cue stays addressable. `transcriptText` gives the spoken text, one line per speaker turn, with the character range of every cue, so that a position in the text maps back to a timecode. The reader returns `{ metadata: { format, language, duration, cues, speakers }, text, units }`, where `units` (`transcriptUnits`) cuts the text into one addressable unit per speaker turn, `{ label, text, anchor, speaker? }`, labelled by the timecode of its first cue, named after its speaker when the cues name one and anchored like the rendering; the build reads them as the positions of the transcript, so that its text goes through term recognition like any other content and a mention in it cites `00:12:05` where a note cites a line. `rewriteTranscript` writes the file again with every speaker and every text passed through the substitution the pseudonymisation gives, timecodes kept, so that the file offered for download never carries a real name.
|
|
84
84
|
|
|
85
85
|
### Parsing rules
|
|
86
86
|
|
package/dist/render.d.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import type { Cue, Transcript } from "./parse.js";
|
|
2
|
-
export interface CueGroup {
|
|
2
|
+
export interface CueGroup<C extends Cue = Cue> {
|
|
3
3
|
speaker?: string;
|
|
4
|
-
cues:
|
|
4
|
+
cues: C[];
|
|
5
|
+
}
|
|
6
|
+
/** A cue with the anchor of its paragraph in the rendered transcript. */
|
|
7
|
+
export interface AnchoredCue extends Cue {
|
|
8
|
+
anchor: string;
|
|
5
9
|
}
|
|
6
10
|
/** Consecutive cues of the same speaker (or of no speaker) form one group, in transcript order. */
|
|
7
|
-
export declare function groupCues(cues:
|
|
11
|
+
export declare function groupCues<C extends Cue>(cues: readonly C[]): CueGroup<C>[];
|
|
8
12
|
export declare function escapeHtml(text: string): string;
|
|
9
13
|
/** The identifier of the anchor of a cue: `t-` followed by its start in milliseconds. */
|
|
10
14
|
export declare function anchorOf(cue: Cue): string;
|
|
15
|
+
/**
|
|
16
|
+
* The anchor of every cue, unique in the transcript: a second cue at the same instant (two
|
|
17
|
+
* speakers overlapping, a cue cut in two lines) takes its rank as a suffix, `t-12000-2`, so that
|
|
18
|
+
* every cue stays addressable and a citation lands where it was taken.
|
|
19
|
+
*/
|
|
20
|
+
export declare function anchorsOf(cues: readonly Cue[]): AnchoredCue[];
|
|
11
21
|
/** `hh:mm:ss`, whole seconds, hours on two digits or more. */
|
|
12
22
|
export declare function formatTimecode(seconds: number): string;
|
|
13
23
|
/** Static HTML without style or script: one article per speaker turn, one anchored paragraph per cue. */
|
package/dist/render.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,QAAQ;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,CAAC,EAAE,CAAC;CACX;AAED,yEAAyE;AACzE,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,mGAAmG;AACnG,wBAAgB,SAAS,CAAC,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAW1E;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM/C;AAED,yFAAyF;AACzF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,GAAG,WAAW,EAAE,CAQ7D;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAItD;AAgBD,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAM/D"}
|
package/dist/render.js
CHANGED
|
@@ -22,6 +22,20 @@ export function escapeHtml(text) {
|
|
|
22
22
|
export function anchorOf(cue) {
|
|
23
23
|
return `t-${String(Math.round(cue.start * 1000))}`;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The anchor of every cue, unique in the transcript: a second cue at the same instant (two
|
|
27
|
+
* speakers overlapping, a cue cut in two lines) takes its rank as a suffix, `t-12000-2`, so that
|
|
28
|
+
* every cue stays addressable and a citation lands where it was taken.
|
|
29
|
+
*/
|
|
30
|
+
export function anchorsOf(cues) {
|
|
31
|
+
const taken = new Map();
|
|
32
|
+
return cues.map((cue) => {
|
|
33
|
+
const base = anchorOf(cue);
|
|
34
|
+
const rank = (taken.get(base) ?? 0) + 1;
|
|
35
|
+
taken.set(base, rank);
|
|
36
|
+
return { ...cue, anchor: rank === 1 ? base : `${base}-${String(rank)}` };
|
|
37
|
+
});
|
|
38
|
+
}
|
|
25
39
|
/** `hh:mm:ss`, whole seconds, hours on two digits or more. */
|
|
26
40
|
export function formatTimecode(seconds) {
|
|
27
41
|
const whole = Math.floor(seconds);
|
|
@@ -29,7 +43,7 @@ export function formatTimecode(seconds) {
|
|
|
29
43
|
return `${pad(Math.floor(whole / 3600))}:${pad(Math.floor((whole % 3600) / 60))}:${pad(whole % 60)}`;
|
|
30
44
|
}
|
|
31
45
|
function renderCue(cue) {
|
|
32
|
-
const anchor =
|
|
46
|
+
const { anchor } = cue;
|
|
33
47
|
return (`<p id="${anchor}"><a class="timecode" href="#${anchor}">` +
|
|
34
48
|
`<time datetime="PT${String(cue.start)}S">${formatTimecode(cue.start)}</time></a> ` +
|
|
35
49
|
`<span class="cue">${escapeHtml(cue.text)}</span></p>`);
|
|
@@ -42,7 +56,7 @@ function renderGroup(group) {
|
|
|
42
56
|
export function renderTranscript(transcript) {
|
|
43
57
|
return [
|
|
44
58
|
'<section class="transcript">',
|
|
45
|
-
...groupCues(transcript.cues).map(renderGroup),
|
|
59
|
+
...groupCues(anchorsOf(transcript.cues)).map(renderGroup),
|
|
46
60
|
"</section>",
|
|
47
61
|
].join("\n");
|
|
48
62
|
}
|
package/dist/render.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
|
package/dist/text.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,GAAG,cAAc,CAkBrE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,EAAE,CAWpE"}
|
package/dist/text.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { anchorsOf, formatTimecode, groupCues } from "./render.js";
|
|
2
2
|
/** The spoken text, one line per speaker turn; the offsets map any position of `text` back to a cue. */
|
|
3
3
|
export function transcriptText(transcript) {
|
|
4
4
|
const offsets = [];
|
|
@@ -25,13 +25,13 @@ export function transcriptText(transcript) {
|
|
|
25
25
|
* citation lands on the passage.
|
|
26
26
|
*/
|
|
27
27
|
export function transcriptUnits(transcript) {
|
|
28
|
-
return groupCues(transcript.cues).map((group) => {
|
|
28
|
+
return groupCues(anchorsOf(transcript.cues)).map((group) => {
|
|
29
29
|
// groupCues only ever opens a group around a cue: the first one is always there.
|
|
30
30
|
const first = group.cues[0];
|
|
31
31
|
return {
|
|
32
32
|
label: formatTimecode(first.start),
|
|
33
33
|
text: group.cues.map((cue) => cue.text).join(" "),
|
|
34
|
-
anchor:
|
|
34
|
+
anchor: first.anchor,
|
|
35
35
|
...(group.speaker === undefined ? {} : { speaker: group.speaker }),
|
|
36
36
|
};
|
|
37
37
|
});
|
package/dist/text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAoB,MAAM,aAAa,CAAC;AAcrF,wGAAwG;AACxG,MAAM,UAAU,cAAc,CAAC,UAAsB;IACnD,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/C,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;gBACb,QAAQ,IAAI,CAAC,CAAC;YAChB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACxF,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,QAAQ,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,UAAsB;IACpD,OAAO,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACzD,iFAAiF;QACjF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAgB,CAAC;QAC3C,OAAO;YACL,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC;YAClC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YACjD,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;SACnE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@concordance-wiki/plugin-reader-vtt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Reader for VTT and SRT transcripts: cues, speakers, duration, language, HTML rendering with addressable timecodes.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"LICENSE"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@concordance-wiki/core": "0.
|
|
33
|
+
"@concordance-wiki/core": "0.4.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"vitest": "4.1.11"
|