@storyteller-platform/align 0.1.56 → 0.1.58
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/dist/align/align.cjs +38 -19
- package/dist/align/align.d.cts +12 -1
- package/dist/align/align.d.ts +12 -1
- package/dist/align/align.js +38 -19
- package/dist/align/ctc/Aligner.cjs +800 -0
- package/dist/align/ctc/Aligner.d.cts +56 -0
- package/dist/align/ctc/Aligner.d.ts +56 -0
- package/dist/align/ctc/Aligner.js +789 -0
- package/dist/align/ctc/align.cjs +189 -0
- package/dist/align/ctc/align.d.cts +14 -0
- package/dist/align/ctc/align.d.ts +14 -0
- package/dist/align/ctc/align.js +132 -0
- package/dist/{process/mime.cjs → align/ctc/emissions.cjs} +7 -20
- package/dist/align/ctc/emissions.d.cts +6 -0
- package/dist/align/ctc/emissions.d.ts +6 -0
- package/dist/align/ctc/emissions.js +7 -0
- package/dist/align/ctc/forcedAlign.cjs +273 -0
- package/dist/align/ctc/forcedAlign.d.cts +21 -0
- package/dist/align/ctc/forcedAlign.d.ts +21 -0
- package/dist/align/ctc/forcedAlign.js +248 -0
- package/dist/align/ctc/greedyDecode.cjs +61 -0
- package/dist/align/ctc/greedyDecode.d.cts +15 -0
- package/dist/align/ctc/greedyDecode.d.ts +15 -0
- package/dist/align/ctc/greedyDecode.js +37 -0
- package/dist/align/ctc/mediaOverlay.cjs +248 -0
- package/dist/align/ctc/mediaOverlay.d.cts +16 -0
- package/dist/align/ctc/mediaOverlay.d.ts +16 -0
- package/dist/align/ctc/mediaOverlay.js +224 -0
- package/dist/align/ctc/report.cjs +66 -0
- package/dist/align/ctc/report.d.cts +79 -0
- package/dist/align/ctc/report.d.ts +79 -0
- package/dist/align/ctc/report.js +42 -0
- package/dist/align/ctc/search.cjs +437 -0
- package/dist/align/ctc/search.d.cts +46 -0
- package/dist/align/ctc/search.d.ts +46 -0
- package/dist/align/ctc/search.js +412 -0
- package/dist/align/getSentenceRanges.cjs +9 -3
- package/dist/align/getSentenceRanges.d.cts +1 -0
- package/dist/align/getSentenceRanges.d.ts +1 -0
- package/dist/align/getSentenceRanges.js +9 -3
- package/dist/align/parse.cjs +17 -4
- package/dist/align/parse.d.cts +8 -4
- package/dist/align/parse.d.ts +8 -4
- package/dist/align/parse.js +19 -4
- package/dist/align/search.cjs +76 -23
- package/dist/align/search.d.cts +8 -4
- package/dist/align/search.d.ts +8 -4
- package/dist/align/search.js +76 -24
- package/dist/align/slugify.cjs +3 -2
- package/dist/align/slugify.d.cts +1 -1
- package/dist/align/slugify.d.ts +1 -1
- package/dist/align/slugify.js +3 -2
- package/dist/cli/bin.cjs +204 -114
- package/dist/cli/bin.js +194 -103
- package/dist/common/ffmpeg.cjs +10 -5
- package/dist/common/ffmpeg.js +10 -5
- package/dist/common/logging.cjs +33 -1
- package/dist/common/logging.js +33 -1
- package/dist/emit/emit.cjs +105 -0
- package/dist/emit/emit.d.cts +15 -0
- package/dist/emit/emit.d.ts +15 -0
- package/dist/emit/emit.js +86 -0
- package/dist/emit/fs.cjs +227 -0
- package/dist/emit/fs.d.cts +22 -0
- package/dist/emit/fs.d.ts +22 -0
- package/dist/emit/fs.js +162 -0
- package/dist/emit/parse.cjs +68 -0
- package/dist/emit/parse.d.cts +26 -0
- package/dist/emit/parse.d.ts +26 -0
- package/dist/emit/parse.js +58 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/markup/model.cjs +0 -16
- package/dist/markup/model.d.cts +1 -1
- package/dist/markup/model.d.ts +1 -1
- package/dist/markup/model.js +0 -12
- package/dist/markup/parseDom.cjs +13 -14
- package/dist/markup/parseDom.d.cts +2 -2
- package/dist/markup/parseDom.d.ts +2 -2
- package/dist/markup/parseDom.js +13 -18
- package/dist/markup/resolvedPos.d.cts +1 -1
- package/dist/markup/resolvedPos.d.ts +1 -1
- package/dist/markup/segmentation.cjs +3 -1
- package/dist/markup/segmentation.js +3 -1
- package/dist/markup/serializeDom.cjs +1 -1
- package/dist/markup/serializeDom.d.cts +1 -1
- package/dist/markup/serializeDom.d.ts +1 -1
- package/dist/markup/serializeDom.js +1 -1
- package/dist/markup/transform.cjs +5 -2
- package/dist/markup/transform.d.cts +1 -1
- package/dist/markup/transform.d.ts +1 -1
- package/dist/markup/transform.js +5 -4
- package/dist/{model-CZ2mMHJO.d.cts → model-CeOMDDgt.d.cts} +3 -11
- package/dist/{model-CZ2mMHJO.d.ts → model-CeOMDDgt.d.ts} +3 -11
- package/dist/process/processAudiobook.cjs +12 -23
- package/dist/process/processAudiobook.js +14 -30
- package/dist/process/ranges.cjs +2 -2
- package/dist/process/ranges.js +2 -2
- package/dist/readium/guidedNavigation.cjs +8 -8
- package/dist/readium/guidedNavigation.js +8 -8
- package/dist/readium/manifest.cjs +5 -4
- package/dist/readium/manifest.js +5 -4
- package/dist/snapshot/ctc/snapshot.cjs +261 -0
- package/dist/snapshot/ctc/snapshot.d.cts +6 -0
- package/dist/snapshot/ctc/snapshot.d.ts +6 -0
- package/dist/snapshot/ctc/snapshot.js +200 -0
- package/dist/snapshot/parse.cjs +29 -16
- package/dist/snapshot/parse.d.cts +14 -10
- package/dist/snapshot/parse.d.ts +14 -10
- package/dist/snapshot/parse.js +32 -17
- package/dist/transcribe/parse.d.cts +6 -6
- package/dist/transcribe/parse.d.ts +6 -6
- package/dist/transcribe/transcribe.cjs +4 -2
- package/dist/transcribe/transcribe.js +4 -2
- package/package.json +6 -4
- package/dist/process/mime.d.cts +0 -3
- package/dist/process/mime.d.ts +0 -3
- package/dist/process/mime.js +0 -24
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as _storyteller_platform_epub from '@storyteller-platform/epub';
|
|
2
|
+
import { ManifestItem } from '@storyteller-platform/epub';
|
|
3
|
+
import { SegmentationResult } from '@echogarden/text-segmentation';
|
|
4
|
+
import { SentenceRange, WordRange } from '../getSentenceRanges.js';
|
|
5
|
+
import '@storyteller-platform/ghost-story';
|
|
6
|
+
import '@storyteller-platform/mapping';
|
|
7
|
+
|
|
8
|
+
declare function createMediaOverlay(chapter: ManifestItem, granularity: "sentence" | "word", sentenceRanges: SentenceRange[], wordRanges: Map<number, WordRange[]>, sentenceIdToBlockFragment: Map<number, string> | null, sentenceIdToFragment: Map<number, string>, wordIdToFragment: Map<number, Map<number, string>>): _storyteller_platform_epub.XmlElement<"smil">[];
|
|
9
|
+
declare function buildFragmentMaps(sentenceRanges: SentenceRange[], wordRanges: WordRange[][], sentences: SegmentationResult["sentences"], locale: Intl.Locale, textRef: "id-fragment" | "text-fragment", granularity: "sentence" | "word"): {
|
|
10
|
+
sentenceIdToBlockFragment: Map<number, string> | null;
|
|
11
|
+
sentenceIdToFragment: Map<number, string>;
|
|
12
|
+
wordIdToFragment: Map<number, Map<number, string>>;
|
|
13
|
+
wordRangeMap: Map<number, WordRange[]>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export { buildFragmentMaps, createMediaOverlay };
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import "../../chunk-BIEQXUOY.js";
|
|
2
|
+
import { basename } from "node:path/posix";
|
|
3
|
+
import { enumerate } from "itertools";
|
|
4
|
+
import { Epub } from "@storyteller-platform/epub";
|
|
5
|
+
import { TextFragmentFactory } from "../textFragments.js";
|
|
6
|
+
function createMediaOverlay(chapter, granularity, sentenceRanges, wordRanges, sentenceIdToBlockFragment, sentenceIdToFragment, wordIdToFragment) {
|
|
7
|
+
const subSequences = sentenceIdToBlockFragment ? createTextRangeLargeSequences(
|
|
8
|
+
chapter,
|
|
9
|
+
granularity,
|
|
10
|
+
sentenceRanges,
|
|
11
|
+
wordRanges,
|
|
12
|
+
sentenceIdToBlockFragment,
|
|
13
|
+
sentenceIdToFragment,
|
|
14
|
+
wordIdToFragment
|
|
15
|
+
) : createTextRangeSmallSequences(
|
|
16
|
+
chapter,
|
|
17
|
+
granularity,
|
|
18
|
+
sentenceRanges,
|
|
19
|
+
wordRanges,
|
|
20
|
+
sentenceIdToFragment,
|
|
21
|
+
wordIdToFragment
|
|
22
|
+
);
|
|
23
|
+
return [
|
|
24
|
+
Epub.createXmlElement(
|
|
25
|
+
"smil",
|
|
26
|
+
{
|
|
27
|
+
xmlns: "http://www.w3.org/ns/SMIL",
|
|
28
|
+
"xmlns:epub": "http://www.idpf.org/2007/ops",
|
|
29
|
+
version: "3.0"
|
|
30
|
+
},
|
|
31
|
+
[
|
|
32
|
+
Epub.createXmlElement("body", {}, [
|
|
33
|
+
Epub.createXmlElement(
|
|
34
|
+
"seq",
|
|
35
|
+
{
|
|
36
|
+
id: `${chapter.id}_overlay`,
|
|
37
|
+
"epub:textref": `../${chapter.href}`,
|
|
38
|
+
"epub:type": "chapter"
|
|
39
|
+
},
|
|
40
|
+
subSequences
|
|
41
|
+
)
|
|
42
|
+
])
|
|
43
|
+
]
|
|
44
|
+
)
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
function createTextRangeLargeSequences(chapter, granularity, sentenceRanges, wordRanges, sentenceIdToBlockFragment, sentenceIdToFragment, wordIdToFragment) {
|
|
48
|
+
const blockStarts = sentenceIdToBlockFragment.entries().toArray().toSorted(([a], [b]) => a - b);
|
|
49
|
+
return blockStarts.map(([sentenceId, fragment], index) => {
|
|
50
|
+
const blockEnd = index === blockStarts.length - 1 ? sentenceRanges.length - 1 : (
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
52
|
+
blockStarts[index + 1][0]
|
|
53
|
+
);
|
|
54
|
+
const sentences = sentenceRanges.slice(sentenceId, blockEnd);
|
|
55
|
+
return Epub.createXmlElement(
|
|
56
|
+
"seq",
|
|
57
|
+
{
|
|
58
|
+
id: `${chapter.id}-b${index}`,
|
|
59
|
+
"epub:type": "text-range-large",
|
|
60
|
+
"epub:textref": `../${chapter.href}#${fragment}`
|
|
61
|
+
},
|
|
62
|
+
createTextRangeSmallSequences(
|
|
63
|
+
chapter,
|
|
64
|
+
granularity,
|
|
65
|
+
sentences,
|
|
66
|
+
wordRanges,
|
|
67
|
+
sentenceIdToFragment,
|
|
68
|
+
wordIdToFragment
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function createTextRangeSmallSequences(chapter, granularity, sentenceRanges, wordRanges, sentenceIdToFragment, wordIdToFragment) {
|
|
74
|
+
return sentenceRanges.map((sentenceRange) => {
|
|
75
|
+
if (granularity === "sentence" || !wordRanges.has(sentenceRange.id)) {
|
|
76
|
+
return Epub.createXmlElement(
|
|
77
|
+
"par",
|
|
78
|
+
{
|
|
79
|
+
id: `${chapter.id}-s${sentenceRange.id}`
|
|
80
|
+
},
|
|
81
|
+
[
|
|
82
|
+
Epub.createXmlElement("text", {
|
|
83
|
+
src: `../${chapter.href}#${sentenceIdToFragment.get(sentenceRange.id)}`
|
|
84
|
+
}),
|
|
85
|
+
Epub.createXmlElement("audio", {
|
|
86
|
+
src: `../Audio/${basename(sentenceRange.audiofile)}`,
|
|
87
|
+
clipBegin: `${sentenceRange.start.toFixed(3)}s`,
|
|
88
|
+
clipEnd: `${sentenceRange.end.toFixed(3)}s`
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const words = wordRanges.get(sentenceRange.id);
|
|
94
|
+
const wordToFragment = wordIdToFragment.get(sentenceRange.id);
|
|
95
|
+
return Epub.createXmlElement(
|
|
96
|
+
"seq",
|
|
97
|
+
{
|
|
98
|
+
id: `${chapter.id}-s${sentenceRange.id}`,
|
|
99
|
+
"epub:type": "text-range-small",
|
|
100
|
+
"epub:textref": `../${chapter.href}#${sentenceIdToFragment.get(sentenceRange.id)}`
|
|
101
|
+
},
|
|
102
|
+
words.map(
|
|
103
|
+
(word) => Epub.createXmlElement(
|
|
104
|
+
"par",
|
|
105
|
+
{
|
|
106
|
+
id: `${chapter.id}-s${sentenceRange.id}-w${word.id}`
|
|
107
|
+
},
|
|
108
|
+
[
|
|
109
|
+
Epub.createXmlElement("text", {
|
|
110
|
+
src: `../${chapter.href}#${wordToFragment.get(word.id)}`
|
|
111
|
+
}),
|
|
112
|
+
Epub.createXmlElement("audio", {
|
|
113
|
+
src: `../Audio/${basename(word.audiofile)}`,
|
|
114
|
+
clipBegin: `${word.start.toFixed(3)}s`,
|
|
115
|
+
clipEnd: `${word.end.toFixed(3)}s`
|
|
116
|
+
})
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function buildFragmentMaps(sentenceRanges, wordRanges, sentences, locale, textRef, granularity) {
|
|
124
|
+
let sentenceIdToBlockFragment = null;
|
|
125
|
+
const sentenceIdToFragment = new Map(
|
|
126
|
+
sentenceRanges.map((range) => [
|
|
127
|
+
range.id,
|
|
128
|
+
`${range.chapterId}-s${range.id}`
|
|
129
|
+
])
|
|
130
|
+
);
|
|
131
|
+
const wordIdToFragment = new Map(
|
|
132
|
+
wordRanges.map((ranges) => [
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
134
|
+
ranges[0].sentenceId,
|
|
135
|
+
new Map(
|
|
136
|
+
ranges.map((range) => [
|
|
137
|
+
range.id,
|
|
138
|
+
`${range.chapterId}-s${range.sentenceId}-w${range.id}`
|
|
139
|
+
])
|
|
140
|
+
)
|
|
141
|
+
])
|
|
142
|
+
);
|
|
143
|
+
const wordRangeMap = new Map(wordRanges.map((w) => [w[0].sentenceId, w]));
|
|
144
|
+
if (textRef === "text-fragment") {
|
|
145
|
+
sentenceIdToBlockFragment = /* @__PURE__ */ new Map();
|
|
146
|
+
const blocks = [[]];
|
|
147
|
+
for (const [i, sentence] of enumerate(sentences)) {
|
|
148
|
+
const text = sentence.text;
|
|
149
|
+
blocks.at(-1)?.push(text);
|
|
150
|
+
if (text.includes("\n") && i < sentences.length - 1) {
|
|
151
|
+
blocks.push([]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const blockFactory = new TextFragmentFactory(
|
|
155
|
+
blocks.map((block) => block.join("")),
|
|
156
|
+
locale
|
|
157
|
+
);
|
|
158
|
+
let sentenceRangeIndex = 0;
|
|
159
|
+
for (const [i, block] of enumerate(blocks)) {
|
|
160
|
+
sentenceIdToBlockFragment.set(
|
|
161
|
+
sentenceRangeIndex,
|
|
162
|
+
blockFactory.findMinimalFragment(i)
|
|
163
|
+
);
|
|
164
|
+
const sentenceFactory = new TextFragmentFactory(
|
|
165
|
+
block.map((s) => s.replace("\n", "")),
|
|
166
|
+
locale
|
|
167
|
+
);
|
|
168
|
+
const blockRanges = sentenceRanges.slice(
|
|
169
|
+
sentenceRangeIndex,
|
|
170
|
+
sentenceRangeIndex + block.length
|
|
171
|
+
);
|
|
172
|
+
for (const [j, range] of enumerate(blockRanges)) {
|
|
173
|
+
sentenceIdToFragment.set(
|
|
174
|
+
range.id,
|
|
175
|
+
sentenceFactory.findMinimalFragment(j)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (granularity === "word") {
|
|
179
|
+
const allWords = [];
|
|
180
|
+
for (const range of blockRanges) {
|
|
181
|
+
const sentence = sentences[range.id];
|
|
182
|
+
const words = [];
|
|
183
|
+
for (const w of sentence.words.entries) {
|
|
184
|
+
if (w.isPunctuation) {
|
|
185
|
+
const lastWord = words.at(-1);
|
|
186
|
+
if (lastWord === void 0) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
words[words.length - 1] = lastWord + w.text.replace("\n", "");
|
|
190
|
+
} else {
|
|
191
|
+
words.push(w.text);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
allWords.push(...words);
|
|
195
|
+
}
|
|
196
|
+
const wordFactory = new TextFragmentFactory(allWords);
|
|
197
|
+
let wordRangeIndex = 0;
|
|
198
|
+
for (const range of blockRanges) {
|
|
199
|
+
const ranges = wordRangeMap.get(range.id);
|
|
200
|
+
const toFragment = wordIdToFragment.get(range.id);
|
|
201
|
+
if (!ranges || !toFragment) continue;
|
|
202
|
+
for (const [k, wordRange] of enumerate(ranges)) {
|
|
203
|
+
toFragment.set(
|
|
204
|
+
wordRange.id,
|
|
205
|
+
wordFactory.findMinimalFragment(k + wordRangeIndex)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
wordRangeIndex += ranges.length;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
sentenceRangeIndex += block.length;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
sentenceIdToBlockFragment,
|
|
216
|
+
sentenceIdToFragment,
|
|
217
|
+
wordIdToFragment,
|
|
218
|
+
wordRangeMap
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
buildFragmentMaps,
|
|
223
|
+
createMediaOverlay
|
|
224
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var report_exports = {};
|
|
20
|
+
__export(report_exports, {
|
|
21
|
+
buildAudioFileContexts: () => buildAudioFileContexts,
|
|
22
|
+
updateAudioFileReports: () => updateAudioFileReports
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(report_exports);
|
|
25
|
+
function buildAudioFileContexts(sentenceRanges) {
|
|
26
|
+
return sentenceRanges.reduce((acc, range) => {
|
|
27
|
+
const existing = acc.find((context) => context.filepath === range.audiofile);
|
|
28
|
+
if (existing) {
|
|
29
|
+
existing.end = range.end;
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
acc.push({
|
|
33
|
+
filepath: range.audiofile,
|
|
34
|
+
start: range.start,
|
|
35
|
+
end: range.end
|
|
36
|
+
});
|
|
37
|
+
return acc;
|
|
38
|
+
}, []);
|
|
39
|
+
}
|
|
40
|
+
function updateAudioFileReports(report, audioFiles, audioFileDurations) {
|
|
41
|
+
for (const audioFile of audioFiles) {
|
|
42
|
+
const existing = report.audioFiles.find(
|
|
43
|
+
({ filepath }) => audioFile.filepath === filepath
|
|
44
|
+
);
|
|
45
|
+
if (existing) {
|
|
46
|
+
existing.matchedRanges.push({
|
|
47
|
+
start: audioFile.start,
|
|
48
|
+
end: audioFile.end
|
|
49
|
+
});
|
|
50
|
+
existing.matchedRanges.sort((a, b) => a.start - b.start);
|
|
51
|
+
existing.alignedDuration += audioFile.end - audioFile.start;
|
|
52
|
+
} else {
|
|
53
|
+
report.audioFiles.push({
|
|
54
|
+
alignedDuration: audioFile.end - audioFile.start,
|
|
55
|
+
duration: audioFileDurations[audioFile.filepath] ?? 0,
|
|
56
|
+
filepath: audioFile.filepath,
|
|
57
|
+
matchedRanges: [{ start: audioFile.start, end: audioFile.end }]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
63
|
+
0 && (module.exports = {
|
|
64
|
+
buildAudioFileContexts,
|
|
65
|
+
updateAudioFileReports
|
|
66
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ManifestItem, ParsedXml } from '@storyteller-platform/epub';
|
|
2
|
+
import { SentenceRange, WordRange } from '../getSentenceRanges.cjs';
|
|
3
|
+
import '@storyteller-platform/ghost-story';
|
|
4
|
+
import '@echogarden/text-segmentation';
|
|
5
|
+
import '@storyteller-platform/mapping';
|
|
6
|
+
|
|
7
|
+
type AlignedChapter = {
|
|
8
|
+
chapter: ManifestItem;
|
|
9
|
+
chunk?: number;
|
|
10
|
+
xml: ParsedXml;
|
|
11
|
+
sentenceRanges: SentenceRange[];
|
|
12
|
+
wordRanges: WordRange[][];
|
|
13
|
+
startOffset: number;
|
|
14
|
+
endOffset: number;
|
|
15
|
+
};
|
|
16
|
+
interface AudioFileContext {
|
|
17
|
+
start: number;
|
|
18
|
+
end: number;
|
|
19
|
+
filepath: string;
|
|
20
|
+
}
|
|
21
|
+
interface ChapterReport {
|
|
22
|
+
href: string;
|
|
23
|
+
spineIndex: number;
|
|
24
|
+
chunk: number;
|
|
25
|
+
score: number;
|
|
26
|
+
transcriptionOffset: number;
|
|
27
|
+
endTranscriptionOffset: number;
|
|
28
|
+
firstMatchedSentenceId: number;
|
|
29
|
+
firstMatchedSentenceContext: {
|
|
30
|
+
prevSentence: string | null;
|
|
31
|
+
matchedSentence: string;
|
|
32
|
+
nextSentence: string | null;
|
|
33
|
+
};
|
|
34
|
+
lastMatchedSentenceId: number;
|
|
35
|
+
lastMatchedSentenceContext: {
|
|
36
|
+
prevSentence: string | null;
|
|
37
|
+
matchedSentence: string;
|
|
38
|
+
nextSentence: string | null;
|
|
39
|
+
};
|
|
40
|
+
chapterSentenceCount: number;
|
|
41
|
+
alignedSentenceCount: number;
|
|
42
|
+
audioFiles: AudioFileContext[];
|
|
43
|
+
}
|
|
44
|
+
type UnalignedChapterReason = "too-short" | "not-found" | "is-nav" | "no-text";
|
|
45
|
+
interface UnalignedChapterReport {
|
|
46
|
+
href: string;
|
|
47
|
+
reason: Exclude<UnalignedChapterReason, "not-found">;
|
|
48
|
+
}
|
|
49
|
+
interface UnalignedNotFoundChapterReport {
|
|
50
|
+
href: string;
|
|
51
|
+
spineIndex: number;
|
|
52
|
+
score: number;
|
|
53
|
+
chapterSentenceCount: number;
|
|
54
|
+
reason: "not-found";
|
|
55
|
+
start: string;
|
|
56
|
+
end: string;
|
|
57
|
+
}
|
|
58
|
+
interface AudioFileReport {
|
|
59
|
+
filepath: string;
|
|
60
|
+
matchedRanges: {
|
|
61
|
+
start: number;
|
|
62
|
+
end: number;
|
|
63
|
+
}[];
|
|
64
|
+
duration: number;
|
|
65
|
+
alignedDuration: number;
|
|
66
|
+
}
|
|
67
|
+
interface UnalignedAudioFileReport {
|
|
68
|
+
filepath: string;
|
|
69
|
+
}
|
|
70
|
+
interface Report {
|
|
71
|
+
chapters: ChapterReport[];
|
|
72
|
+
unalignedChapters: (UnalignedChapterReport | UnalignedNotFoundChapterReport)[];
|
|
73
|
+
audioFiles: AudioFileReport[];
|
|
74
|
+
unalignedAudioFiles: UnalignedAudioFileReport[];
|
|
75
|
+
}
|
|
76
|
+
declare function buildAudioFileContexts(sentenceRanges: SentenceRange[]): AudioFileContext[];
|
|
77
|
+
declare function updateAudioFileReports(report: Report, audioFiles: AudioFileContext[], audioFileDurations: Record<string, number>): void;
|
|
78
|
+
|
|
79
|
+
export { type AlignedChapter, type AudioFileContext, type AudioFileReport, type ChapterReport, type Report, type UnalignedAudioFileReport, type UnalignedChapterReason, type UnalignedChapterReport, type UnalignedNotFoundChapterReport, buildAudioFileContexts, updateAudioFileReports };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ManifestItem, ParsedXml } from '@storyteller-platform/epub';
|
|
2
|
+
import { SentenceRange, WordRange } from '../getSentenceRanges.js';
|
|
3
|
+
import '@storyteller-platform/ghost-story';
|
|
4
|
+
import '@echogarden/text-segmentation';
|
|
5
|
+
import '@storyteller-platform/mapping';
|
|
6
|
+
|
|
7
|
+
type AlignedChapter = {
|
|
8
|
+
chapter: ManifestItem;
|
|
9
|
+
chunk?: number;
|
|
10
|
+
xml: ParsedXml;
|
|
11
|
+
sentenceRanges: SentenceRange[];
|
|
12
|
+
wordRanges: WordRange[][];
|
|
13
|
+
startOffset: number;
|
|
14
|
+
endOffset: number;
|
|
15
|
+
};
|
|
16
|
+
interface AudioFileContext {
|
|
17
|
+
start: number;
|
|
18
|
+
end: number;
|
|
19
|
+
filepath: string;
|
|
20
|
+
}
|
|
21
|
+
interface ChapterReport {
|
|
22
|
+
href: string;
|
|
23
|
+
spineIndex: number;
|
|
24
|
+
chunk: number;
|
|
25
|
+
score: number;
|
|
26
|
+
transcriptionOffset: number;
|
|
27
|
+
endTranscriptionOffset: number;
|
|
28
|
+
firstMatchedSentenceId: number;
|
|
29
|
+
firstMatchedSentenceContext: {
|
|
30
|
+
prevSentence: string | null;
|
|
31
|
+
matchedSentence: string;
|
|
32
|
+
nextSentence: string | null;
|
|
33
|
+
};
|
|
34
|
+
lastMatchedSentenceId: number;
|
|
35
|
+
lastMatchedSentenceContext: {
|
|
36
|
+
prevSentence: string | null;
|
|
37
|
+
matchedSentence: string;
|
|
38
|
+
nextSentence: string | null;
|
|
39
|
+
};
|
|
40
|
+
chapterSentenceCount: number;
|
|
41
|
+
alignedSentenceCount: number;
|
|
42
|
+
audioFiles: AudioFileContext[];
|
|
43
|
+
}
|
|
44
|
+
type UnalignedChapterReason = "too-short" | "not-found" | "is-nav" | "no-text";
|
|
45
|
+
interface UnalignedChapterReport {
|
|
46
|
+
href: string;
|
|
47
|
+
reason: Exclude<UnalignedChapterReason, "not-found">;
|
|
48
|
+
}
|
|
49
|
+
interface UnalignedNotFoundChapterReport {
|
|
50
|
+
href: string;
|
|
51
|
+
spineIndex: number;
|
|
52
|
+
score: number;
|
|
53
|
+
chapterSentenceCount: number;
|
|
54
|
+
reason: "not-found";
|
|
55
|
+
start: string;
|
|
56
|
+
end: string;
|
|
57
|
+
}
|
|
58
|
+
interface AudioFileReport {
|
|
59
|
+
filepath: string;
|
|
60
|
+
matchedRanges: {
|
|
61
|
+
start: number;
|
|
62
|
+
end: number;
|
|
63
|
+
}[];
|
|
64
|
+
duration: number;
|
|
65
|
+
alignedDuration: number;
|
|
66
|
+
}
|
|
67
|
+
interface UnalignedAudioFileReport {
|
|
68
|
+
filepath: string;
|
|
69
|
+
}
|
|
70
|
+
interface Report {
|
|
71
|
+
chapters: ChapterReport[];
|
|
72
|
+
unalignedChapters: (UnalignedChapterReport | UnalignedNotFoundChapterReport)[];
|
|
73
|
+
audioFiles: AudioFileReport[];
|
|
74
|
+
unalignedAudioFiles: UnalignedAudioFileReport[];
|
|
75
|
+
}
|
|
76
|
+
declare function buildAudioFileContexts(sentenceRanges: SentenceRange[]): AudioFileContext[];
|
|
77
|
+
declare function updateAudioFileReports(report: Report, audioFiles: AudioFileContext[], audioFileDurations: Record<string, number>): void;
|
|
78
|
+
|
|
79
|
+
export { type AlignedChapter, type AudioFileContext, type AudioFileReport, type ChapterReport, type Report, type UnalignedAudioFileReport, type UnalignedChapterReason, type UnalignedChapterReport, type UnalignedNotFoundChapterReport, buildAudioFileContexts, updateAudioFileReports };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import "../../chunk-BIEQXUOY.js";
|
|
2
|
+
function buildAudioFileContexts(sentenceRanges) {
|
|
3
|
+
return sentenceRanges.reduce((acc, range) => {
|
|
4
|
+
const existing = acc.find((context) => context.filepath === range.audiofile);
|
|
5
|
+
if (existing) {
|
|
6
|
+
existing.end = range.end;
|
|
7
|
+
return acc;
|
|
8
|
+
}
|
|
9
|
+
acc.push({
|
|
10
|
+
filepath: range.audiofile,
|
|
11
|
+
start: range.start,
|
|
12
|
+
end: range.end
|
|
13
|
+
});
|
|
14
|
+
return acc;
|
|
15
|
+
}, []);
|
|
16
|
+
}
|
|
17
|
+
function updateAudioFileReports(report, audioFiles, audioFileDurations) {
|
|
18
|
+
for (const audioFile of audioFiles) {
|
|
19
|
+
const existing = report.audioFiles.find(
|
|
20
|
+
({ filepath }) => audioFile.filepath === filepath
|
|
21
|
+
);
|
|
22
|
+
if (existing) {
|
|
23
|
+
existing.matchedRanges.push({
|
|
24
|
+
start: audioFile.start,
|
|
25
|
+
end: audioFile.end
|
|
26
|
+
});
|
|
27
|
+
existing.matchedRanges.sort((a, b) => a.start - b.start);
|
|
28
|
+
existing.alignedDuration += audioFile.end - audioFile.start;
|
|
29
|
+
} else {
|
|
30
|
+
report.audioFiles.push({
|
|
31
|
+
alignedDuration: audioFile.end - audioFile.start,
|
|
32
|
+
duration: audioFileDurations[audioFile.filepath] ?? 0,
|
|
33
|
+
filepath: audioFile.filepath,
|
|
34
|
+
matchedRanges: [{ start: audioFile.start, end: audioFile.end }]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
buildAudioFileContexts,
|
|
41
|
+
updateAudioFileReports
|
|
42
|
+
};
|