@prose-reader/streamer 1.20.0 → 1.22.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/dist/generators/resources/hooks/cssFixHook.d.ts +6 -0
- package/dist/generators/resources/hooks/defaultHook.d.ts +6 -0
- package/dist/generators/resources/hooks/types.d.ts +7 -0
- package/dist/generators/resources/index.d.ts +17 -0
- package/dist/index.d.ts +1 -1
- package/dist/prose-streamer.js +622 -371
- package/dist/prose-streamer.js.map +1 -1
- package/dist/prose-streamer.umd.cjs +723 -8
- package/dist/prose-streamer.umd.cjs.map +1 -1
- package/package.json +3 -3
- package/dist/generators/resources.d.ts +0 -9
package/dist/prose-streamer.js
CHANGED
|
@@ -1,405 +1,631 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { urlJoin, PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME, isXmlBasedMimeType, detectMimeTypeFromName } from "@prose-reader/shared";
|
|
2
|
+
import xmldoc from "xmldoc";
|
|
3
|
+
let enabled = false;
|
|
4
|
+
const Report = {
|
|
5
|
+
enable: (enable) => {
|
|
6
|
+
enabled = enable;
|
|
7
|
+
},
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
log: (...data) => {
|
|
10
|
+
if (enabled) {
|
|
11
|
+
console.log(`[prose-reader-streamer]`, ...data);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
warn: (...data) => {
|
|
16
|
+
if (enabled) {
|
|
17
|
+
console.warn(`[prose-reader-streamer]`, ...data);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
error: (...data) => {
|
|
22
|
+
console.error(...data);
|
|
23
|
+
},
|
|
24
|
+
time: (label) => {
|
|
25
|
+
if (enabled) {
|
|
26
|
+
console.time(`[prose-reader-streamer] [metric] ${label}`);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
timeEnd: (label) => {
|
|
30
|
+
if (enabled) {
|
|
31
|
+
console.timeEnd(`[prose-reader-streamer] [metric] ${label}`);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
metric: (performanceEntry, targetDuration = Infinity) => {
|
|
35
|
+
const duration = typeof performanceEntry === `number` ? performanceEntry : performanceEntry.duration;
|
|
36
|
+
if (enabled) {
|
|
37
|
+
if (performanceEntry.duration <= targetDuration) {
|
|
38
|
+
console.log(`[prose-reader-streamer] [metric] `, `${performanceEntry.name} took ${duration}ms`);
|
|
39
|
+
} else {
|
|
40
|
+
console.warn(
|
|
41
|
+
`[prose-reader-streamer] [metric] `,
|
|
42
|
+
`${performanceEntry.name} took ${performanceEntry.duration}ms which is above the ${targetDuration}ms target for this function`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
+
measurePerformance: (name, targetDuration = 10, functionToMeasure) => {
|
|
49
|
+
return (...args) => {
|
|
50
|
+
const t0 = performance.now();
|
|
51
|
+
const response = functionToMeasure(...args);
|
|
52
|
+
if (response && response.then) {
|
|
53
|
+
return response.then((res) => {
|
|
54
|
+
const t12 = performance.now();
|
|
55
|
+
Report.metric({ name, duration: t12 - t0 }, targetDuration);
|
|
56
|
+
return res;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const t1 = performance.now();
|
|
60
|
+
Report.metric({ name, duration: t1 - t0 }, targetDuration);
|
|
61
|
+
return response;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const cssFixHook = ({ archive, resourcePath }) => async (resource) => {
|
|
66
|
+
const file = Object.values(archive.files).find((file2) => file2.uri === resourcePath);
|
|
67
|
+
if (file == null ? void 0 : file.basename.endsWith(`.css`)) {
|
|
68
|
+
const bodyToParse = resource.body ?? await file.string();
|
|
69
|
+
const newBody = bodyToParse.replaceAll(`-webkit-writing-mode`, `writing-mode`);
|
|
70
|
+
return {
|
|
71
|
+
...resource,
|
|
72
|
+
body: newBody
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return resource;
|
|
76
|
+
};
|
|
77
|
+
const getArchiveOpfInfo = (archive) => {
|
|
78
|
+
const filesAsArray = Object.values(archive.files).filter((file2) => !file2.dir);
|
|
79
|
+
const file = filesAsArray.find((file2) => file2.uri.endsWith(`.opf`));
|
|
80
|
+
return {
|
|
81
|
+
data: file,
|
|
82
|
+
basePath: (file == null ? void 0 : file.uri.substring(0, file.uri.lastIndexOf(`/`))) || ``
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const extractNavChapter = (li, { opfBasePath, baseUrl }) => {
|
|
86
|
+
const chp = {
|
|
5
87
|
contents: [],
|
|
6
|
-
path:
|
|
7
|
-
href:
|
|
8
|
-
title:
|
|
88
|
+
path: ``,
|
|
89
|
+
href: ``,
|
|
90
|
+
title: ``
|
|
9
91
|
};
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
let
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
92
|
+
let contentNode = li.childNamed(`span`) || li.childNamed(`a`);
|
|
93
|
+
chp.title = (contentNode == null ? void 0 : contentNode.attr.title) || (contentNode == null ? void 0 : contentNode.val.trim()) || chp.title;
|
|
94
|
+
let node = contentNode == null ? void 0 : contentNode.name;
|
|
95
|
+
if (node !== `a`) {
|
|
96
|
+
contentNode = li.descendantWithPath(`${node}.a`);
|
|
97
|
+
if (contentNode) {
|
|
98
|
+
node = contentNode.name.toLowerCase();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (node === `a` && (contentNode == null ? void 0 : contentNode.attr.href)) {
|
|
102
|
+
chp.path = urlJoin(opfBasePath, contentNode.attr.href);
|
|
103
|
+
chp.href = urlJoin(baseUrl, opfBasePath, contentNode.attr.href);
|
|
18
104
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return e.descendantWithPath("body.nav.ol") ? t = (r = e.descendantWithPath("body.nav.ol")) == null ? void 0 : r.children : e.descendantWithPath("body.section.nav.ol") && (t = (o = e.descendantWithPath("body.section.nav.ol")) == null ? void 0 : o.children), t && t.length > 0 && t.filter((s) => s.name === "li").forEach((s) => a.push(O(s, { opfBasePath: i, baseUrl: n }))), a;
|
|
25
|
-
}, K = async (e, i, { opfBasePath: n, baseUrl: a }) => {
|
|
26
|
-
var r;
|
|
27
|
-
const t = (r = e.childNamed("manifest")) == null ? void 0 : r.childrenNamed("item").find((o) => o.attr.properties === "nav");
|
|
28
|
-
if (t) {
|
|
29
|
-
const o = Object.values(i.files).find((s) => s.uri.endsWith(t.attr.href || ""));
|
|
30
|
-
if (o) {
|
|
31
|
-
const s = new h.XmlDocument(await o.string());
|
|
32
|
-
return J(s, { opfBasePath: n, baseUrl: a });
|
|
105
|
+
const sublistNode = li.childNamed(`ol`);
|
|
106
|
+
if (sublistNode) {
|
|
107
|
+
const children = sublistNode.childrenNamed(`li`);
|
|
108
|
+
if (children && children.length > 0) {
|
|
109
|
+
chp.contents = children.map((child) => extractNavChapter(child, { opfBasePath, baseUrl }));
|
|
33
110
|
}
|
|
34
111
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
112
|
+
return chp;
|
|
113
|
+
};
|
|
114
|
+
const buildTOCFromNav = (doc, { opfBasePath, baseUrl }) => {
|
|
115
|
+
var _a, _b;
|
|
116
|
+
const toc = [];
|
|
117
|
+
let navDataChildren;
|
|
118
|
+
if (doc.descendantWithPath(`body.nav.ol`)) {
|
|
119
|
+
navDataChildren = (_a = doc.descendantWithPath(`body.nav.ol`)) == null ? void 0 : _a.children;
|
|
120
|
+
} else if (doc.descendantWithPath(`body.section.nav.ol`)) {
|
|
121
|
+
navDataChildren = (_b = doc.descendantWithPath(`body.section.nav.ol`)) == null ? void 0 : _b.children;
|
|
122
|
+
}
|
|
123
|
+
if (navDataChildren && navDataChildren.length > 0) {
|
|
124
|
+
navDataChildren.filter((li) => li.name === `li`).forEach((li) => toc.push(extractNavChapter(li, { opfBasePath, baseUrl })));
|
|
125
|
+
}
|
|
126
|
+
return toc;
|
|
127
|
+
};
|
|
128
|
+
const parseTocFromNavPath = async (opfXmlDoc, archive, { opfBasePath, baseUrl }) => {
|
|
129
|
+
var _a;
|
|
130
|
+
const navItem = (_a = opfXmlDoc.childNamed(`manifest`)) == null ? void 0 : _a.childrenNamed(`item`).find((child) => child.attr.properties === `nav`);
|
|
131
|
+
if (navItem) {
|
|
132
|
+
const tocFile = Object.values(archive.files).find((item) => item.uri.endsWith(navItem.attr.href || ``));
|
|
133
|
+
if (tocFile) {
|
|
134
|
+
const doc = new xmldoc.XmlDocument(await tocFile.string());
|
|
135
|
+
return buildTOCFromNav(doc, { opfBasePath, baseUrl });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const mapNcxChapter = (point, { opfBasePath, baseUrl, prefix }) => {
|
|
140
|
+
var _a, _b;
|
|
141
|
+
const src = ((_a = point == null ? void 0 : point.childNamed(`${prefix}content`)) == null ? void 0 : _a.attr.src) || ``;
|
|
142
|
+
const out = {
|
|
143
|
+
title: ((_b = point == null ? void 0 : point.descendantWithPath(`${prefix}navLabel.${prefix}text`)) == null ? void 0 : _b.val) || ``,
|
|
144
|
+
path: urlJoin(opfBasePath, src),
|
|
145
|
+
href: urlJoin(baseUrl, opfBasePath, src),
|
|
41
146
|
contents: []
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
147
|
+
};
|
|
148
|
+
const children = point.childrenNamed(`${prefix}navPoint`);
|
|
149
|
+
if (children && children.length > 0) {
|
|
150
|
+
out.contents = children.map((pt) => mapNcxChapter(pt, { opfBasePath, baseUrl, prefix }));
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
};
|
|
154
|
+
const buildTOCFromNCX = (ncxData, { opfBasePath, baseUrl }) => {
|
|
155
|
+
var _a;
|
|
156
|
+
const toc = [];
|
|
157
|
+
const rootTagName = ncxData.name;
|
|
158
|
+
let prefix = ``;
|
|
159
|
+
if (rootTagName.indexOf(`:`) !== -1) {
|
|
160
|
+
prefix = rootTagName.split(`:`)[0] + `:`;
|
|
161
|
+
}
|
|
162
|
+
(_a = ncxData.childNamed(`${prefix}navMap`)) == null ? void 0 : _a.childrenNamed(`${prefix}navPoint`).forEach((point) => toc.push(mapNcxChapter(point, { opfBasePath, baseUrl, prefix })));
|
|
163
|
+
return toc;
|
|
164
|
+
};
|
|
165
|
+
const parseTocFromNcx = async ({
|
|
166
|
+
opfData,
|
|
167
|
+
opfBasePath,
|
|
168
|
+
baseUrl,
|
|
169
|
+
archive
|
|
54
170
|
}) => {
|
|
55
|
-
var
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
171
|
+
var _a;
|
|
172
|
+
const spine = opfData.childNamed(`spine`);
|
|
173
|
+
const ncxId = spine && spine.attr.toc;
|
|
174
|
+
if (ncxId) {
|
|
175
|
+
const ncxItem = (_a = opfData.childNamed(`manifest`)) == null ? void 0 : _a.childrenNamed(`item`).find((item) => item.attr.id === ncxId);
|
|
176
|
+
if (ncxItem) {
|
|
177
|
+
const ncxPath = `${opfBasePath}${opfBasePath === `` ? `` : `/`}${ncxItem.attr.href}`;
|
|
178
|
+
const file = Object.values(archive.files).find((item) => item.uri.endsWith(ncxPath));
|
|
179
|
+
if (file) {
|
|
180
|
+
const ncxData = new xmldoc.XmlDocument(await file.string());
|
|
181
|
+
return buildTOCFromNCX(ncxData, { opfBasePath, baseUrl });
|
|
64
182
|
}
|
|
65
183
|
}
|
|
66
184
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
185
|
+
};
|
|
186
|
+
const parseToc = async (opfXmlDoc, archive, { baseUrl }) => {
|
|
187
|
+
const { basePath: opfBasePath } = getArchiveOpfInfo(archive) || {};
|
|
188
|
+
const tocFromNcx = await parseTocFromNcx({
|
|
189
|
+
opfData: opfXmlDoc,
|
|
190
|
+
opfBasePath,
|
|
191
|
+
archive,
|
|
192
|
+
baseUrl
|
|
73
193
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
194
|
+
if (tocFromNcx) {
|
|
195
|
+
return tocFromNcx;
|
|
196
|
+
}
|
|
197
|
+
return await parseTocFromNavPath(opfXmlDoc, archive, { opfBasePath, baseUrl });
|
|
198
|
+
};
|
|
199
|
+
const extractKoboInformationFromArchive = async (archive) => {
|
|
200
|
+
const koboInformation = {
|
|
77
201
|
renditionLayout: void 0
|
|
78
202
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
var
|
|
82
|
-
if (
|
|
83
|
-
const
|
|
84
|
-
|
|
203
|
+
await Promise.all(
|
|
204
|
+
archive.files.map(async (file) => {
|
|
205
|
+
var _a, _b;
|
|
206
|
+
if (file.uri.endsWith(`com.kobobooks.display-options.xml`)) {
|
|
207
|
+
const opfXmlDoc = new xmldoc.XmlDocument(await file.string());
|
|
208
|
+
const optionElement = (_a = opfXmlDoc.childNamed(`platform`)) == null ? void 0 : _a.childNamed(`option`);
|
|
209
|
+
if (((_b = optionElement == null ? void 0 : optionElement.attr) == null ? void 0 : _b.name) === `fixed-layout` && optionElement.val === `true`) {
|
|
210
|
+
koboInformation.renditionLayout = `pre-paginated`;
|
|
211
|
+
}
|
|
85
212
|
}
|
|
86
213
|
})
|
|
87
|
-
)
|
|
214
|
+
);
|
|
215
|
+
return koboInformation;
|
|
88
216
|
};
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
},
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
-
log: (...e) => {
|
|
96
|
-
b && console.log("[prose-reader-streamer]", ...e);
|
|
97
|
-
},
|
|
98
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
-
warn: (...e) => {
|
|
100
|
-
b && console.warn("[prose-reader-streamer]", ...e);
|
|
101
|
-
},
|
|
102
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
-
error: (...e) => {
|
|
104
|
-
console.error(...e);
|
|
105
|
-
},
|
|
106
|
-
time: (e) => {
|
|
107
|
-
b && console.time(`[prose-reader-streamer] [metric] ${e}`);
|
|
108
|
-
},
|
|
109
|
-
timeEnd: (e) => {
|
|
110
|
-
b && console.timeEnd(`[prose-reader-streamer] [metric] ${e}`);
|
|
111
|
-
},
|
|
112
|
-
metric: (e, i = 1 / 0) => {
|
|
113
|
-
const n = typeof e == "number" ? e : e.duration;
|
|
114
|
-
b && (e.duration <= i ? console.log("[prose-reader-streamer] [metric] ", `${e.name} took ${n}ms`) : console.warn(
|
|
115
|
-
"[prose-reader-streamer] [metric] ",
|
|
116
|
-
`${e.name} took ${e.duration}ms which is above the ${i}ms target for this function`
|
|
117
|
-
));
|
|
118
|
-
},
|
|
119
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
-
measurePerformance: (e, i = 10, n) => (...a) => {
|
|
121
|
-
const t = performance.now(), r = n(...a);
|
|
122
|
-
if (r && r.then)
|
|
123
|
-
return r.then((s) => {
|
|
124
|
-
const d = performance.now();
|
|
125
|
-
return y.metric({ name: e, duration: d - t }, i), s;
|
|
126
|
-
});
|
|
127
|
-
const o = performance.now();
|
|
128
|
-
return y.metric({ name: e, duration: o - t }, i), r;
|
|
129
|
-
}
|
|
130
|
-
}, $ = (e) => {
|
|
131
|
-
const n = Object.values(e.files).filter((a) => !a.dir).find((a) => a.uri.endsWith(".opf"));
|
|
132
|
-
return {
|
|
133
|
-
data: n,
|
|
134
|
-
basePath: (n == null ? void 0 : n.uri.substring(0, n.uri.lastIndexOf("/"))) || ""
|
|
135
|
-
};
|
|
136
|
-
}, j = async ({ archive: e }) => {
|
|
137
|
-
const { data: i, basePath: n } = $(e) || {}, a = await (i == null ? void 0 : i.string());
|
|
138
|
-
if (!a)
|
|
217
|
+
const getSpineItemFilesFromArchive = async ({ archive }) => {
|
|
218
|
+
const { data: opsFile, basePath: opfBasePath } = getArchiveOpfInfo(archive) || {};
|
|
219
|
+
const data = await (opsFile == null ? void 0 : opsFile.string());
|
|
220
|
+
if (!data)
|
|
139
221
|
return [];
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
222
|
+
const _opfXmlDoc = new xmldoc.XmlDocument(data);
|
|
223
|
+
const manifestElm = _opfXmlDoc.childNamed(`manifest`);
|
|
224
|
+
const spineElm = _opfXmlDoc.childNamed(`spine`);
|
|
225
|
+
const spineItemIds = spineElm == null ? void 0 : spineElm.childrenNamed(`itemref`).map((item) => item.attr.idref);
|
|
226
|
+
const manifestItemsFromSpine = (manifestElm == null ? void 0 : manifestElm.childrenNamed(`item`).filter((item) => spineItemIds.includes(item.attr.id || ``))) || [];
|
|
227
|
+
const archiveSpineItems = archive.files.filter((file) => {
|
|
228
|
+
return manifestItemsFromSpine.find((item) => {
|
|
229
|
+
if (!opfBasePath)
|
|
230
|
+
return `${item.attr.href}` === file.uri;
|
|
231
|
+
return `${opfBasePath}/${item.attr.href}` === file.uri;
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
return archiveSpineItems;
|
|
235
|
+
};
|
|
236
|
+
const getItemsFromDoc = (doc) => {
|
|
237
|
+
var _a;
|
|
238
|
+
const manifestElm = doc.childNamed(`manifest`);
|
|
239
|
+
return ((_a = manifestElm == null ? void 0 : manifestElm.childrenNamed(`item`)) == null ? void 0 : _a.map((el) => ({
|
|
240
|
+
href: el.attr.href || ``,
|
|
241
|
+
id: el.attr.id || ``,
|
|
242
|
+
mediaType: el.attr[`media-type`]
|
|
149
243
|
}))) || [];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
244
|
+
};
|
|
245
|
+
const epubHook = ({ archive, baseUrl }) => async (manifest) => {
|
|
246
|
+
var _a;
|
|
247
|
+
const { data: opsFile, basePath: opfBasePath } = getArchiveOpfInfo(archive) || {};
|
|
248
|
+
const koboInformation = await extractKoboInformationFromArchive(archive);
|
|
249
|
+
if (!opsFile) {
|
|
250
|
+
return manifest;
|
|
251
|
+
}
|
|
252
|
+
const data = await opsFile.string();
|
|
253
|
+
Report.log(data, koboInformation);
|
|
254
|
+
const opfXmlDoc = new xmldoc.XmlDocument(data);
|
|
255
|
+
const toc = await parseToc(opfXmlDoc, archive, { baseUrl }) || [];
|
|
256
|
+
const metadataElm = opfXmlDoc.childNamed(`metadata`);
|
|
257
|
+
const manifestElm = opfXmlDoc.childNamed(`manifest`);
|
|
258
|
+
const spineElm = opfXmlDoc.childNamed(`spine`);
|
|
259
|
+
const guideElm = opfXmlDoc.childNamed(`guide`);
|
|
260
|
+
const titleElm = metadataElm == null ? void 0 : metadataElm.childNamed(`dc:title`);
|
|
261
|
+
const metaElmChildren = (metadataElm == null ? void 0 : metadataElm.childrenNamed(`meta`)) || [];
|
|
262
|
+
const metaElmWithRendition = metaElmChildren.find((meta) => meta.attr.property === `rendition:layout`);
|
|
263
|
+
const metaElmWithRenditionFlow = metaElmChildren.find((meta) => meta.attr.property === `rendition:flow`);
|
|
264
|
+
const metaElmWithRenditionSpread = metaElmChildren.find((meta) => meta.attr.property === `rendition:spread`);
|
|
265
|
+
const publisherRenditionLayout = metaElmWithRendition == null ? void 0 : metaElmWithRendition.val;
|
|
266
|
+
const publisherRenditionFlow = metaElmWithRenditionFlow == null ? void 0 : metaElmWithRenditionFlow.val;
|
|
267
|
+
const renditionSpread = metaElmWithRenditionSpread == null ? void 0 : metaElmWithRenditionSpread.val;
|
|
268
|
+
const title = (titleElm == null ? void 0 : titleElm.val) || ((_a = archive.files.find(({ dir }) => dir)) == null ? void 0 : _a.basename) || ``;
|
|
269
|
+
const pageProgressionDirection = spineElm == null ? void 0 : spineElm.attr[`page-progression-direction`];
|
|
270
|
+
const archiveSpineItems = await getSpineItemFilesFromArchive({ archive });
|
|
271
|
+
const totalSize = archiveSpineItems.reduce((size, file) => file.size + size, 0);
|
|
158
272
|
return {
|
|
159
|
-
filename:
|
|
273
|
+
filename: archive.filename,
|
|
160
274
|
nav: {
|
|
161
|
-
toc
|
|
275
|
+
toc
|
|
162
276
|
},
|
|
163
|
-
renditionLayout:
|
|
164
|
-
renditionFlow:
|
|
165
|
-
renditionSpread
|
|
166
|
-
title
|
|
167
|
-
readingDirection:
|
|
168
|
-
spineItems: (
|
|
169
|
-
var
|
|
170
|
-
const
|
|
277
|
+
renditionLayout: publisherRenditionLayout || koboInformation.renditionLayout || `reflowable`,
|
|
278
|
+
renditionFlow: publisherRenditionFlow || `auto`,
|
|
279
|
+
renditionSpread,
|
|
280
|
+
title,
|
|
281
|
+
readingDirection: pageProgressionDirection || `ltr`,
|
|
282
|
+
spineItems: (spineElm == null ? void 0 : spineElm.childrenNamed(`itemref`).map((itemrefElm) => {
|
|
283
|
+
var _a2, _b, _c;
|
|
284
|
+
const manifestItem = manifestElm == null ? void 0 : manifestElm.childrenNamed(`item`).find((item) => item.attr.id === (itemrefElm == null ? void 0 : itemrefElm.attr.idref));
|
|
285
|
+
const href = (manifestItem == null ? void 0 : manifestItem.attr.href) || ``;
|
|
286
|
+
const properties = ((_a2 = itemrefElm == null ? void 0 : itemrefElm.attr.properties) == null ? void 0 : _a2.split(` `)) || [];
|
|
287
|
+
const itemSize = ((_b = archive.files.find((file) => file.uri.endsWith(href))) == null ? void 0 : _b.size) || 0;
|
|
288
|
+
const hrefBaseUri = baseUrl ?? "";
|
|
171
289
|
return {
|
|
172
|
-
id: (
|
|
173
|
-
href: (
|
|
174
|
-
renditionLayout:
|
|
175
|
-
...
|
|
176
|
-
renditionLayout:
|
|
290
|
+
id: (manifestItem == null ? void 0 : manifestItem.attr.id) || ``,
|
|
291
|
+
href: ((_c = manifestItem == null ? void 0 : manifestItem.attr.href) == null ? void 0 : _c.startsWith(`https://`)) ? manifestItem == null ? void 0 : manifestItem.attr.href : opfBasePath ? `${hrefBaseUri}${opfBasePath}/${manifestItem == null ? void 0 : manifestItem.attr.href}` : `${hrefBaseUri}${manifestItem == null ? void 0 : manifestItem.attr.href}`,
|
|
292
|
+
renditionLayout: publisherRenditionLayout || `reflowable`,
|
|
293
|
+
...properties.find((property) => property === `rendition:layout-reflowable`) && {
|
|
294
|
+
renditionLayout: `reflowable`
|
|
177
295
|
},
|
|
178
|
-
progressionWeight:
|
|
179
|
-
pageSpreadLeft:
|
|
180
|
-
pageSpreadRight:
|
|
296
|
+
progressionWeight: itemSize / totalSize,
|
|
297
|
+
pageSpreadLeft: properties.some((property) => property === `page-spread-left`) || void 0,
|
|
298
|
+
pageSpreadRight: properties.some((property) => property === `page-spread-right`) || void 0,
|
|
181
299
|
// size: itemSize
|
|
182
|
-
mediaType:
|
|
300
|
+
mediaType: manifestItem == null ? void 0 : manifestItem.attr[`media-type`]
|
|
183
301
|
};
|
|
184
302
|
})) || [],
|
|
185
|
-
items:
|
|
186
|
-
guide:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
303
|
+
items: getItemsFromDoc(opfXmlDoc),
|
|
304
|
+
guide: guideElm == null ? void 0 : guideElm.childrenNamed(`reference`).map((elm) => {
|
|
305
|
+
return {
|
|
306
|
+
href: elm.attr.href || ``,
|
|
307
|
+
title: elm.attr.title || ``,
|
|
308
|
+
type: elm.attr.type
|
|
309
|
+
};
|
|
310
|
+
})
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
const getMetadata = async (archive, resourcePath) => {
|
|
314
|
+
var _a, _b;
|
|
315
|
+
const opfInfo = getArchiveOpfInfo(archive);
|
|
316
|
+
const data = await ((_a = opfInfo.data) == null ? void 0 : _a.string());
|
|
317
|
+
if (data) {
|
|
318
|
+
const opfXmlDoc = new xmldoc.XmlDocument(data);
|
|
319
|
+
const items = getItemsFromDoc(opfXmlDoc);
|
|
320
|
+
return {
|
|
321
|
+
mediaType: (_b = items.find((item) => resourcePath.endsWith(item.href))) == null ? void 0 : _b.mediaType
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
mediaType: getContentTypeFromExtension(resourcePath)
|
|
191
326
|
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
327
|
+
};
|
|
328
|
+
const getContentTypeFromExtension = (uri) => {
|
|
329
|
+
if (uri.endsWith(`.css`)) {
|
|
330
|
+
return `text/css; charset=UTF-8`;
|
|
331
|
+
}
|
|
332
|
+
if (uri.endsWith(`.jpg`)) {
|
|
333
|
+
return `image/jpg`;
|
|
334
|
+
}
|
|
335
|
+
if (uri.endsWith(`.xhtml`)) {
|
|
336
|
+
return `application/xhtml+xml`;
|
|
337
|
+
}
|
|
338
|
+
if (uri.endsWith(`.mp4`)) {
|
|
339
|
+
return `video/mp4`;
|
|
340
|
+
}
|
|
341
|
+
if (uri.endsWith(`.svg`)) {
|
|
342
|
+
return `image/svg+xml`;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
const defaultHook$1 = ({ archive, resourcePath }) => async (resource) => {
|
|
346
|
+
const file = Object.values(archive.files).find((file2) => file2.uri === resourcePath);
|
|
347
|
+
if (!file)
|
|
348
|
+
return resource;
|
|
349
|
+
const metadata = await getMetadata(archive, resourcePath);
|
|
197
350
|
return {
|
|
198
|
-
|
|
351
|
+
...resource,
|
|
199
352
|
params: {
|
|
353
|
+
...resource.params,
|
|
354
|
+
status: 200,
|
|
200
355
|
headers: {
|
|
201
|
-
...
|
|
202
|
-
"Content-Type":
|
|
203
|
-
},
|
|
204
|
-
...n.encodingFormat && {
|
|
205
|
-
"Content-Type": n.encodingFormat
|
|
356
|
+
...(file == null ? void 0 : file.encodingFormat) && {
|
|
357
|
+
"Content-Type": file.encodingFormat
|
|
206
358
|
},
|
|
207
|
-
...
|
|
208
|
-
"Content-Type":
|
|
359
|
+
...metadata.mediaType && {
|
|
360
|
+
"Content-Type": metadata.mediaType
|
|
209
361
|
}
|
|
210
|
-
// 'Cache-Control': `no-cache, no-store, no-transform`
|
|
211
362
|
}
|
|
212
363
|
}
|
|
213
364
|
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
|
|
365
|
+
};
|
|
366
|
+
const generateResourceFromArchive = async (archive, resourcePath) => {
|
|
367
|
+
const file = Object.values(archive.files).find((file2) => file2.uri === resourcePath);
|
|
368
|
+
if (!file) {
|
|
369
|
+
throw new Error(`no file found`);
|
|
370
|
+
}
|
|
371
|
+
const defaultResource = {
|
|
372
|
+
params: {
|
|
373
|
+
status: 200
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
const hooks = [defaultHook$1({ archive, resourcePath }), cssFixHook({ archive, resourcePath })];
|
|
377
|
+
try {
|
|
378
|
+
const resource = await hooks.reduce(async (manifest, gen) => {
|
|
379
|
+
return await gen(await manifest);
|
|
380
|
+
}, Promise.resolve(defaultResource));
|
|
381
|
+
Report.log("Generated resource", resourcePath, resource);
|
|
219
382
|
return {
|
|
220
|
-
|
|
383
|
+
...resource,
|
|
384
|
+
body: resource.body || await file.blob()
|
|
221
385
|
};
|
|
386
|
+
} catch (e) {
|
|
387
|
+
Report.error(e);
|
|
388
|
+
throw e;
|
|
222
389
|
}
|
|
390
|
+
};
|
|
391
|
+
const generateResourceFromError = (error) => {
|
|
223
392
|
return {
|
|
224
|
-
|
|
393
|
+
body: `
|
|
394
|
+
<!DOCTYPE html>
|
|
395
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="en" lang="en">
|
|
396
|
+
<head>
|
|
397
|
+
<meta name="${PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME}" content="${String(error)}" />
|
|
398
|
+
</head>
|
|
399
|
+
<body>
|
|
400
|
+
<pre>${String(error)}</pre>
|
|
401
|
+
</body>
|
|
402
|
+
</html>
|
|
403
|
+
`,
|
|
404
|
+
params: {
|
|
405
|
+
status: 500,
|
|
406
|
+
headers: {
|
|
407
|
+
"Content-Type": "text/html;charset=UTF-8"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
225
410
|
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return "image/jpg";
|
|
231
|
-
if (e.endsWith(".xhtml"))
|
|
232
|
-
return "application/xhtml+xml";
|
|
233
|
-
if (e.endsWith(".mp4"))
|
|
234
|
-
return "video/mp4";
|
|
235
|
-
if (e.endsWith(".svg"))
|
|
236
|
-
return "image/svg+xml";
|
|
237
|
-
}, re = ({ archive: e, baseUrl: i }) => async () => {
|
|
238
|
-
var a;
|
|
239
|
-
const n = Object.values(e.files).filter((t) => !t.dir);
|
|
411
|
+
};
|
|
412
|
+
const defaultHook = ({ archive, baseUrl }) => async () => {
|
|
413
|
+
var _a;
|
|
414
|
+
const files = Object.values(archive.files).filter((file) => !file.dir);
|
|
240
415
|
return {
|
|
241
|
-
filename:
|
|
242
|
-
title: ((
|
|
243
|
-
renditionLayout:
|
|
244
|
-
renditionSpread:
|
|
245
|
-
readingDirection:
|
|
246
|
-
spineItems:
|
|
416
|
+
filename: archive.filename,
|
|
417
|
+
title: ((_a = archive.files.find(({ dir }) => dir)) == null ? void 0 : _a.basename.replace(/\/$/, ``)) || ``,
|
|
418
|
+
renditionLayout: `pre-paginated`,
|
|
419
|
+
renditionSpread: `auto`,
|
|
420
|
+
readingDirection: `ltr`,
|
|
421
|
+
spineItems: files.map((file, index) => ({
|
|
247
422
|
// some books such as cbz can have same basename inside different sub folder
|
|
248
423
|
// we need to make sure to have unique index
|
|
249
424
|
// /chap01/01.png, /chap02/01.png, etc
|
|
250
|
-
id: `${
|
|
251
|
-
href: encodeURI(`${
|
|
252
|
-
renditionLayout:
|
|
253
|
-
progressionWeight: 1 /
|
|
425
|
+
id: `${index}.${file.basename}`,
|
|
426
|
+
href: encodeURI(`${baseUrl}${file.uri}`),
|
|
427
|
+
renditionLayout: `pre-paginated`,
|
|
428
|
+
progressionWeight: 1 / files.length,
|
|
254
429
|
pageSpreadLeft: void 0,
|
|
255
430
|
pageSpreadRight: void 0,
|
|
256
|
-
mediaType:
|
|
431
|
+
mediaType: file.encodingFormat
|
|
257
432
|
})),
|
|
258
|
-
items:
|
|
259
|
-
id: `${
|
|
260
|
-
href: `${
|
|
433
|
+
items: files.map((file, index) => ({
|
|
434
|
+
id: `${index}.${file.basename}`,
|
|
435
|
+
href: `${baseUrl}${file.uri}`
|
|
261
436
|
}))
|
|
262
437
|
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
438
|
+
};
|
|
439
|
+
const comicInfoHook = ({ archive, baseUrl }) => async (manifest) => {
|
|
440
|
+
var _a;
|
|
441
|
+
const comicInfoFile = archive.files.find((file) => file.basename.toLowerCase() === `comicinfo.xml`);
|
|
442
|
+
if (!comicInfoFile) {
|
|
443
|
+
return manifest;
|
|
444
|
+
}
|
|
445
|
+
const content = await comicInfoFile.string();
|
|
446
|
+
const xmlDoc = new xmldoc.XmlDocument(content);
|
|
447
|
+
const mangaVal = ((_a = xmlDoc.childNamed(`Manga`)) == null ? void 0 : _a.val) || `unknown`;
|
|
269
448
|
return {
|
|
270
|
-
...
|
|
271
|
-
spineItems:
|
|
272
|
-
readingDirection:
|
|
449
|
+
...manifest,
|
|
450
|
+
spineItems: manifest.spineItems.filter((item) => item.id.toLowerCase() !== `comicinfo.xml`),
|
|
451
|
+
readingDirection: mangaVal === `YesAndRightToLeft` ? `rtl` : `ltr`
|
|
273
452
|
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
453
|
+
};
|
|
454
|
+
const hasDocMetaViewport = (doc) => {
|
|
455
|
+
var _a;
|
|
456
|
+
const metaElm = (_a = doc.descendantWithPath("head")) == null ? void 0 : _a.childrenNamed("meta").find((node) => node.attr.name === "viewport");
|
|
457
|
+
return !!(metaElm && metaElm.attr.name === "viewport");
|
|
458
|
+
};
|
|
459
|
+
const allFilesHaveViewportMeta = (files) => files.reduce(async (result, current) => {
|
|
460
|
+
const _result = await result;
|
|
461
|
+
if (!_result)
|
|
462
|
+
return false;
|
|
463
|
+
if (!isXmlBasedMimeType({
|
|
464
|
+
mimeType: current.encodingFormat,
|
|
465
|
+
uri: current.uri
|
|
466
|
+
})) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
const file = await current.string();
|
|
470
|
+
if (!file)
|
|
471
|
+
return false;
|
|
472
|
+
return hasDocMetaViewport(new xmldoc.XmlDocument(file));
|
|
473
|
+
}, Promise.resolve(true));
|
|
474
|
+
const epubOptimizerHook = ({ archive, baseUrl }) => async (manifest) => {
|
|
475
|
+
const bookIsFullReflowable = manifest.renditionLayout === "reflowable" && manifest.spineItems.every((item) => item.renditionLayout === "reflowable");
|
|
476
|
+
if (bookIsFullReflowable) {
|
|
477
|
+
const files = await getSpineItemFilesFromArchive({ archive });
|
|
478
|
+
const hasAllViewport = await allFilesHaveViewportMeta(files);
|
|
479
|
+
if (hasAllViewport) {
|
|
290
480
|
return {
|
|
291
|
-
...
|
|
292
|
-
spineItems:
|
|
293
|
-
...
|
|
481
|
+
...manifest,
|
|
482
|
+
spineItems: manifest.spineItems.map((item) => ({
|
|
483
|
+
...item,
|
|
294
484
|
renditionLayout: "pre-paginated"
|
|
295
485
|
})),
|
|
296
486
|
renditionLayout: "pre-paginated"
|
|
297
487
|
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return manifest;
|
|
491
|
+
};
|
|
492
|
+
const sortByTitleComparator = (a, b) => {
|
|
493
|
+
var _a;
|
|
494
|
+
const alist = a.split(/(\d+)/);
|
|
495
|
+
const blist = b.split(/(\d+)/);
|
|
496
|
+
for (let i = 0, len = alist.length; i < len; i++) {
|
|
497
|
+
if (alist[i] !== blist[i]) {
|
|
498
|
+
if ((_a = alist[i]) == null ? void 0 : _a.match(/\d/)) {
|
|
499
|
+
return +(alist[i] || ``) - +(blist[i] || ``);
|
|
500
|
+
} else {
|
|
501
|
+
return (alist[i] || ``).localeCompare(blist[i] || ``);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
298
504
|
}
|
|
299
|
-
return n;
|
|
300
|
-
}, L = (e, i) => {
|
|
301
|
-
var t;
|
|
302
|
-
const n = e.split(/(\d+)/), a = i.split(/(\d+)/);
|
|
303
|
-
for (let r = 0, o = n.length; r < o; r++)
|
|
304
|
-
if (n[r] !== a[r])
|
|
305
|
-
return (t = n[r]) != null && t.match(/\d/) ? +(n[r] || "") - +(a[r] || "") : (n[r] || "").localeCompare(a[r] || "");
|
|
306
505
|
return 1;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
506
|
+
};
|
|
507
|
+
const navigationFallbackHook = ({ archive, baseUrl }) => async (manifest) => {
|
|
508
|
+
if (manifest.nav)
|
|
509
|
+
return manifest;
|
|
510
|
+
const filesSortedByAlpha = [...archive.files].sort((a, b) => sortByTitleComparator(a.uri, b.uri));
|
|
511
|
+
const toc = Object.values(filesSortedByAlpha).reduce((acc, file) => {
|
|
512
|
+
const parts = file.uri.split("/");
|
|
513
|
+
const isFileUnderFolder = !file.dir && parts.length > 1;
|
|
514
|
+
if (isFileUnderFolder) {
|
|
515
|
+
parts.forEach((part, level) => {
|
|
516
|
+
const partIsFileName = level === parts.length - 1;
|
|
517
|
+
if (partIsFileName)
|
|
518
|
+
return;
|
|
519
|
+
const existingTocItem = acc.find(({ title }) => title === part);
|
|
520
|
+
if (existingTocItem)
|
|
521
|
+
;
|
|
522
|
+
else {
|
|
523
|
+
acc.push({
|
|
524
|
+
contents: [],
|
|
525
|
+
href: urlJoin(baseUrl, encodeURI(file.uri)).replace(/\/$/, ""),
|
|
526
|
+
path: file.uri.replace(/\/$/, ""),
|
|
527
|
+
title: parts[0] ?? ""
|
|
528
|
+
});
|
|
529
|
+
}
|
|
320
530
|
});
|
|
321
|
-
}
|
|
531
|
+
}
|
|
532
|
+
return acc;
|
|
322
533
|
}, []);
|
|
323
|
-
|
|
324
|
-
|
|
534
|
+
if (toc.length === 0)
|
|
535
|
+
return manifest;
|
|
536
|
+
return {
|
|
537
|
+
...manifest,
|
|
325
538
|
nav: {
|
|
326
|
-
toc
|
|
539
|
+
toc
|
|
327
540
|
}
|
|
328
541
|
};
|
|
329
|
-
}
|
|
330
|
-
|
|
542
|
+
};
|
|
543
|
+
const baseManifest = {
|
|
544
|
+
filename: ``,
|
|
331
545
|
items: [],
|
|
332
546
|
nav: {
|
|
333
547
|
toc: []
|
|
334
548
|
},
|
|
335
|
-
readingDirection:
|
|
336
|
-
renditionLayout:
|
|
337
|
-
renditionSpread:
|
|
549
|
+
readingDirection: `ltr`,
|
|
550
|
+
renditionLayout: `pre-paginated`,
|
|
551
|
+
renditionSpread: `auto`,
|
|
338
552
|
spineItems: [],
|
|
339
|
-
title:
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
553
|
+
title: ``
|
|
554
|
+
};
|
|
555
|
+
const generateManifestFromArchive = async (archive, { baseUrl = `` } = {}) => {
|
|
556
|
+
const hooks = [
|
|
557
|
+
defaultHook({ archive, baseUrl }),
|
|
558
|
+
epubHook({ archive, baseUrl }),
|
|
559
|
+
epubOptimizerHook({ archive, baseUrl }),
|
|
560
|
+
comicInfoHook({ archive, baseUrl }),
|
|
561
|
+
navigationFallbackHook({ archive, baseUrl })
|
|
347
562
|
];
|
|
348
563
|
try {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
564
|
+
const manifest = await hooks.reduce(async (manifest2, gen) => {
|
|
565
|
+
return await gen(await manifest2);
|
|
566
|
+
}, Promise.resolve(baseManifest));
|
|
567
|
+
Report.log("Generated manifest", manifest);
|
|
568
|
+
return manifest;
|
|
569
|
+
} catch (e) {
|
|
570
|
+
Report.error(e);
|
|
571
|
+
throw e;
|
|
353
572
|
}
|
|
354
|
-
}
|
|
355
|
-
|
|
573
|
+
};
|
|
574
|
+
const getUriBasename = (uri) => uri.substring(uri.lastIndexOf(`/`) + 1) || uri;
|
|
575
|
+
const createArchiveFromUrls = async (urls, options) => {
|
|
576
|
+
const opfFileData = `
|
|
356
577
|
<?xml version="1.0" encoding="UTF-8"?><package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="bookid">
|
|
357
578
|
<metadata>
|
|
358
|
-
<meta property="rendition:layout">${
|
|
359
|
-
${
|
|
579
|
+
<meta property="rendition:layout">${(options == null ? void 0 : options.useRenditionFlow) ? `reflowable` : `pre-paginated`}</meta>
|
|
580
|
+
${(options == null ? void 0 : options.useRenditionFlow) ? `<meta property="rendition:flow">scrolled-continuous</meta>` : ``}
|
|
360
581
|
</metadata>
|
|
361
582
|
<manifest>
|
|
362
|
-
${
|
|
583
|
+
${urls.map((url) => `<item id="${getUriBasename(url)}" href="${url}" media-type="${detectMimeTypeFromName(url)}"/>`).join(`
|
|
363
584
|
`)}
|
|
364
585
|
</manifest>
|
|
365
586
|
<spine>
|
|
366
|
-
${
|
|
587
|
+
${urls.map((url) => `<itemref idref="${getUriBasename(url)}" />`).join(`
|
|
367
588
|
`)}
|
|
368
589
|
</spine>
|
|
369
590
|
</package>
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
591
|
+
`;
|
|
592
|
+
const filesFromUrl = urls.map((url) => ({
|
|
593
|
+
dir: false,
|
|
594
|
+
basename: getUriBasename(url),
|
|
595
|
+
encodingFormat: detectMimeTypeFromName(url),
|
|
596
|
+
uri: url,
|
|
597
|
+
size: 100 / urls.length,
|
|
598
|
+
base64: async () => ``,
|
|
377
599
|
blob: async () => new Blob(),
|
|
378
|
-
string: async () =>
|
|
600
|
+
string: async () => ``
|
|
379
601
|
}));
|
|
602
|
+
const opfFile = {
|
|
603
|
+
dir: false,
|
|
604
|
+
basename: `content.opf`,
|
|
605
|
+
uri: `content.opf`,
|
|
606
|
+
size: 0,
|
|
607
|
+
base64: async () => opfFileData,
|
|
608
|
+
blob: async () => new Blob(),
|
|
609
|
+
string: async () => opfFileData
|
|
610
|
+
};
|
|
380
611
|
return {
|
|
381
|
-
filename:
|
|
382
|
-
files: [
|
|
383
|
-
dir: !1,
|
|
384
|
-
basename: "content.opf",
|
|
385
|
-
uri: "content.opf",
|
|
386
|
-
size: 0,
|
|
387
|
-
base64: async () => n,
|
|
388
|
-
blob: async () => new Blob(),
|
|
389
|
-
string: async () => n
|
|
390
|
-
}, ...a]
|
|
612
|
+
filename: ``,
|
|
613
|
+
files: [opfFile, ...filesFromUrl]
|
|
391
614
|
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
615
|
+
};
|
|
616
|
+
const blobToBase64 = async (blob) => new Promise((resolve) => {
|
|
617
|
+
const reader = new FileReader();
|
|
618
|
+
reader.readAsDataURL(blob);
|
|
619
|
+
reader.onloadend = function() {
|
|
620
|
+
const base64data = reader.result;
|
|
621
|
+
resolve(base64data);
|
|
397
622
|
};
|
|
398
|
-
})
|
|
399
|
-
|
|
400
|
-
|
|
623
|
+
});
|
|
624
|
+
const createArchiveFromText = async (content, {
|
|
625
|
+
mimeType,
|
|
626
|
+
direction
|
|
401
627
|
} = { mimeType: "text/plain" }) => {
|
|
402
|
-
const
|
|
628
|
+
const txtOpfContent = `
|
|
403
629
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
404
630
|
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="ja" prefix="rendition: http://www.idpf.org/vocab/rendition/#"
|
|
405
631
|
unique-identifier="ootuya-id">
|
|
@@ -410,85 +636,110 @@ const y = {
|
|
|
410
636
|
<manifest>
|
|
411
637
|
<item id="p01" href="p01.txt" media-type="text/plain"/>
|
|
412
638
|
</manifest>
|
|
413
|
-
<spine page-progression-direction="${
|
|
639
|
+
<spine page-progression-direction="${direction ?? `ltr`}">
|
|
414
640
|
<itemref idref="p01" />
|
|
415
641
|
</spine>
|
|
416
642
|
</package>
|
|
417
643
|
`;
|
|
418
|
-
|
|
419
|
-
filename:
|
|
644
|
+
const archive = {
|
|
645
|
+
filename: `content.txt`,
|
|
420
646
|
files: [
|
|
421
647
|
{
|
|
422
|
-
dir:
|
|
423
|
-
basename:
|
|
424
|
-
uri:
|
|
425
|
-
blob: async () => new Blob([
|
|
426
|
-
string: async () =>
|
|
427
|
-
base64: async () => btoa(
|
|
648
|
+
dir: false,
|
|
649
|
+
basename: getUriBasename(`generated.opf`),
|
|
650
|
+
uri: `generated.opf`,
|
|
651
|
+
blob: async () => new Blob([txtOpfContent]),
|
|
652
|
+
string: async () => txtOpfContent,
|
|
653
|
+
base64: async () => btoa(txtOpfContent),
|
|
428
654
|
size: 0
|
|
429
655
|
},
|
|
430
656
|
{
|
|
431
|
-
dir:
|
|
432
|
-
basename:
|
|
433
|
-
uri:
|
|
434
|
-
blob: async () =>
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
657
|
+
dir: false,
|
|
658
|
+
basename: getUriBasename(`p01.txt`),
|
|
659
|
+
uri: `p01.txt`,
|
|
660
|
+
blob: async () => {
|
|
661
|
+
if (typeof content === `string`)
|
|
662
|
+
return new Blob([content]);
|
|
663
|
+
return content;
|
|
664
|
+
},
|
|
665
|
+
string: async () => {
|
|
666
|
+
if (typeof content === `string`)
|
|
667
|
+
return content;
|
|
668
|
+
return content.text();
|
|
669
|
+
},
|
|
670
|
+
base64: async () => {
|
|
671
|
+
if (typeof content === `string`)
|
|
672
|
+
return btoa(content);
|
|
673
|
+
return blobToBase64(content);
|
|
674
|
+
},
|
|
675
|
+
size: typeof content === `string` ? content.length : content.size,
|
|
676
|
+
encodingFormat: mimeType
|
|
439
677
|
}
|
|
440
678
|
]
|
|
441
679
|
};
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
files
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
680
|
+
return archive;
|
|
681
|
+
};
|
|
682
|
+
const createArchiveFromJszip = async (jszip, { orderByAlpha, name } = {}) => {
|
|
683
|
+
let files = Object.values(jszip.files);
|
|
684
|
+
if (orderByAlpha) {
|
|
685
|
+
files = files.sort((a, b) => sortByTitleComparator(a.name, b.name));
|
|
686
|
+
}
|
|
687
|
+
const archive = {
|
|
688
|
+
filename: name || ``,
|
|
689
|
+
files: files.map((file) => ({
|
|
690
|
+
dir: file.dir,
|
|
691
|
+
basename: getUriBasename(file.name),
|
|
692
|
+
uri: file.name,
|
|
693
|
+
blob: () => file.async(`blob`),
|
|
694
|
+
string: () => file.async(`string`),
|
|
695
|
+
base64: () => file.async(`base64`),
|
|
696
|
+
...file.internalStream && {
|
|
697
|
+
stream: file.internalStream
|
|
456
698
|
},
|
|
457
699
|
// this is private API
|
|
458
700
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
459
701
|
// @ts-ignore
|
|
460
|
-
size:
|
|
702
|
+
size: file._data.uncompressedSize
|
|
461
703
|
}))
|
|
462
704
|
};
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
705
|
+
Report.log("Generated archive", archive);
|
|
706
|
+
return archive;
|
|
707
|
+
};
|
|
708
|
+
const createArchiveFromArrayBufferList = async (list, { orderByAlpha, name } = {}) => {
|
|
709
|
+
let files = list;
|
|
710
|
+
if (orderByAlpha) {
|
|
711
|
+
files = files.sort((a, b) => sortByTitleComparator(a.name, b.name));
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
filename: name || ``,
|
|
715
|
+
files: files.map((file) => ({
|
|
716
|
+
dir: file.isDir,
|
|
717
|
+
basename: getUriBasename(file.name),
|
|
718
|
+
uri: file.name,
|
|
719
|
+
blob: async () => new Blob([await file.data()]),
|
|
473
720
|
string: async () => {
|
|
474
|
-
const
|
|
475
|
-
return String.fromCharCode.apply(null, Array.from(new Uint16Array(
|
|
721
|
+
const data = await file.data();
|
|
722
|
+
return String.fromCharCode.apply(null, Array.from(new Uint16Array(data)));
|
|
723
|
+
},
|
|
724
|
+
base64: async () => {
|
|
725
|
+
return ``;
|
|
476
726
|
},
|
|
477
|
-
|
|
478
|
-
size: t.size
|
|
727
|
+
size: file.size
|
|
479
728
|
}))
|
|
480
729
|
};
|
|
481
|
-
}
|
|
482
|
-
|
|
730
|
+
};
|
|
731
|
+
const configure = ({ enableReport } = {}) => {
|
|
732
|
+
Report.enable(!!enableReport);
|
|
483
733
|
};
|
|
484
734
|
export {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
735
|
+
configure,
|
|
736
|
+
createArchiveFromArrayBufferList,
|
|
737
|
+
createArchiveFromJszip,
|
|
738
|
+
createArchiveFromText,
|
|
739
|
+
createArchiveFromUrls,
|
|
740
|
+
generateManifestFromArchive,
|
|
741
|
+
generateResourceFromArchive,
|
|
742
|
+
generateResourceFromError,
|
|
743
|
+
getArchiveOpfInfo
|
|
493
744
|
};
|
|
494
745
|
//# sourceMappingURL=prose-streamer.js.map
|