@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.
Files changed (118) hide show
  1. package/dist/align/align.cjs +38 -19
  2. package/dist/align/align.d.cts +12 -1
  3. package/dist/align/align.d.ts +12 -1
  4. package/dist/align/align.js +38 -19
  5. package/dist/align/ctc/Aligner.cjs +800 -0
  6. package/dist/align/ctc/Aligner.d.cts +56 -0
  7. package/dist/align/ctc/Aligner.d.ts +56 -0
  8. package/dist/align/ctc/Aligner.js +789 -0
  9. package/dist/align/ctc/align.cjs +189 -0
  10. package/dist/align/ctc/align.d.cts +14 -0
  11. package/dist/align/ctc/align.d.ts +14 -0
  12. package/dist/align/ctc/align.js +132 -0
  13. package/dist/{process/mime.cjs → align/ctc/emissions.cjs} +7 -20
  14. package/dist/align/ctc/emissions.d.cts +6 -0
  15. package/dist/align/ctc/emissions.d.ts +6 -0
  16. package/dist/align/ctc/emissions.js +7 -0
  17. package/dist/align/ctc/forcedAlign.cjs +273 -0
  18. package/dist/align/ctc/forcedAlign.d.cts +21 -0
  19. package/dist/align/ctc/forcedAlign.d.ts +21 -0
  20. package/dist/align/ctc/forcedAlign.js +248 -0
  21. package/dist/align/ctc/greedyDecode.cjs +61 -0
  22. package/dist/align/ctc/greedyDecode.d.cts +15 -0
  23. package/dist/align/ctc/greedyDecode.d.ts +15 -0
  24. package/dist/align/ctc/greedyDecode.js +37 -0
  25. package/dist/align/ctc/mediaOverlay.cjs +248 -0
  26. package/dist/align/ctc/mediaOverlay.d.cts +16 -0
  27. package/dist/align/ctc/mediaOverlay.d.ts +16 -0
  28. package/dist/align/ctc/mediaOverlay.js +224 -0
  29. package/dist/align/ctc/report.cjs +66 -0
  30. package/dist/align/ctc/report.d.cts +79 -0
  31. package/dist/align/ctc/report.d.ts +79 -0
  32. package/dist/align/ctc/report.js +42 -0
  33. package/dist/align/ctc/search.cjs +437 -0
  34. package/dist/align/ctc/search.d.cts +46 -0
  35. package/dist/align/ctc/search.d.ts +46 -0
  36. package/dist/align/ctc/search.js +412 -0
  37. package/dist/align/getSentenceRanges.cjs +9 -3
  38. package/dist/align/getSentenceRanges.d.cts +1 -0
  39. package/dist/align/getSentenceRanges.d.ts +1 -0
  40. package/dist/align/getSentenceRanges.js +9 -3
  41. package/dist/align/parse.cjs +17 -4
  42. package/dist/align/parse.d.cts +8 -4
  43. package/dist/align/parse.d.ts +8 -4
  44. package/dist/align/parse.js +19 -4
  45. package/dist/align/search.cjs +76 -23
  46. package/dist/align/search.d.cts +8 -4
  47. package/dist/align/search.d.ts +8 -4
  48. package/dist/align/search.js +76 -24
  49. package/dist/align/slugify.cjs +3 -2
  50. package/dist/align/slugify.d.cts +1 -1
  51. package/dist/align/slugify.d.ts +1 -1
  52. package/dist/align/slugify.js +3 -2
  53. package/dist/cli/bin.cjs +204 -114
  54. package/dist/cli/bin.js +194 -103
  55. package/dist/common/ffmpeg.cjs +10 -5
  56. package/dist/common/ffmpeg.js +10 -5
  57. package/dist/common/logging.cjs +33 -1
  58. package/dist/common/logging.js +33 -1
  59. package/dist/emit/emit.cjs +105 -0
  60. package/dist/emit/emit.d.cts +15 -0
  61. package/dist/emit/emit.d.ts +15 -0
  62. package/dist/emit/emit.js +86 -0
  63. package/dist/emit/fs.cjs +227 -0
  64. package/dist/emit/fs.d.cts +22 -0
  65. package/dist/emit/fs.d.ts +22 -0
  66. package/dist/emit/fs.js +162 -0
  67. package/dist/emit/parse.cjs +68 -0
  68. package/dist/emit/parse.d.cts +26 -0
  69. package/dist/emit/parse.d.ts +26 -0
  70. package/dist/emit/parse.js +58 -0
  71. package/dist/index.d.cts +1 -1
  72. package/dist/index.d.ts +1 -1
  73. package/dist/markup/model.cjs +0 -16
  74. package/dist/markup/model.d.cts +1 -1
  75. package/dist/markup/model.d.ts +1 -1
  76. package/dist/markup/model.js +0 -12
  77. package/dist/markup/parseDom.cjs +13 -14
  78. package/dist/markup/parseDom.d.cts +2 -2
  79. package/dist/markup/parseDom.d.ts +2 -2
  80. package/dist/markup/parseDom.js +13 -18
  81. package/dist/markup/resolvedPos.d.cts +1 -1
  82. package/dist/markup/resolvedPos.d.ts +1 -1
  83. package/dist/markup/segmentation.cjs +3 -1
  84. package/dist/markup/segmentation.js +3 -1
  85. package/dist/markup/serializeDom.cjs +1 -1
  86. package/dist/markup/serializeDom.d.cts +1 -1
  87. package/dist/markup/serializeDom.d.ts +1 -1
  88. package/dist/markup/serializeDom.js +1 -1
  89. package/dist/markup/transform.cjs +5 -2
  90. package/dist/markup/transform.d.cts +1 -1
  91. package/dist/markup/transform.d.ts +1 -1
  92. package/dist/markup/transform.js +5 -4
  93. package/dist/{model-CZ2mMHJO.d.cts → model-CeOMDDgt.d.cts} +3 -11
  94. package/dist/{model-CZ2mMHJO.d.ts → model-CeOMDDgt.d.ts} +3 -11
  95. package/dist/process/processAudiobook.cjs +12 -23
  96. package/dist/process/processAudiobook.js +14 -30
  97. package/dist/process/ranges.cjs +2 -2
  98. package/dist/process/ranges.js +2 -2
  99. package/dist/readium/guidedNavigation.cjs +8 -8
  100. package/dist/readium/guidedNavigation.js +8 -8
  101. package/dist/readium/manifest.cjs +5 -4
  102. package/dist/readium/manifest.js +5 -4
  103. package/dist/snapshot/ctc/snapshot.cjs +261 -0
  104. package/dist/snapshot/ctc/snapshot.d.cts +6 -0
  105. package/dist/snapshot/ctc/snapshot.d.ts +6 -0
  106. package/dist/snapshot/ctc/snapshot.js +200 -0
  107. package/dist/snapshot/parse.cjs +29 -16
  108. package/dist/snapshot/parse.d.cts +14 -10
  109. package/dist/snapshot/parse.d.ts +14 -10
  110. package/dist/snapshot/parse.js +32 -17
  111. package/dist/transcribe/parse.d.cts +6 -6
  112. package/dist/transcribe/parse.d.ts +6 -6
  113. package/dist/transcribe/transcribe.cjs +4 -2
  114. package/dist/transcribe/transcribe.js +4 -2
  115. package/package.json +6 -4
  116. package/dist/process/mime.d.cts +0 -3
  117. package/dist/process/mime.d.ts +0 -3
  118. 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
+ };