@storyteller-platform/align 0.1.40 → 0.1.47
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/cli/bin.cjs +4 -5
- package/dist/cli/bin.js +4 -5
- package/dist/common/ffmpeg.cjs +7 -8
- package/dist/common/ffmpeg.js +7 -8
- package/dist/readium/index.cjs +33 -0
- package/dist/readium/index.d.cts +5 -0
- package/dist/readium/index.d.ts +5 -0
- package/dist/readium/index.js +11 -0
- package/dist/readium/manifest.cjs +53 -20
- package/dist/readium/manifest.d.cts +15 -4
- package/dist/readium/manifest.d.ts +15 -4
- package/dist/readium/manifest.js +52 -19
- package/dist/readium/manifest.types.cjs +16 -0
- package/dist/readium/manifest.types.d.cts +462 -0
- package/dist/readium/manifest.types.d.ts +462 -0
- package/dist/readium/manifest.types.js +0 -0
- package/dist/transcribe/transcribe.cjs +8 -3
- package/dist/transcribe/transcribe.js +8 -3
- package/package.json +23 -3
package/dist/cli/bin.cjs
CHANGED
|
@@ -359,13 +359,12 @@ async function main() {
|
|
|
359
359
|
if (parsed.textRef === "id-fragment") {
|
|
360
360
|
logger.info("Marking up EPUB...");
|
|
361
361
|
startProgressBar();
|
|
362
|
-
const markedup2 = parsed.markedup ?? (0, import_node_path.join)(os.tmpdir(), `stalign-markedup-${(0, import_node_crypto.randomUUID)()}.epub`);
|
|
363
362
|
if (!parsed.markedup) {
|
|
364
363
|
stack.defer(() => {
|
|
365
|
-
(0, import_node_fs.rmSync)(
|
|
364
|
+
(0, import_node_fs.rmSync)(markedup, { recursive: true, force: true });
|
|
366
365
|
});
|
|
367
366
|
}
|
|
368
|
-
const markupTiming = await (0, import_markup.markup)(input,
|
|
367
|
+
const markupTiming = await (0, import_markup.markup)(input, markedup, {
|
|
369
368
|
granularity: parsed.granularity,
|
|
370
369
|
primaryLocale,
|
|
371
370
|
logger,
|
|
@@ -376,12 +375,12 @@ async function main() {
|
|
|
376
375
|
}
|
|
377
376
|
});
|
|
378
377
|
resetProgressBar();
|
|
379
|
-
logger.info(`Markup complete, marked up EPUB saved to ${
|
|
378
|
+
logger.info(`Markup complete, marked up EPUB saved to ${markedup}.`);
|
|
380
379
|
if (parsed.time) {
|
|
381
380
|
markupTiming.print();
|
|
382
381
|
}
|
|
383
382
|
} else {
|
|
384
|
-
logger.info("Skipping markup, text-
|
|
383
|
+
logger.info("Skipping markup, text-ref set to text-fragment");
|
|
385
384
|
}
|
|
386
385
|
logger.info("Aligning EPUB with audiobook...");
|
|
387
386
|
startProgressBar();
|
package/dist/cli/bin.js
CHANGED
|
@@ -312,13 +312,12 @@ async function main() {
|
|
|
312
312
|
if (parsed.textRef === "id-fragment") {
|
|
313
313
|
logger.info("Marking up EPUB...");
|
|
314
314
|
startProgressBar();
|
|
315
|
-
const markedup2 = parsed.markedup ?? join(os.tmpdir(), `stalign-markedup-${randomUUID()}.epub`);
|
|
316
315
|
if (!parsed.markedup) {
|
|
317
316
|
stack.defer(() => {
|
|
318
|
-
rmSync(
|
|
317
|
+
rmSync(markedup, { recursive: true, force: true });
|
|
319
318
|
});
|
|
320
319
|
}
|
|
321
|
-
const markupTiming = await markup(input,
|
|
320
|
+
const markupTiming = await markup(input, markedup, {
|
|
322
321
|
granularity: parsed.granularity,
|
|
323
322
|
primaryLocale,
|
|
324
323
|
logger,
|
|
@@ -329,12 +328,12 @@ async function main() {
|
|
|
329
328
|
}
|
|
330
329
|
});
|
|
331
330
|
resetProgressBar();
|
|
332
|
-
logger.info(`Markup complete, marked up EPUB saved to ${
|
|
331
|
+
logger.info(`Markup complete, marked up EPUB saved to ${markedup}.`);
|
|
333
332
|
if (parsed.time) {
|
|
334
333
|
markupTiming.print();
|
|
335
334
|
}
|
|
336
335
|
} else {
|
|
337
|
-
logger.info("Skipping markup, text-
|
|
336
|
+
logger.info("Skipping markup, text-ref set to text-fragment");
|
|
338
337
|
}
|
|
339
338
|
logger.info("Aligning EPUB with audiobook...");
|
|
340
339
|
startProgressBar();
|
package/dist/common/ffmpeg.cjs
CHANGED
|
@@ -43,14 +43,11 @@ var import_mime = require("../process/mime.cjs");
|
|
|
43
43
|
var import_shell = require("./shell.cjs");
|
|
44
44
|
const execPromise = (0, import_node_util.promisify)(import_node_child_process.exec);
|
|
45
45
|
async function execCmd(command, logger, signal) {
|
|
46
|
-
let stdout = "";
|
|
47
|
-
let stderr = "";
|
|
48
46
|
try {
|
|
49
|
-
|
|
50
|
-
({ stdout, stderr } = await execPromise(command, {
|
|
47
|
+
const { stdout } = await execPromise(command, {
|
|
51
48
|
maxBuffer: 50 * 1024 * 1024,
|
|
52
49
|
signal: signal ?? void 0
|
|
53
|
-
})
|
|
50
|
+
});
|
|
54
51
|
return stdout;
|
|
55
52
|
} catch (error) {
|
|
56
53
|
if (error instanceof RangeError && error.message.includes("stdout maxBuffer length exceeded")) {
|
|
@@ -58,14 +55,16 @@ async function execCmd(command, logger, signal) {
|
|
|
58
55
|
"stdout maxBuffer length exceeded. This likely means that youre trying to process a very large file, and the ffmpeg process is running out of memory. Maybe check the image size of your cover art."
|
|
59
56
|
);
|
|
60
57
|
}
|
|
58
|
+
const execErr = error;
|
|
61
59
|
logger?.error(error);
|
|
62
|
-
logger?.info(stdout);
|
|
63
|
-
|
|
60
|
+
if (execErr.stdout) logger?.info(execErr.stdout);
|
|
61
|
+
const errorDetail = execErr.stderr || execErr.stdout || `Command failed: ${command}`;
|
|
62
|
+
throw new Error(errorDetail);
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
const getTrackInfo = (0, import_memoize.default)(async function getTrackInfo2(path, logger) {
|
|
67
66
|
const stdout = await execCmd(
|
|
68
|
-
`ffprobe -i ${(0, import_shell.quotePath)(path)} -show_format -of json`,
|
|
67
|
+
`ffprobe -v error -i ${(0, import_shell.quotePath)(path)} -show_format -of json`,
|
|
69
68
|
logger
|
|
70
69
|
);
|
|
71
70
|
const info = JSON.parse(stdout);
|
package/dist/common/ffmpeg.js
CHANGED
|
@@ -8,14 +8,11 @@ import { areSameType } from "../process/mime.js";
|
|
|
8
8
|
import { quotePath } from "./shell.js";
|
|
9
9
|
const execPromise = promisify(exec);
|
|
10
10
|
async function execCmd(command, logger, signal) {
|
|
11
|
-
let stdout = "";
|
|
12
|
-
let stderr = "";
|
|
13
11
|
try {
|
|
14
|
-
|
|
15
|
-
({ stdout, stderr } = await execPromise(command, {
|
|
12
|
+
const { stdout } = await execPromise(command, {
|
|
16
13
|
maxBuffer: 50 * 1024 * 1024,
|
|
17
14
|
signal: signal ?? void 0
|
|
18
|
-
})
|
|
15
|
+
});
|
|
19
16
|
return stdout;
|
|
20
17
|
} catch (error) {
|
|
21
18
|
if (error instanceof RangeError && error.message.includes("stdout maxBuffer length exceeded")) {
|
|
@@ -23,14 +20,16 @@ async function execCmd(command, logger, signal) {
|
|
|
23
20
|
"stdout maxBuffer length exceeded. This likely means that youre trying to process a very large file, and the ffmpeg process is running out of memory. Maybe check the image size of your cover art."
|
|
24
21
|
);
|
|
25
22
|
}
|
|
23
|
+
const execErr = error;
|
|
26
24
|
logger?.error(error);
|
|
27
|
-
logger?.info(stdout);
|
|
28
|
-
|
|
25
|
+
if (execErr.stdout) logger?.info(execErr.stdout);
|
|
26
|
+
const errorDetail = execErr.stderr || execErr.stdout || `Command failed: ${command}`;
|
|
27
|
+
throw new Error(errorDetail);
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
const getTrackInfo = memoize(async function getTrackInfo2(path, logger) {
|
|
32
31
|
const stdout = await execCmd(
|
|
33
|
-
`ffprobe -i ${quotePath(path)} -show_format -of json`,
|
|
32
|
+
`ffprobe -v error -i ${quotePath(path)} -show_format -of json`,
|
|
34
33
|
logger
|
|
35
34
|
);
|
|
36
35
|
const info = JSON.parse(stdout);
|
|
@@ -0,0 +1,33 @@
|
|
|
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 readium_exports = {};
|
|
20
|
+
__export(readium_exports, {
|
|
21
|
+
generateGuidedNavigationDocuments: () => import_guidedNavigation.generateGuidedNavigationDocuments,
|
|
22
|
+
generateGuidedNavigationManifest: () => import_guidedNavigation.generateGuidedNavigationManifest,
|
|
23
|
+
generateReadiumManifest: () => import_manifest.generateReadiumManifest
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(readium_exports);
|
|
26
|
+
var import_manifest = require("./manifest.cjs");
|
|
27
|
+
var import_guidedNavigation = require("./guidedNavigation.cjs");
|
|
28
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
29
|
+
0 && (module.exports = {
|
|
30
|
+
generateGuidedNavigationDocuments,
|
|
31
|
+
generateGuidedNavigationManifest,
|
|
32
|
+
generateReadiumManifest
|
|
33
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { generateReadiumManifest } from './manifest.cjs';
|
|
2
|
+
export { ReadiumWebPublicationManifest } from './manifest.types.cjs';
|
|
3
|
+
export { generateGuidedNavigationDocuments, generateGuidedNavigationManifest } from './guidedNavigation.cjs';
|
|
4
|
+
import '@storyteller-platform/epub';
|
|
5
|
+
import '@readium/shared';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { generateReadiumManifest } from './manifest.js';
|
|
2
|
+
export { ReadiumWebPublicationManifest } from './manifest.types.js';
|
|
3
|
+
export { generateGuidedNavigationDocuments, generateGuidedNavigationManifest } from './guidedNavigation.js';
|
|
4
|
+
import '@storyteller-platform/epub';
|
|
5
|
+
import '@readium/shared';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "../chunk-BIEQXUOY.js";
|
|
2
|
+
import { generateReadiumManifest } from "./manifest.js";
|
|
3
|
+
import {
|
|
4
|
+
generateGuidedNavigationDocuments,
|
|
5
|
+
generateGuidedNavigationManifest
|
|
6
|
+
} from "./guidedNavigation.js";
|
|
7
|
+
export {
|
|
8
|
+
generateGuidedNavigationDocuments,
|
|
9
|
+
generateGuidedNavigationManifest,
|
|
10
|
+
generateReadiumManifest
|
|
11
|
+
};
|
|
@@ -28,12 +28,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
var manifest_exports = {};
|
|
30
30
|
__export(manifest_exports, {
|
|
31
|
-
|
|
31
|
+
generateReadiumManifest: () => generateReadiumManifest
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(manifest_exports);
|
|
34
34
|
var import_shared = require("@readium/shared");
|
|
35
35
|
var import_smil_clockvalue = __toESM(require("smil-clockvalue"), 1);
|
|
36
|
-
|
|
36
|
+
const BYTES_PER_PAGE = 1024;
|
|
37
|
+
async function generateReadiumManifest(epub, options = {}) {
|
|
37
38
|
const primaryLocale = await epub.getLanguage() ?? new Intl.Locale("en-US");
|
|
38
39
|
const creators = await epub.getCreators();
|
|
39
40
|
const authors = creators.filter((contributor) => contributor.role === "aut").map((author) => createContributor(author, primaryLocale));
|
|
@@ -53,9 +54,13 @@ async function generateManifest(epub) {
|
|
|
53
54
|
const dcSubjects = await epub.getSubjects();
|
|
54
55
|
const subjects = dcSubjects.map((subject) => {
|
|
55
56
|
if (typeof subject === "string") {
|
|
56
|
-
return new import_shared.Subject({
|
|
57
|
+
return new import_shared.Subject({
|
|
58
|
+
// TODO: should this be in primary locale?
|
|
59
|
+
name: new import_shared.LocalizedString(subject)
|
|
60
|
+
});
|
|
57
61
|
}
|
|
58
62
|
return new import_shared.Subject({
|
|
63
|
+
// TODO: should this be in primary locale?
|
|
59
64
|
name: new import_shared.LocalizedString(subject.value),
|
|
60
65
|
scheme: subject.authority,
|
|
61
66
|
code: subject.term
|
|
@@ -83,9 +88,19 @@ async function generateManifest(epub) {
|
|
|
83
88
|
const dir = await epub.getBaseDirection();
|
|
84
89
|
const epubMetadata = await epub.getMetadata();
|
|
85
90
|
const vocab = await epub.getPackageVocabularyPrefixes();
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
91
|
+
let duration = void 0;
|
|
92
|
+
const refinesDurationMap = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const dur of epubMetadata) {
|
|
94
|
+
if (dur.properties["property"] !== "media:duration") continue;
|
|
95
|
+
if (!dur.properties["refines"]) {
|
|
96
|
+
duration = dur.value ? (0, import_smil_clockvalue.default)(dur.value) / 1e3 : void 0;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const value = dur.value ? (0, import_smil_clockvalue.default)(dur.value) / 1e3 : void 0;
|
|
100
|
+
if (value) {
|
|
101
|
+
refinesDurationMap.set(dur.properties["refines"], value);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
89
104
|
const otherMetadata = epubMetadata.filter(
|
|
90
105
|
(meta) => (meta.properties["property"]?.split(":")[0] ?? "") in vocab
|
|
91
106
|
).map((meta) => {
|
|
@@ -93,6 +108,9 @@ async function generateManifest(epub) {
|
|
|
93
108
|
const scheme = vocab[prefix];
|
|
94
109
|
return [`${scheme}#${property}`, meta.value];
|
|
95
110
|
});
|
|
111
|
+
const spine = await epub.getSpineItems();
|
|
112
|
+
const epubManifest = await epub.getManifest();
|
|
113
|
+
const numberOfPages = options.inferPageCount ? await inferPageCount(epub, spine, options.signal) : void 0;
|
|
96
114
|
const metadata = new import_shared.Metadata({
|
|
97
115
|
title: new import_shared.LocalizedString(title ?? ""),
|
|
98
116
|
conformsTo: [import_shared.Profile.EPUB],
|
|
@@ -121,12 +139,11 @@ async function generateManifest(epub) {
|
|
|
121
139
|
...dir !== "auto" && {
|
|
122
140
|
readingProgression: dir === "ltr" ? import_shared.ReadingProgression.ltr : import_shared.ReadingProgression.rtl
|
|
123
141
|
},
|
|
124
|
-
//
|
|
125
|
-
...duration !== void 0 && { duration
|
|
142
|
+
// it's seconds
|
|
143
|
+
...duration !== void 0 && { duration },
|
|
144
|
+
...numberOfPages !== void 0 && { numberOfPages },
|
|
126
145
|
otherMetadata: Object.fromEntries(otherMetadata)
|
|
127
146
|
});
|
|
128
|
-
const spine = await epub.getSpineItems();
|
|
129
|
-
const epubManifest = await epub.getManifest();
|
|
130
147
|
const readingOrder = await Promise.all(
|
|
131
148
|
spine.map(async (item) => {
|
|
132
149
|
const link = new import_shared.Link({
|
|
@@ -158,9 +175,9 @@ async function generateManifest(epub) {
|
|
|
158
175
|
rels.add("cover");
|
|
159
176
|
}
|
|
160
177
|
if (item.mediaType === "application/xhtml+xml") {
|
|
161
|
-
rels.add("
|
|
178
|
+
rels.add("contents");
|
|
162
179
|
}
|
|
163
|
-
const
|
|
180
|
+
const otherResourceMetadata = epubMetadata.filter((meta) => meta.properties["refines"] === `#${item.id}`).filter(
|
|
164
181
|
(meta) => (meta.properties["property"]?.split(":")[0] ?? "") in vocab
|
|
165
182
|
).map((meta) => {
|
|
166
183
|
const prefix = meta.properties["property"].split(":")[0];
|
|
@@ -171,16 +188,13 @@ async function generateManifest(epub) {
|
|
|
171
188
|
href: await epub.resolveHref(item.href, void 0, { toRoot: true }),
|
|
172
189
|
...item.mediaType && { type: item.mediaType },
|
|
173
190
|
rels,
|
|
174
|
-
properties: new import_shared.Properties(Object.fromEntries(
|
|
191
|
+
properties: new import_shared.Properties(Object.fromEntries(otherResourceMetadata))
|
|
175
192
|
});
|
|
176
193
|
if (!item.mediaOverlay) return link;
|
|
177
194
|
const mediaOverlayItem = epubManifest[item.id];
|
|
178
195
|
if (!mediaOverlayItem) return link;
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
);
|
|
182
|
-
if (!refinedBy?.value) return link;
|
|
183
|
-
const duration2 = (0, import_smil_clockvalue.default)(refinedBy.value);
|
|
196
|
+
const duration2 = refinesDurationMap.get(`#${item.mediaOverlay}`) || refinesDurationMap.get(`#${mediaOverlayItem.id}`);
|
|
197
|
+
if (!duration2) return link;
|
|
184
198
|
return new import_shared.Link({
|
|
185
199
|
href: link.href,
|
|
186
200
|
type: link.mediaType.string,
|
|
@@ -210,7 +224,7 @@ async function generateManifest(epub) {
|
|
|
210
224
|
})
|
|
211
225
|
]);
|
|
212
226
|
}
|
|
213
|
-
|
|
227
|
+
const manifest = new import_shared.Manifest({
|
|
214
228
|
context: ["https://readium.org/webpub-manifest/context.jsonld"],
|
|
215
229
|
metadata,
|
|
216
230
|
readingOrder: new import_shared.Links(readingOrder),
|
|
@@ -225,6 +239,25 @@ async function generateManifest(epub) {
|
|
|
225
239
|
...toc && { toc: new import_shared.Links(toc) },
|
|
226
240
|
subcollections
|
|
227
241
|
});
|
|
242
|
+
return manifest.serialize();
|
|
243
|
+
}
|
|
244
|
+
async function inferPageCount(epub, spine, signal) {
|
|
245
|
+
let total = 0;
|
|
246
|
+
for (const item of spine) {
|
|
247
|
+
if (signal?.aborted) {
|
|
248
|
+
throw new (signal.reason instanceof Error ? signal.reason.constructor : Error)(
|
|
249
|
+
signal.reason instanceof Error ? signal.reason.message : "Aborted while inferring page count"
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (item.mediaType !== "application/xhtml+xml") continue;
|
|
253
|
+
try {
|
|
254
|
+
const length = await epub.getItemArchiveLength(item.id);
|
|
255
|
+
total += Math.max(1, Math.ceil(length / BYTES_PER_PAGE));
|
|
256
|
+
} catch {
|
|
257
|
+
total += 1;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return total > 0 ? total : void 0;
|
|
228
261
|
}
|
|
229
262
|
function createContributor(dcCreator, primaryLocale) {
|
|
230
263
|
return new import_shared.Contributor({
|
|
@@ -256,5 +289,5 @@ function createLinkTree(navList) {
|
|
|
256
289
|
}
|
|
257
290
|
// Annotate the CommonJS export names for ESM import in node:
|
|
258
291
|
0 && (module.exports = {
|
|
259
|
-
|
|
292
|
+
generateReadiumManifest
|
|
260
293
|
});
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { EpubReader } from '@storyteller-platform/epub';
|
|
2
|
+
import { ReadiumWebPublicationManifest } from './manifest.types.cjs';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
interface ReadiumManifestOptions {
|
|
5
|
+
/**
|
|
6
|
+
* if true, infer a `numberOfPages`
|
|
7
|
+
* uses the compressed size of the entries, see {@link https://github.com/readium/architecture/issues/123}
|
|
8
|
+
*/
|
|
9
|
+
inferPageCount?: boolean;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* build a Readium WebPub manifest for an EPUB
|
|
14
|
+
*/
|
|
15
|
+
declare function generateReadiumManifest(epub: EpubReader, options?: ReadiumManifestOptions): Promise<ReadiumWebPublicationManifest>;
|
|
5
16
|
|
|
6
|
-
export {
|
|
17
|
+
export { type ReadiumManifestOptions, generateReadiumManifest };
|
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { EpubReader } from '@storyteller-platform/epub';
|
|
2
|
+
import { ReadiumWebPublicationManifest } from './manifest.types.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
interface ReadiumManifestOptions {
|
|
5
|
+
/**
|
|
6
|
+
* if true, infer a `numberOfPages`
|
|
7
|
+
* uses the compressed size of the entries, see {@link https://github.com/readium/architecture/issues/123}
|
|
8
|
+
*/
|
|
9
|
+
inferPageCount?: boolean;
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* build a Readium WebPub manifest for an EPUB
|
|
14
|
+
*/
|
|
15
|
+
declare function generateReadiumManifest(epub: EpubReader, options?: ReadiumManifestOptions): Promise<ReadiumWebPublicationManifest>;
|
|
5
16
|
|
|
6
|
-
export {
|
|
17
|
+
export { type ReadiumManifestOptions, generateReadiumManifest };
|
package/dist/readium/manifest.js
CHANGED
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
Subjects
|
|
17
17
|
} from "@readium/shared";
|
|
18
18
|
import clockvalue from "smil-clockvalue";
|
|
19
|
-
|
|
19
|
+
const BYTES_PER_PAGE = 1024;
|
|
20
|
+
async function generateReadiumManifest(epub, options = {}) {
|
|
20
21
|
const primaryLocale = await epub.getLanguage() ?? new Intl.Locale("en-US");
|
|
21
22
|
const creators = await epub.getCreators();
|
|
22
23
|
const authors = creators.filter((contributor) => contributor.role === "aut").map((author) => createContributor(author, primaryLocale));
|
|
@@ -36,9 +37,13 @@ async function generateManifest(epub) {
|
|
|
36
37
|
const dcSubjects = await epub.getSubjects();
|
|
37
38
|
const subjects = dcSubjects.map((subject) => {
|
|
38
39
|
if (typeof subject === "string") {
|
|
39
|
-
return new Subject({
|
|
40
|
+
return new Subject({
|
|
41
|
+
// TODO: should this be in primary locale?
|
|
42
|
+
name: new LocalizedString(subject)
|
|
43
|
+
});
|
|
40
44
|
}
|
|
41
45
|
return new Subject({
|
|
46
|
+
// TODO: should this be in primary locale?
|
|
42
47
|
name: new LocalizedString(subject.value),
|
|
43
48
|
scheme: subject.authority,
|
|
44
49
|
code: subject.term
|
|
@@ -66,9 +71,19 @@ async function generateManifest(epub) {
|
|
|
66
71
|
const dir = await epub.getBaseDirection();
|
|
67
72
|
const epubMetadata = await epub.getMetadata();
|
|
68
73
|
const vocab = await epub.getPackageVocabularyPrefixes();
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)
|
|
74
|
+
let duration = void 0;
|
|
75
|
+
const refinesDurationMap = /* @__PURE__ */ new Map();
|
|
76
|
+
for (const dur of epubMetadata) {
|
|
77
|
+
if (dur.properties["property"] !== "media:duration") continue;
|
|
78
|
+
if (!dur.properties["refines"]) {
|
|
79
|
+
duration = dur.value ? clockvalue(dur.value) / 1e3 : void 0;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const value = dur.value ? clockvalue(dur.value) / 1e3 : void 0;
|
|
83
|
+
if (value) {
|
|
84
|
+
refinesDurationMap.set(dur.properties["refines"], value);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
72
87
|
const otherMetadata = epubMetadata.filter(
|
|
73
88
|
(meta) => (meta.properties["property"]?.split(":")[0] ?? "") in vocab
|
|
74
89
|
).map((meta) => {
|
|
@@ -76,6 +91,9 @@ async function generateManifest(epub) {
|
|
|
76
91
|
const scheme = vocab[prefix];
|
|
77
92
|
return [`${scheme}#${property}`, meta.value];
|
|
78
93
|
});
|
|
94
|
+
const spine = await epub.getSpineItems();
|
|
95
|
+
const epubManifest = await epub.getManifest();
|
|
96
|
+
const numberOfPages = options.inferPageCount ? await inferPageCount(epub, spine, options.signal) : void 0;
|
|
79
97
|
const metadata = new Metadata({
|
|
80
98
|
title: new LocalizedString(title ?? ""),
|
|
81
99
|
conformsTo: [Profile.EPUB],
|
|
@@ -104,12 +122,11 @@ async function generateManifest(epub) {
|
|
|
104
122
|
...dir !== "auto" && {
|
|
105
123
|
readingProgression: dir === "ltr" ? ReadingProgression.ltr : ReadingProgression.rtl
|
|
106
124
|
},
|
|
107
|
-
//
|
|
108
|
-
...duration !== void 0 && { duration
|
|
125
|
+
// it's seconds
|
|
126
|
+
...duration !== void 0 && { duration },
|
|
127
|
+
...numberOfPages !== void 0 && { numberOfPages },
|
|
109
128
|
otherMetadata: Object.fromEntries(otherMetadata)
|
|
110
129
|
});
|
|
111
|
-
const spine = await epub.getSpineItems();
|
|
112
|
-
const epubManifest = await epub.getManifest();
|
|
113
130
|
const readingOrder = await Promise.all(
|
|
114
131
|
spine.map(async (item) => {
|
|
115
132
|
const link = new Link({
|
|
@@ -141,9 +158,9 @@ async function generateManifest(epub) {
|
|
|
141
158
|
rels.add("cover");
|
|
142
159
|
}
|
|
143
160
|
if (item.mediaType === "application/xhtml+xml") {
|
|
144
|
-
rels.add("
|
|
161
|
+
rels.add("contents");
|
|
145
162
|
}
|
|
146
|
-
const
|
|
163
|
+
const otherResourceMetadata = epubMetadata.filter((meta) => meta.properties["refines"] === `#${item.id}`).filter(
|
|
147
164
|
(meta) => (meta.properties["property"]?.split(":")[0] ?? "") in vocab
|
|
148
165
|
).map((meta) => {
|
|
149
166
|
const prefix = meta.properties["property"].split(":")[0];
|
|
@@ -154,16 +171,13 @@ async function generateManifest(epub) {
|
|
|
154
171
|
href: await epub.resolveHref(item.href, void 0, { toRoot: true }),
|
|
155
172
|
...item.mediaType && { type: item.mediaType },
|
|
156
173
|
rels,
|
|
157
|
-
properties: new Properties(Object.fromEntries(
|
|
174
|
+
properties: new Properties(Object.fromEntries(otherResourceMetadata))
|
|
158
175
|
});
|
|
159
176
|
if (!item.mediaOverlay) return link;
|
|
160
177
|
const mediaOverlayItem = epubManifest[item.id];
|
|
161
178
|
if (!mediaOverlayItem) return link;
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
);
|
|
165
|
-
if (!refinedBy?.value) return link;
|
|
166
|
-
const duration2 = clockvalue(refinedBy.value);
|
|
179
|
+
const duration2 = refinesDurationMap.get(`#${item.mediaOverlay}`) || refinesDurationMap.get(`#${mediaOverlayItem.id}`);
|
|
180
|
+
if (!duration2) return link;
|
|
167
181
|
return new Link({
|
|
168
182
|
href: link.href,
|
|
169
183
|
type: link.mediaType.string,
|
|
@@ -193,7 +207,7 @@ async function generateManifest(epub) {
|
|
|
193
207
|
})
|
|
194
208
|
]);
|
|
195
209
|
}
|
|
196
|
-
|
|
210
|
+
const manifest = new Manifest({
|
|
197
211
|
context: ["https://readium.org/webpub-manifest/context.jsonld"],
|
|
198
212
|
metadata,
|
|
199
213
|
readingOrder: new Links(readingOrder),
|
|
@@ -208,6 +222,25 @@ async function generateManifest(epub) {
|
|
|
208
222
|
...toc && { toc: new Links(toc) },
|
|
209
223
|
subcollections
|
|
210
224
|
});
|
|
225
|
+
return manifest.serialize();
|
|
226
|
+
}
|
|
227
|
+
async function inferPageCount(epub, spine, signal) {
|
|
228
|
+
let total = 0;
|
|
229
|
+
for (const item of spine) {
|
|
230
|
+
if (signal?.aborted) {
|
|
231
|
+
throw new (signal.reason instanceof Error ? signal.reason.constructor : Error)(
|
|
232
|
+
signal.reason instanceof Error ? signal.reason.message : "Aborted while inferring page count"
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (item.mediaType !== "application/xhtml+xml") continue;
|
|
236
|
+
try {
|
|
237
|
+
const length = await epub.getItemArchiveLength(item.id);
|
|
238
|
+
total += Math.max(1, Math.ceil(length / BYTES_PER_PAGE));
|
|
239
|
+
} catch {
|
|
240
|
+
total += 1;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return total > 0 ? total : void 0;
|
|
211
244
|
}
|
|
212
245
|
function createContributor(dcCreator, primaryLocale) {
|
|
213
246
|
return new Contributor({
|
|
@@ -238,5 +271,5 @@ function createLinkTree(navList) {
|
|
|
238
271
|
);
|
|
239
272
|
}
|
|
240
273
|
export {
|
|
241
|
-
|
|
274
|
+
generateReadiumManifest
|
|
242
275
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
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 __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var manifest_types_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(manifest_types_exports);
|