wp-epub-gen 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +261 -75
- package/build/downloadImage.d.ts +0 -5
- package/build/errors.d.ts +0 -1
- package/build/generateTempFile.d.ts +0 -5
- package/build/index.d.ts +1 -5
- package/build/index.js +422 -118
- package/build/index.mjs +1043 -0
- package/build/libs/safeFineName.d.ts +0 -1
- package/build/libs/utils.d.ts +1 -7
- package/build/makeCover.d.ts +0 -5
- package/build/parseContent.d.ts +0 -5
- package/build/render.d.ts +0 -5
- package/build/templates.d.ts +11 -0
- package/build/types.d.ts +0 -1
- package/package.json +29 -35
- package/tsconfig.json +8 -7
- package/vite.config.ts +76 -3
- package/vitest.config.ts +9 -0
package/build/index.mjs
ADDED
|
@@ -0,0 +1,1043 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { v4 } from "uuid";
|
|
5
|
+
import * as cheerio from "cheerio";
|
|
6
|
+
import { remove } from "diacritics";
|
|
7
|
+
import uslug from "uslug";
|
|
8
|
+
import archiver from "archiver";
|
|
9
|
+
import fs$1 from "fs-extra";
|
|
10
|
+
import request from "superagent";
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import { promisify } from "util";
|
|
13
|
+
import ejs from "ejs";
|
|
14
|
+
import * as entities from "entities";
|
|
15
|
+
const errors = {
|
|
16
|
+
no_output_path: "No output path!",
|
|
17
|
+
no_title: "Title is required.",
|
|
18
|
+
no_content: "Content is required."
|
|
19
|
+
};
|
|
20
|
+
function safeFineName(name) {
|
|
21
|
+
return name.replace(/[/\\?%*:|"<>\t\r\n]/g, "_");
|
|
22
|
+
}
|
|
23
|
+
const mimeModule$1 = require("mime/lite");
|
|
24
|
+
const mime$1 = mimeModule$1.default || mimeModule$1;
|
|
25
|
+
function parseContent(content, index, epubConfigs) {
|
|
26
|
+
let chapter = { ...content };
|
|
27
|
+
let { filename } = chapter;
|
|
28
|
+
if (!filename) {
|
|
29
|
+
let titleSlug = uslug(remove(chapter.title || "no title"));
|
|
30
|
+
titleSlug = titleSlug.replace(/[\/\\]/g, "_");
|
|
31
|
+
chapter.href = `${index}_${titleSlug}.xhtml`;
|
|
32
|
+
chapter.filePath = path.join(epubConfigs.dir, "OEBPS", chapter.href);
|
|
33
|
+
} else {
|
|
34
|
+
filename = safeFineName(filename);
|
|
35
|
+
let is_xhtml = filename.endsWith(".xhtml");
|
|
36
|
+
chapter.href = is_xhtml ? filename : `${filename}.xhtml`;
|
|
37
|
+
if (is_xhtml) {
|
|
38
|
+
chapter.filePath = path.join(epubConfigs.dir, "OEBPS", filename);
|
|
39
|
+
} else {
|
|
40
|
+
chapter.filePath = path.join(epubConfigs.dir, "OEBPS", `${filename}.xhtml`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
chapter.id = `item_${index}`;
|
|
44
|
+
chapter.dir = path.dirname(chapter.filePath);
|
|
45
|
+
chapter.excludeFromToc = chapter.excludeFromToc || false;
|
|
46
|
+
chapter.beforeToc = chapter.beforeToc || false;
|
|
47
|
+
if (chapter.author && typeof chapter.author === "string") {
|
|
48
|
+
chapter.author = [chapter.author];
|
|
49
|
+
} else if (!chapter.author || !Array.isArray(chapter.author)) {
|
|
50
|
+
chapter.author = [];
|
|
51
|
+
}
|
|
52
|
+
let allowedAttributes = [
|
|
53
|
+
"content",
|
|
54
|
+
"alt",
|
|
55
|
+
"id",
|
|
56
|
+
"title",
|
|
57
|
+
"src",
|
|
58
|
+
"href",
|
|
59
|
+
"about",
|
|
60
|
+
"accesskey",
|
|
61
|
+
"aria-activedescendant",
|
|
62
|
+
"aria-atomic",
|
|
63
|
+
"aria-autocomplete",
|
|
64
|
+
"aria-busy",
|
|
65
|
+
"aria-checked",
|
|
66
|
+
"aria-controls",
|
|
67
|
+
"aria-describedat",
|
|
68
|
+
"aria-describedby",
|
|
69
|
+
"aria-disabled",
|
|
70
|
+
"aria-dropeffect",
|
|
71
|
+
"aria-expanded",
|
|
72
|
+
"aria-flowto",
|
|
73
|
+
"aria-grabbed",
|
|
74
|
+
"aria-haspopup",
|
|
75
|
+
"aria-hidden",
|
|
76
|
+
"aria-invalid",
|
|
77
|
+
"aria-label",
|
|
78
|
+
"aria-labelledby",
|
|
79
|
+
"aria-level",
|
|
80
|
+
"aria-live",
|
|
81
|
+
"aria-multiline",
|
|
82
|
+
"aria-multiselectable",
|
|
83
|
+
"aria-orientation",
|
|
84
|
+
"aria-owns",
|
|
85
|
+
"aria-posinset",
|
|
86
|
+
"aria-pressed",
|
|
87
|
+
"aria-readonly",
|
|
88
|
+
"aria-relevant",
|
|
89
|
+
"aria-required",
|
|
90
|
+
"aria-selected",
|
|
91
|
+
"aria-setsize",
|
|
92
|
+
"aria-sort",
|
|
93
|
+
"aria-valuemax",
|
|
94
|
+
"aria-valuemin",
|
|
95
|
+
"aria-valuenow",
|
|
96
|
+
"aria-valuetext",
|
|
97
|
+
"class",
|
|
98
|
+
"content",
|
|
99
|
+
"contenteditable",
|
|
100
|
+
"contextmenu",
|
|
101
|
+
"datatype",
|
|
102
|
+
"dir",
|
|
103
|
+
"draggable",
|
|
104
|
+
"dropzone",
|
|
105
|
+
"hidden",
|
|
106
|
+
"hreflang",
|
|
107
|
+
"id",
|
|
108
|
+
"inlist",
|
|
109
|
+
"itemid",
|
|
110
|
+
"itemref",
|
|
111
|
+
"itemscope",
|
|
112
|
+
"itemtype",
|
|
113
|
+
"lang",
|
|
114
|
+
"media",
|
|
115
|
+
"ns1:type",
|
|
116
|
+
"ns2:alphabet",
|
|
117
|
+
"ns2:ph",
|
|
118
|
+
"onabort",
|
|
119
|
+
"onblur",
|
|
120
|
+
"oncanplay",
|
|
121
|
+
"oncanplaythrough",
|
|
122
|
+
"onchange",
|
|
123
|
+
"onclick",
|
|
124
|
+
"oncontextmenu",
|
|
125
|
+
"ondblclick",
|
|
126
|
+
"ondrag",
|
|
127
|
+
"ondragend",
|
|
128
|
+
"ondragenter",
|
|
129
|
+
"ondragleave",
|
|
130
|
+
"ondragover",
|
|
131
|
+
"ondragstart",
|
|
132
|
+
"ondrop",
|
|
133
|
+
"ondurationchange",
|
|
134
|
+
"onemptied",
|
|
135
|
+
"onended",
|
|
136
|
+
"onerror",
|
|
137
|
+
"onfocus",
|
|
138
|
+
"oninput",
|
|
139
|
+
"oninvalid",
|
|
140
|
+
"onkeydown",
|
|
141
|
+
"onkeypress",
|
|
142
|
+
"onkeyup",
|
|
143
|
+
"onload",
|
|
144
|
+
"onloadeddata",
|
|
145
|
+
"onloadedmetadata",
|
|
146
|
+
"onloadstart",
|
|
147
|
+
"onmousedown",
|
|
148
|
+
"onmousemove",
|
|
149
|
+
"onmouseout",
|
|
150
|
+
"onmouseover",
|
|
151
|
+
"onmouseup",
|
|
152
|
+
"onmousewheel",
|
|
153
|
+
"onpause",
|
|
154
|
+
"onplay",
|
|
155
|
+
"onplaying",
|
|
156
|
+
"onprogress",
|
|
157
|
+
"onratechange",
|
|
158
|
+
"onreadystatechange",
|
|
159
|
+
"onreset",
|
|
160
|
+
"onscroll",
|
|
161
|
+
"onseeked",
|
|
162
|
+
"onseeking",
|
|
163
|
+
"onselect",
|
|
164
|
+
"onshow",
|
|
165
|
+
"onstalled",
|
|
166
|
+
"onsubmit",
|
|
167
|
+
"onsuspend",
|
|
168
|
+
"ontimeupdate",
|
|
169
|
+
"onvolumechange",
|
|
170
|
+
"onwaiting",
|
|
171
|
+
"prefix",
|
|
172
|
+
"property",
|
|
173
|
+
"rel",
|
|
174
|
+
"resource",
|
|
175
|
+
"rev",
|
|
176
|
+
"role",
|
|
177
|
+
"spellcheck",
|
|
178
|
+
"style",
|
|
179
|
+
"tabindex",
|
|
180
|
+
"target",
|
|
181
|
+
"title",
|
|
182
|
+
"type",
|
|
183
|
+
"typeof",
|
|
184
|
+
"vocab",
|
|
185
|
+
"xml:base",
|
|
186
|
+
"xml:lang",
|
|
187
|
+
"xml:space",
|
|
188
|
+
"colspan",
|
|
189
|
+
"rowspan",
|
|
190
|
+
"epub:type",
|
|
191
|
+
"epub:prefix"
|
|
192
|
+
];
|
|
193
|
+
let allowedXhtml11Tags = [
|
|
194
|
+
"div",
|
|
195
|
+
"p",
|
|
196
|
+
"h1",
|
|
197
|
+
"h2",
|
|
198
|
+
"h3",
|
|
199
|
+
"h4",
|
|
200
|
+
"h5",
|
|
201
|
+
"h6",
|
|
202
|
+
"ul",
|
|
203
|
+
"ol",
|
|
204
|
+
"li",
|
|
205
|
+
"dl",
|
|
206
|
+
"dt",
|
|
207
|
+
"dd",
|
|
208
|
+
"address",
|
|
209
|
+
"hr",
|
|
210
|
+
"pre",
|
|
211
|
+
"blockquote",
|
|
212
|
+
"center",
|
|
213
|
+
"ins",
|
|
214
|
+
"del",
|
|
215
|
+
"a",
|
|
216
|
+
"span",
|
|
217
|
+
"bdo",
|
|
218
|
+
"br",
|
|
219
|
+
"em",
|
|
220
|
+
"strong",
|
|
221
|
+
"dfn",
|
|
222
|
+
"code",
|
|
223
|
+
"samp",
|
|
224
|
+
"kbd",
|
|
225
|
+
"bar",
|
|
226
|
+
"cite",
|
|
227
|
+
"abbr",
|
|
228
|
+
"acronym",
|
|
229
|
+
"q",
|
|
230
|
+
"sub",
|
|
231
|
+
"sup",
|
|
232
|
+
"tt",
|
|
233
|
+
"i",
|
|
234
|
+
"b",
|
|
235
|
+
"big",
|
|
236
|
+
"small",
|
|
237
|
+
"u",
|
|
238
|
+
"s",
|
|
239
|
+
"strike",
|
|
240
|
+
"basefont",
|
|
241
|
+
"font",
|
|
242
|
+
"object",
|
|
243
|
+
"param",
|
|
244
|
+
"img",
|
|
245
|
+
"table",
|
|
246
|
+
"caption",
|
|
247
|
+
"colgroup",
|
|
248
|
+
"col",
|
|
249
|
+
"thead",
|
|
250
|
+
"tfoot",
|
|
251
|
+
"tbody",
|
|
252
|
+
"tr",
|
|
253
|
+
"th",
|
|
254
|
+
"td",
|
|
255
|
+
"embed",
|
|
256
|
+
"applet",
|
|
257
|
+
"iframe",
|
|
258
|
+
"img",
|
|
259
|
+
"map",
|
|
260
|
+
"noscript",
|
|
261
|
+
"ns:svg",
|
|
262
|
+
"object",
|
|
263
|
+
"script",
|
|
264
|
+
"table",
|
|
265
|
+
"tt",
|
|
266
|
+
"var"
|
|
267
|
+
];
|
|
268
|
+
let $ = cheerio.load(chapter.data, {
|
|
269
|
+
xml: {
|
|
270
|
+
lowerCaseTags: true,
|
|
271
|
+
recognizeSelfClosing: true
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
let body = $("body");
|
|
275
|
+
if (body.length) {
|
|
276
|
+
let html = body.html();
|
|
277
|
+
if (html) {
|
|
278
|
+
$ = cheerio.load(html, {
|
|
279
|
+
xml: {
|
|
280
|
+
lowerCaseTags: true,
|
|
281
|
+
recognizeSelfClosing: true
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
$($("*").get().reverse()).each(function(elemIndex, elem) {
|
|
287
|
+
let attrs = elem.attribs;
|
|
288
|
+
let that = this;
|
|
289
|
+
let tags = ["img", "br", "hr"];
|
|
290
|
+
if (tags.includes(that.name)) {
|
|
291
|
+
if (that.name === "img" && !$(that).attr("alt")) {
|
|
292
|
+
$(that).attr("alt", "image-placeholder");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
Object.entries(attrs).map(([k, v]) => {
|
|
296
|
+
if (allowedAttributes.includes(k)) {
|
|
297
|
+
if (k === "type" && that.name !== "script") {
|
|
298
|
+
$(that).removeAttr(k);
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
$(that).removeAttr(k);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
if (epubConfigs.version === 2) {
|
|
305
|
+
if (!allowedXhtml11Tags.includes(that.name)) {
|
|
306
|
+
if (epubConfigs.verbose) {
|
|
307
|
+
console.log(
|
|
308
|
+
"Warning (content[" + index + "]):",
|
|
309
|
+
that.name,
|
|
310
|
+
"tag isn't allowed on EPUB 2/XHTML 1.1 DTD."
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
let child = $(that).html();
|
|
314
|
+
$(that).replaceWith($("<div>" + child + "</div>"));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
$("img").each((index2, elem) => {
|
|
319
|
+
let url = $(elem).attr("src") || "";
|
|
320
|
+
let image = epubConfigs.images.find((el) => el.url === url);
|
|
321
|
+
let id;
|
|
322
|
+
let extension;
|
|
323
|
+
if (image) {
|
|
324
|
+
id = image.id;
|
|
325
|
+
extension = image.extension;
|
|
326
|
+
} else {
|
|
327
|
+
id = v4();
|
|
328
|
+
let mediaType = mime$1.getType(url.replace(/\?.*/, "")) || "";
|
|
329
|
+
extension = mime$1.getExtension(mediaType) || "";
|
|
330
|
+
let dir = chapter.dir || "";
|
|
331
|
+
let img = { id, url, dir, mediaType, extension };
|
|
332
|
+
epubConfigs.images.push(img);
|
|
333
|
+
}
|
|
334
|
+
$(elem).attr("src", `images/${id}.${extension}`);
|
|
335
|
+
});
|
|
336
|
+
chapter.data = $.xml();
|
|
337
|
+
if (Array.isArray(chapter.children)) {
|
|
338
|
+
chapter.children = chapter.children.map(
|
|
339
|
+
(content2, idx) => parseContent(content2, `${index}_${idx}`, epubConfigs)
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
return chapter;
|
|
343
|
+
}
|
|
344
|
+
promisify(fs.readFile);
|
|
345
|
+
const writeFile = promisify(fs.writeFile);
|
|
346
|
+
const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36";
|
|
347
|
+
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
348
|
+
async function fileIsStable(filename, max_wait = 3e4) {
|
|
349
|
+
let start_time = (/* @__PURE__ */ new Date()).getTime();
|
|
350
|
+
let last_size = fs.statSync(filename).size;
|
|
351
|
+
while ((/* @__PURE__ */ new Date()).getTime() - start_time <= max_wait) {
|
|
352
|
+
await wait(1e3);
|
|
353
|
+
let size = fs.statSync(filename).size;
|
|
354
|
+
if (size === last_size) return true;
|
|
355
|
+
last_size = size;
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
function simpleMinifier(xhtml) {
|
|
360
|
+
xhtml = xhtml.replace(/\s+<\/a>/gi, "</a>").replace(/\n\s+</g, "\n<").replace(/\n+/g, "\n");
|
|
361
|
+
return xhtml;
|
|
362
|
+
}
|
|
363
|
+
const downloadImage = async (epubData, options) => {
|
|
364
|
+
let { url } = options;
|
|
365
|
+
let { log } = epubData;
|
|
366
|
+
let epub_dir = epubData.dir;
|
|
367
|
+
if (!url) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
let image_dir = path.join(epub_dir, "OEBPS", "images");
|
|
371
|
+
fs$1.ensureDirSync(image_dir);
|
|
372
|
+
let filename = path.join(image_dir, options.id + "." + options.extension);
|
|
373
|
+
if (url.startsWith("file://") || url.startsWith("/")) {
|
|
374
|
+
let aux_path = url.replace(/^file:\/\//i, "");
|
|
375
|
+
try {
|
|
376
|
+
aux_path = decodeURIComponent(aux_path);
|
|
377
|
+
} catch (e) {
|
|
378
|
+
log(`[URL Decode Warning] Failed to decode path: ${aux_path}`);
|
|
379
|
+
}
|
|
380
|
+
if (process.platform === "win32") {
|
|
381
|
+
if (aux_path.match(/^\/[a-zA-Z]:/)) {
|
|
382
|
+
aux_path = aux_path.replace(/^\//, "");
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
log(`[Copy 1] '${aux_path}' to '${filename}'`);
|
|
386
|
+
if (fs$1.existsSync(aux_path)) {
|
|
387
|
+
try {
|
|
388
|
+
fs$1.copySync(aux_path, filename);
|
|
389
|
+
} catch (e) {
|
|
390
|
+
log("[Copy 1 Error] " + e.message);
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
log(`[Copy 1 Fail] '${url}' not exists!`);
|
|
394
|
+
}
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
let requestAction;
|
|
398
|
+
if (url.startsWith("http")) {
|
|
399
|
+
requestAction = request.get(url).set({ "User-Agent": USER_AGENT });
|
|
400
|
+
requestAction.pipe(fs$1.createWriteStream(filename));
|
|
401
|
+
} else {
|
|
402
|
+
log(`[Copy 2] '${url}' to '${filename}'`);
|
|
403
|
+
requestAction = fs$1.createReadStream(path.join(options.dir || "", url));
|
|
404
|
+
requestAction.pipe(fs$1.createWriteStream(filename));
|
|
405
|
+
}
|
|
406
|
+
return new Promise((resolve, reject) => {
|
|
407
|
+
requestAction.on("error", (err) => {
|
|
408
|
+
log("[Download Error] Error while downloading: " + url);
|
|
409
|
+
log(err);
|
|
410
|
+
fs$1.unlinkSync(filename);
|
|
411
|
+
resolve();
|
|
412
|
+
});
|
|
413
|
+
requestAction.on("end", () => {
|
|
414
|
+
log("[Download Success] " + url);
|
|
415
|
+
resolve();
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
const downloadAllImages = async (epubData) => {
|
|
420
|
+
let { images } = epubData;
|
|
421
|
+
if (images.length === 0) return;
|
|
422
|
+
fs$1.ensureDirSync(path.join(epubData.dir, "OEBPS", "images"));
|
|
423
|
+
for (let image of images) {
|
|
424
|
+
await downloadImage(epubData, image);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
const epub2_content_opf_ejs = `<?xml version="1.0" encoding="UTF-8"?>
|
|
428
|
+
<package xmlns="http://www.idpf.org/2007/opf"
|
|
429
|
+
version="2.0"
|
|
430
|
+
unique-identifier="BookId">
|
|
431
|
+
|
|
432
|
+
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
433
|
+
xmlns:opf="http://www.idpf.org/2007/opf">
|
|
434
|
+
|
|
435
|
+
<dc:identifier id="BookId" opf:scheme="URN"><%= id %></dc:identifier>
|
|
436
|
+
<dc:title><%= title %></dc:title>
|
|
437
|
+
<dc:description><%= description %></dc:description>
|
|
438
|
+
<dc:publisher><%= publisher || "anonymous" %></dc:publisher>
|
|
439
|
+
<dc:creator opf:role="aut" opf:file-as="<%= author.length ? author.join(",") : author %>"><%= author.length ? author.join(",") : author %></dc:creator>
|
|
440
|
+
<dc:date opf:event="modification"><%= date %></dc:date>
|
|
441
|
+
<dc:language><%= lang || "en" %></dc:language>
|
|
442
|
+
<meta name="cover" content="image_cover"/>
|
|
443
|
+
<meta name="generator" content="epub-gen"/>
|
|
444
|
+
|
|
445
|
+
</metadata>
|
|
446
|
+
|
|
447
|
+
<manifest>
|
|
448
|
+
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
|
|
449
|
+
<item id="toc" href="toc.xhtml" media-type="application/xhtml+xml"/>
|
|
450
|
+
<item id="css" href="style.css" media-type="text/css"/>
|
|
451
|
+
|
|
452
|
+
<% if(locals.cover) { %>
|
|
453
|
+
<item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>"/>
|
|
454
|
+
<% } %>
|
|
455
|
+
|
|
456
|
+
<% images.forEach(function(image, index){ %>
|
|
457
|
+
<item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>"/>
|
|
458
|
+
<% }) %>
|
|
459
|
+
|
|
460
|
+
<% content.forEach(function(content, index){ %>
|
|
461
|
+
<item id="content_<%= index %>_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml"/>
|
|
462
|
+
<% }) %>
|
|
463
|
+
|
|
464
|
+
<% fonts.forEach(function(font, index) { %>
|
|
465
|
+
<item id="font_<%= index %>" href="fonts/<%= font %>" media-type="application/x-font-ttf"/>
|
|
466
|
+
<% }) %>
|
|
467
|
+
</manifest>
|
|
468
|
+
|
|
469
|
+
<spine toc="ncx">
|
|
470
|
+
<% content.forEach(function(content, index){ %>
|
|
471
|
+
<% if(content.beforeToc && !content.excludeFromToc){ %>
|
|
472
|
+
<itemref idref="content_<%= index %>_<%= content.id %>"/>
|
|
473
|
+
<% } %>
|
|
474
|
+
<% }) %>
|
|
475
|
+
<itemref idref="toc"/>
|
|
476
|
+
<% content.forEach(function(content, index){ %>
|
|
477
|
+
<% if(!content.beforeToc && !content.excludeFromToc){ %>
|
|
478
|
+
<itemref idref="content_<%= index %>_<%= content.id %>"/>
|
|
479
|
+
<% } %>
|
|
480
|
+
<% }) %>
|
|
481
|
+
</spine>
|
|
482
|
+
<guide/>
|
|
483
|
+
</package>
|
|
484
|
+
`;
|
|
485
|
+
const epub2_toc_xhtml_ejs = `<?xml version="1.0" encoding="UTF-8"?>
|
|
486
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
487
|
+
<html xml:lang="<%- lang %>" xmlns="http://www.w3.org/1999/xhtml">
|
|
488
|
+
<head>
|
|
489
|
+
<title><%= title %></title>
|
|
490
|
+
<meta charset="UTF-8"/>
|
|
491
|
+
<link rel="stylesheet" type="text/css" href="style.css"/>
|
|
492
|
+
</head>
|
|
493
|
+
<body>
|
|
494
|
+
<h1 class="h1"><%= tocTitle %></h1>
|
|
495
|
+
<% content.forEach(function(content, index){ %>
|
|
496
|
+
<% if(!content.excludeFromToc){ %>
|
|
497
|
+
<p class="table-of-content">
|
|
498
|
+
<a href="<%= content.href %>"><%= (1 + index) + ". " + (content.title || "Chapter " + (1 + index)) %>
|
|
499
|
+
<% if(content.author.length){ %>
|
|
500
|
+
- <small class="toc-author"><%= content.author.join(",") %></small>
|
|
501
|
+
<% } %>
|
|
502
|
+
<% if(content.url){ %><span class="toc-link"><%= content.url %></span>
|
|
503
|
+
<% }else{ %><span class="toc-link"></span>
|
|
504
|
+
<% } %>
|
|
505
|
+
</a>
|
|
506
|
+
</p>
|
|
507
|
+
<% } %>
|
|
508
|
+
<% }) %>
|
|
509
|
+
</body>
|
|
510
|
+
</html>
|
|
511
|
+
`;
|
|
512
|
+
const epub3_content_opf_ejs = `<?xml version="1.0" encoding="UTF-8"?>
|
|
513
|
+
<package xmlns="http://www.idpf.org/2007/opf"
|
|
514
|
+
version="3.0"
|
|
515
|
+
unique-identifier="BookId"
|
|
516
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
517
|
+
xmlns:dcterms="http://purl.org/dc/terms/"
|
|
518
|
+
xml:lang="en"
|
|
519
|
+
xmlns:media="http://www.idpf.org/epub/vocab/overlays/#"
|
|
520
|
+
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
|
|
521
|
+
|
|
522
|
+
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
|
|
523
|
+
|
|
524
|
+
<dc:identifier id="BookId"><%= id %></dc:identifier>
|
|
525
|
+
<meta refines="#BookId" property="identifier-type" scheme="onix:codelist5">22</meta>
|
|
526
|
+
<meta property="dcterms:identifier" id="meta-identifier">BookId</meta>
|
|
527
|
+
<dc:title><%= title %></dc:title>
|
|
528
|
+
<meta property="dcterms:title" id="meta-title"><%= title %></meta>
|
|
529
|
+
<dc:language><%= lang || "en" %></dc:language>
|
|
530
|
+
<meta property="dcterms:language" id="meta-language"><%= lang || "en" %></meta>
|
|
531
|
+
<meta property="dcterms:modified"><%= (new Date()).toISOString().split(".")[0] + "Z" %></meta>
|
|
532
|
+
<dc:creator id="creator"><%= author.length ? author.join(",") : author %></dc:creator>
|
|
533
|
+
<meta refines="#creator" property="file-as"><%= author.length ? author.join(",") : author %></meta>
|
|
534
|
+
<meta property="dcterms:publisher"><%= publisher || "anonymous" %></meta>
|
|
535
|
+
<dc:publisher><%= publisher || "anonymous" %></dc:publisher>
|
|
536
|
+
<% var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var stringDate = "" + year + "-" + month + "-" + day; %>
|
|
537
|
+
<meta property="dcterms:date"><%= stringDate %></meta>
|
|
538
|
+
<dc:date><%= stringDate %></dc:date>
|
|
539
|
+
<meta property="dcterms:rights">All rights reserved</meta>
|
|
540
|
+
<dc:rights>Copyright © <%= (new Date()).getFullYear() %> by <%= publisher || "anonymous" %></dc:rights>
|
|
541
|
+
<% if(locals.cover) { %>
|
|
542
|
+
<meta name="cover" content="image_cover" />
|
|
543
|
+
<% } %>
|
|
544
|
+
<meta name="generator" content="epub-gen" />
|
|
545
|
+
<meta property="ibooks:specified-fonts">true</meta>
|
|
546
|
+
</metadata>
|
|
547
|
+
|
|
548
|
+
<manifest>
|
|
549
|
+
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
|
|
550
|
+
<item id="toc" href="toc.xhtml" media-type="application/xhtml+xml" properties="nav" />
|
|
551
|
+
<item id="css" href="style.css" media-type="text/css" />
|
|
552
|
+
|
|
553
|
+
<% if(locals.cover) { %>
|
|
554
|
+
<item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>" />
|
|
555
|
+
<% } %>
|
|
556
|
+
|
|
557
|
+
<% images.forEach(function(image, index){ %>
|
|
558
|
+
<item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>" />
|
|
559
|
+
<% }) %>
|
|
560
|
+
|
|
561
|
+
<% function renderContentItem(content) { %>
|
|
562
|
+
<% content.forEach(function(content){ %>
|
|
563
|
+
<item id="content_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml" />
|
|
564
|
+
<% if (Array.isArray(content.children)) { %>
|
|
565
|
+
<% renderContentItem(content.children) %>
|
|
566
|
+
<% } %>
|
|
567
|
+
<% }) %>
|
|
568
|
+
<% } %>
|
|
569
|
+
<% renderContentItem(content) %>
|
|
570
|
+
|
|
571
|
+
<% fonts.forEach(function(font, index){ %>
|
|
572
|
+
<item id="font_<%= index %>" href="fonts/<%= font %>" media-type="application/x-font-ttf" />
|
|
573
|
+
<% }) %>
|
|
574
|
+
</manifest>
|
|
575
|
+
|
|
576
|
+
<spine toc="ncx">
|
|
577
|
+
<% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>
|
|
578
|
+
<% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>
|
|
579
|
+
<% function renderToc(nodes) { %>
|
|
580
|
+
<% nodes.forEach(function(content){ %>
|
|
581
|
+
<itemref idref="content_<%= content.id %>" />
|
|
582
|
+
<% if (Array.isArray(content.children)) { %>
|
|
583
|
+
<% renderToc(content.children) %>
|
|
584
|
+
<% } %>
|
|
585
|
+
<% }) %>
|
|
586
|
+
<% } %>
|
|
587
|
+
<% renderToc(nodes_1) %>
|
|
588
|
+
<itemref idref="toc" />
|
|
589
|
+
<% renderToc(nodes_2) %>
|
|
590
|
+
</spine>
|
|
591
|
+
<guide>
|
|
592
|
+
<reference type="text" title="Table of Content" href="toc.xhtml" />
|
|
593
|
+
</guide>
|
|
594
|
+
</package>
|
|
595
|
+
`;
|
|
596
|
+
const epub3_toc_xhtml_ejs = `<?xml version="1.0" encoding="UTF-8"?>
|
|
597
|
+
<!DOCTYPE html>
|
|
598
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="<%- lang %>"
|
|
599
|
+
lang="<%- lang %>">
|
|
600
|
+
<head>
|
|
601
|
+
<title><%= title %></title>
|
|
602
|
+
<meta charset="UTF-8" />
|
|
603
|
+
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
604
|
+
</head>
|
|
605
|
+
<body>
|
|
606
|
+
<h1 class="h1"><%= tocTitle %></h1>
|
|
607
|
+
<nav id="toc" class="TOC" epub:type="toc">
|
|
608
|
+
<% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>
|
|
609
|
+
<% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>
|
|
610
|
+
<% function renderToc(nodes, indent = 0) { %>
|
|
611
|
+
<ol>
|
|
612
|
+
<% nodes.forEach(function(content, index){ %>
|
|
613
|
+
<li class="table-of-content">
|
|
614
|
+
<a href="<%= content.href %>"><%= (content.title || "Chapter " + (1 + index)) %>
|
|
615
|
+
<% if(content.author.length){ %> - <small
|
|
616
|
+
class="toc-author"><%= content.author.join(",") %></small>
|
|
617
|
+
<% } %>
|
|
618
|
+
<% if(content.url){ %><span class="toc-link"><%= content.url %></span>
|
|
619
|
+
<% } %>
|
|
620
|
+
</a>
|
|
621
|
+
<% if (Array.isArray(content.children) && content.children.length > 0) { %>
|
|
622
|
+
<% renderToc(content.children, indent + 1) %>
|
|
623
|
+
<% } %>
|
|
624
|
+
</li>
|
|
625
|
+
<% }) %>
|
|
626
|
+
</ol>
|
|
627
|
+
<% } %>
|
|
628
|
+
<% renderToc(nodes_1) %>
|
|
629
|
+
<% renderToc(nodes_2) %>
|
|
630
|
+
</nav>
|
|
631
|
+
|
|
632
|
+
</body>
|
|
633
|
+
</html>
|
|
634
|
+
`;
|
|
635
|
+
const template_css = `.epub-author {
|
|
636
|
+
color: #555;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.epub-link {
|
|
640
|
+
margin-bottom: 30px;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.epub-link a {
|
|
644
|
+
color: #666;
|
|
645
|
+
font-size: 90%;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.toc-author {
|
|
649
|
+
font-size: 90%;
|
|
650
|
+
color: #555;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
.toc-link {
|
|
654
|
+
color: #999;
|
|
655
|
+
font-size: 85%;
|
|
656
|
+
display: block;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
hr {
|
|
660
|
+
border: 0;
|
|
661
|
+
border-bottom: 1px solid #dedede;
|
|
662
|
+
margin: 60px 10%;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.TOC > ol {
|
|
666
|
+
margin: 0;
|
|
667
|
+
padding: 0;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.TOC > ol ol {
|
|
671
|
+
padding: 0;
|
|
672
|
+
margin-left: 2em;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.TOC li {
|
|
676
|
+
font-size: 16px;
|
|
677
|
+
list-style: none;
|
|
678
|
+
margin: 0 auto;
|
|
679
|
+
padding: 0;
|
|
680
|
+
}
|
|
681
|
+
`;
|
|
682
|
+
const toc_ncx_ejs = `<?xml version="1.0" encoding="UTF-8"?>
|
|
683
|
+
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
|
|
684
|
+
<head>
|
|
685
|
+
<meta name="dtb:uid" content="<%= id %>"/>
|
|
686
|
+
<meta name="dtb:generator" content="epub-gen"/>
|
|
687
|
+
<meta name="dtb:depth" content="<%= (toc_depth || 1)%>"/>
|
|
688
|
+
<meta name="dtb:totalPageCount" content="0"/>
|
|
689
|
+
<meta name="dtb:maxPageNumber" content="0"/>
|
|
690
|
+
</head>
|
|
691
|
+
<docTitle>
|
|
692
|
+
<text><%= title %></text>
|
|
693
|
+
</docTitle>
|
|
694
|
+
<docAuthor>
|
|
695
|
+
<text><%= author %></text>
|
|
696
|
+
</docAuthor>
|
|
697
|
+
<navMap>
|
|
698
|
+
<% var _index = 1; %>
|
|
699
|
+
<% var nodes_1 = content.filter(c => !c.excludeFromToc && c.beforeToc) %>
|
|
700
|
+
<% var nodes_2 = content.filter(c => !c.excludeFromToc && !c.beforeToc) %>
|
|
701
|
+
<% function renderToc(nodes) { %>
|
|
702
|
+
<% nodes.forEach(function(content, index){ %>
|
|
703
|
+
<navPoint id="content_<%= content.id %>" playOrder="<%= _index++ %>" class="chapter">
|
|
704
|
+
<navLabel>
|
|
705
|
+
<text><%= (tocAutoNumber ? ((1 + index) + ". ") : "") + (content.title || "Chapter " + (1 + index)) %></text>
|
|
706
|
+
</navLabel>
|
|
707
|
+
<content src="<%= content.href %>"/>
|
|
708
|
+
<% if (Array.isArray(content.children)) { %>
|
|
709
|
+
<% renderToc(content.children) %>
|
|
710
|
+
<% } %>
|
|
711
|
+
</navPoint>
|
|
712
|
+
<% }) %>
|
|
713
|
+
<% } %>
|
|
714
|
+
|
|
715
|
+
<% renderToc(nodes_1) %>
|
|
716
|
+
|
|
717
|
+
<navPoint id="toc" playOrder="<%= _index++ %>" class="chapter">
|
|
718
|
+
<navLabel>
|
|
719
|
+
<text><%= tocTitle %></text>
|
|
720
|
+
</navLabel>
|
|
721
|
+
<content src="toc.xhtml"/>
|
|
722
|
+
</navPoint>
|
|
723
|
+
|
|
724
|
+
<% renderToc(nodes_2) %>
|
|
725
|
+
</navMap>
|
|
726
|
+
</ncx>
|
|
727
|
+
`;
|
|
728
|
+
const generateTempFile = async (epubData) => {
|
|
729
|
+
let { log } = epubData;
|
|
730
|
+
let oebps_dir = path.join(epubData.dir, "OEBPS");
|
|
731
|
+
await fs$1.ensureDir(oebps_dir);
|
|
732
|
+
epubData.css = epubData.css || template_css;
|
|
733
|
+
await writeFile(path.join(oebps_dir, "style.css"), epubData.css, "utf-8");
|
|
734
|
+
if (epubData.fonts?.length) {
|
|
735
|
+
let fonts_dir = path.join(oebps_dir, "fonts");
|
|
736
|
+
await fs$1.ensureDir(fonts_dir);
|
|
737
|
+
epubData.fonts = epubData.fonts.map((font) => {
|
|
738
|
+
let filename = path.basename(font);
|
|
739
|
+
if (!fs$1.existsSync(font)) {
|
|
740
|
+
log(`Custom font not found at '${font}'.`);
|
|
741
|
+
} else {
|
|
742
|
+
fs$1.copySync(font, path.join(fonts_dir, filename));
|
|
743
|
+
}
|
|
744
|
+
return filename;
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
const isAppendTitle = (global_append, local_append) => {
|
|
748
|
+
if (typeof local_append === "boolean") return local_append;
|
|
749
|
+
return !!global_append;
|
|
750
|
+
};
|
|
751
|
+
const saveContentToFile = (content) => {
|
|
752
|
+
let title = entities.encodeXML(content.title || "");
|
|
753
|
+
let html = `${epubData.docHeader}
|
|
754
|
+
<head>
|
|
755
|
+
<meta charset="UTF-8" />
|
|
756
|
+
<title>${title}</title>
|
|
757
|
+
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
758
|
+
</head>
|
|
759
|
+
<body>
|
|
760
|
+
`;
|
|
761
|
+
if (content.title && isAppendTitle(epubData.appendChapterTitles, content.appendChapterTitle)) {
|
|
762
|
+
html += `<h1>${title}</h1>`;
|
|
763
|
+
}
|
|
764
|
+
html += content.title && content.author && content.author?.length ? `<p class='epub-author'>${entities.encodeXML(content.author.join(", "))}</p>` : "";
|
|
765
|
+
html += content.title && content.url ? `<p class="epub-link"><a href="${content.url}">${content.url}</a></p>` : "";
|
|
766
|
+
html += `${content.data}`;
|
|
767
|
+
html += "\n</body>\n</html>";
|
|
768
|
+
fs$1.ensureDirSync(path.dirname(content.filePath));
|
|
769
|
+
fs$1.writeFileSync(content.filePath, html, "utf-8");
|
|
770
|
+
if (Array.isArray(content.children)) {
|
|
771
|
+
content.children.map(saveContentToFile);
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
epubData.content.map(saveContentToFile);
|
|
775
|
+
let metainf_dir = path.join(epubData.dir, "META-INF");
|
|
776
|
+
fs$1.ensureDirSync(metainf_dir);
|
|
777
|
+
fs$1.writeFileSync(
|
|
778
|
+
path.join(metainf_dir, "container.xml"),
|
|
779
|
+
`<?xml version="1.0" encoding="UTF-8" ?>
|
|
780
|
+
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
|
781
|
+
<rootfiles><rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/></rootfiles>
|
|
782
|
+
</container>`,
|
|
783
|
+
"utf-8"
|
|
784
|
+
);
|
|
785
|
+
if (epubData.version === 2) {
|
|
786
|
+
let fn = path.join(metainf_dir, "com.apple.ibooks.display-options.xml");
|
|
787
|
+
fs$1.writeFileSync(
|
|
788
|
+
fn,
|
|
789
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
790
|
+
<display_options>
|
|
791
|
+
<platform name="*">
|
|
792
|
+
<option name="specified-fonts">true</option>
|
|
793
|
+
</platform>
|
|
794
|
+
</display_options>
|
|
795
|
+
`,
|
|
796
|
+
"utf-8"
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
let opfTemplate;
|
|
800
|
+
let ncxTocTemplate;
|
|
801
|
+
let htmlTocTemplate;
|
|
802
|
+
if (epubData.customOpfTemplatePath && fs$1.existsSync(epubData.customOpfTemplatePath)) {
|
|
803
|
+
const { readFile } = require("./libs/utils");
|
|
804
|
+
opfTemplate = await readFile(epubData.customOpfTemplatePath, "utf-8");
|
|
805
|
+
} else {
|
|
806
|
+
opfTemplate = epubData.version === 2 ? epub2_content_opf_ejs : epub3_content_opf_ejs;
|
|
807
|
+
}
|
|
808
|
+
if (epubData.customNcxTocTemplatePath && fs$1.existsSync(epubData.customNcxTocTemplatePath)) {
|
|
809
|
+
const { readFile } = require("./libs/utils");
|
|
810
|
+
ncxTocTemplate = await readFile(epubData.customNcxTocTemplatePath, "utf-8");
|
|
811
|
+
} else {
|
|
812
|
+
ncxTocTemplate = toc_ncx_ejs;
|
|
813
|
+
}
|
|
814
|
+
if (epubData.customHtmlTocTemplatePath && fs$1.existsSync(epubData.customHtmlTocTemplatePath)) {
|
|
815
|
+
const { readFile } = require("./libs/utils");
|
|
816
|
+
htmlTocTemplate = await readFile(epubData.customHtmlTocTemplatePath, "utf-8");
|
|
817
|
+
} else {
|
|
818
|
+
htmlTocTemplate = epubData.version === 2 ? epub2_toc_xhtml_ejs : epub3_toc_xhtml_ejs;
|
|
819
|
+
}
|
|
820
|
+
let toc_depth = 1;
|
|
821
|
+
fs$1.writeFileSync(path.join(oebps_dir, "content.opf"), ejs.render(opfTemplate, epubData), "utf-8");
|
|
822
|
+
fs$1.writeFileSync(
|
|
823
|
+
path.join(oebps_dir, "toc.ncx"),
|
|
824
|
+
ejs.render(ncxTocTemplate, { ...epubData, toc_depth }),
|
|
825
|
+
"utf-8"
|
|
826
|
+
);
|
|
827
|
+
fs$1.writeFileSync(
|
|
828
|
+
path.join(oebps_dir, "toc.xhtml"),
|
|
829
|
+
simpleMinifier(ejs.render(htmlTocTemplate, epubData)),
|
|
830
|
+
"utf-8"
|
|
831
|
+
);
|
|
832
|
+
};
|
|
833
|
+
async function makeCover(data) {
|
|
834
|
+
let { cover, _coverExtension, log } = data;
|
|
835
|
+
if (!cover) return;
|
|
836
|
+
let destPath = path.join(data.dir, "OEBPS", `cover.${_coverExtension}`);
|
|
837
|
+
let writeStream = null;
|
|
838
|
+
if (cover.startsWith("http")) {
|
|
839
|
+
writeStream = request.get(cover).set({ "User-Agent": USER_AGENT });
|
|
840
|
+
writeStream.pipe(fs$1.createWriteStream(destPath));
|
|
841
|
+
} else {
|
|
842
|
+
if (!fs$1.existsSync(cover)) return;
|
|
843
|
+
log("local cover image: " + cover);
|
|
844
|
+
writeStream = fs$1.createReadStream(cover);
|
|
845
|
+
writeStream.pipe(fs$1.createWriteStream(destPath));
|
|
846
|
+
}
|
|
847
|
+
return new Promise((resolve) => {
|
|
848
|
+
writeStream.on("end", () => {
|
|
849
|
+
log("[Success] cover image saved.");
|
|
850
|
+
resolve();
|
|
851
|
+
});
|
|
852
|
+
writeStream.on("error", (e) => {
|
|
853
|
+
log("[Error] cover image error: " + e.message);
|
|
854
|
+
log("destPath: " + destPath);
|
|
855
|
+
if (fs$1.existsSync(destPath)) {
|
|
856
|
+
try {
|
|
857
|
+
fs$1.unlinkSync(destPath);
|
|
858
|
+
log("destPath removed.");
|
|
859
|
+
} catch (e2) {
|
|
860
|
+
log("[Error] remove cover image error: " + e2.message);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
resolve(e);
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
async function render(data) {
|
|
868
|
+
let { log } = data;
|
|
869
|
+
log("Generating Template Files...");
|
|
870
|
+
await generateTempFile(data);
|
|
871
|
+
log("Downloading Images...");
|
|
872
|
+
await downloadAllImages(data);
|
|
873
|
+
log("Making Cover...");
|
|
874
|
+
await makeCover(data);
|
|
875
|
+
log("Generating Epub Files...");
|
|
876
|
+
await genEpub(data);
|
|
877
|
+
if (fs$1.existsSync(data.output)) {
|
|
878
|
+
log("Output: " + data.output);
|
|
879
|
+
log("Done.");
|
|
880
|
+
} else {
|
|
881
|
+
log("Output fail!");
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
async function genEpub(epubData) {
|
|
885
|
+
let { log, dir, output } = epubData;
|
|
886
|
+
let archive = archiver("zip", { zlib: { level: 9 } });
|
|
887
|
+
let outputStream = fs$1.createWriteStream(epubData.output);
|
|
888
|
+
log("Zipping temp dir to " + output);
|
|
889
|
+
return new Promise((resolve, reject) => {
|
|
890
|
+
archive.on("end", async () => {
|
|
891
|
+
log("Done zipping, clearing temp dir...");
|
|
892
|
+
let stable = await fileIsStable(epubData.output);
|
|
893
|
+
if (!stable) {
|
|
894
|
+
log("Output epub file is not stable!");
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
fs$1.removeSync(dir);
|
|
898
|
+
resolve();
|
|
899
|
+
} catch (e) {
|
|
900
|
+
log("[Error] " + e.message);
|
|
901
|
+
reject(e);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
archive.on("close", () => {
|
|
905
|
+
log("Zip close..");
|
|
906
|
+
});
|
|
907
|
+
archive.on("finish", () => {
|
|
908
|
+
log("Zip finish..");
|
|
909
|
+
});
|
|
910
|
+
archive.on("error", (err) => {
|
|
911
|
+
log("[Archive Error] " + err.message);
|
|
912
|
+
reject(err);
|
|
913
|
+
});
|
|
914
|
+
archive.pipe(outputStream);
|
|
915
|
+
archive.append("application/epub+zip", { store: true, name: "mimetype" });
|
|
916
|
+
archive.directory(path.join(dir, "META-INF"), "META-INF");
|
|
917
|
+
archive.directory(path.join(dir, "OEBPS"), "OEBPS");
|
|
918
|
+
archive.finalize();
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
const mimeModule = require("mime/lite");
|
|
922
|
+
const mime = mimeModule.default || mimeModule;
|
|
923
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
924
|
+
const __dirname = path.dirname(__filename);
|
|
925
|
+
const baseDir = __dirname;
|
|
926
|
+
function result(success, message, options) {
|
|
927
|
+
if (options && options.verbose) {
|
|
928
|
+
if (!success) {
|
|
929
|
+
console.error(new Error(message));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
let out = {
|
|
933
|
+
success
|
|
934
|
+
};
|
|
935
|
+
if (typeof message === "string") {
|
|
936
|
+
out.message = message;
|
|
937
|
+
}
|
|
938
|
+
if (options) {
|
|
939
|
+
out.options = options;
|
|
940
|
+
}
|
|
941
|
+
return out;
|
|
942
|
+
}
|
|
943
|
+
function check(options) {
|
|
944
|
+
if (!options.output) {
|
|
945
|
+
return result(false, errors.no_output_path, options);
|
|
946
|
+
}
|
|
947
|
+
if (!options.title) {
|
|
948
|
+
return result(false, errors.no_title, options);
|
|
949
|
+
}
|
|
950
|
+
if (!options.content) {
|
|
951
|
+
return result(false, errors.no_content, options);
|
|
952
|
+
}
|
|
953
|
+
return result(true, void 0, options);
|
|
954
|
+
}
|
|
955
|
+
function parseOptions(options) {
|
|
956
|
+
let tmpDir = options.tmpDir || os.tmpdir();
|
|
957
|
+
let id = v4();
|
|
958
|
+
let data = {
|
|
959
|
+
description: options.title,
|
|
960
|
+
publisher: "anonymous",
|
|
961
|
+
author: ["anonymous"],
|
|
962
|
+
tocTitle: "Table Of Contents",
|
|
963
|
+
appendChapterTitles: true,
|
|
964
|
+
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
965
|
+
lang: "en",
|
|
966
|
+
fonts: [],
|
|
967
|
+
version: 3,
|
|
968
|
+
verbose: true,
|
|
969
|
+
timeoutSeconds: 15 * 60,
|
|
970
|
+
// 15 min
|
|
971
|
+
tocAutoNumber: false,
|
|
972
|
+
...options,
|
|
973
|
+
id,
|
|
974
|
+
tmpDir,
|
|
975
|
+
dir: path.resolve(tmpDir, id),
|
|
976
|
+
baseDir,
|
|
977
|
+
docHeader: "",
|
|
978
|
+
images: [],
|
|
979
|
+
content: [],
|
|
980
|
+
log: (msg) => options.verbose && console.log(msg)
|
|
981
|
+
};
|
|
982
|
+
if (data.version === 2) {
|
|
983
|
+
data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
|
|
984
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
985
|
+
<html xmlns="http://www.w3.org/1999/xhtml" lang="#{self.options.lang}">
|
|
986
|
+
`;
|
|
987
|
+
} else {
|
|
988
|
+
data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
|
|
989
|
+
<!DOCTYPE html>
|
|
990
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="#{self.options.lang}">
|
|
991
|
+
`;
|
|
992
|
+
}
|
|
993
|
+
if (Array.isArray(data.author) && data.author.length === 0) {
|
|
994
|
+
data.author = ["anonymous"];
|
|
995
|
+
}
|
|
996
|
+
if (typeof data.author === "string") {
|
|
997
|
+
data.author = [data.author];
|
|
998
|
+
}
|
|
999
|
+
data.content = options.content.map((content, index) => parseContent(content, index, data));
|
|
1000
|
+
if (data.cover) {
|
|
1001
|
+
data._coverMediaType = mime.getType(data.cover) || "";
|
|
1002
|
+
data._coverExtension = mime.getExtension(data._coverMediaType) || "";
|
|
1003
|
+
}
|
|
1004
|
+
return data;
|
|
1005
|
+
}
|
|
1006
|
+
async function epubGen(options, output) {
|
|
1007
|
+
options = { ...options };
|
|
1008
|
+
if (output) {
|
|
1009
|
+
options.output = output;
|
|
1010
|
+
}
|
|
1011
|
+
let o = check(options);
|
|
1012
|
+
let verbose = options.verbose !== false;
|
|
1013
|
+
if (!o.success) {
|
|
1014
|
+
if (verbose) console.error(o.message);
|
|
1015
|
+
return o;
|
|
1016
|
+
}
|
|
1017
|
+
let t;
|
|
1018
|
+
try {
|
|
1019
|
+
let data = parseOptions(options);
|
|
1020
|
+
let timeoutSeconds = data.timeoutSeconds || 0;
|
|
1021
|
+
if (timeoutSeconds > 0) {
|
|
1022
|
+
if (verbose) console.log(`TIMEOUT: ${timeoutSeconds}s`);
|
|
1023
|
+
t = setTimeout(() => {
|
|
1024
|
+
throw new Error("timeout!");
|
|
1025
|
+
}, timeoutSeconds * 1e3);
|
|
1026
|
+
} else {
|
|
1027
|
+
if (verbose) console.log(`TIMEOUT: N/A`);
|
|
1028
|
+
}
|
|
1029
|
+
await render(data);
|
|
1030
|
+
return result(true, void 0, data);
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
if (verbose) console.error(e);
|
|
1033
|
+
return result(false, e.message, options);
|
|
1034
|
+
} finally {
|
|
1035
|
+
clearTimeout(t);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
const gen = epubGen;
|
|
1039
|
+
export {
|
|
1040
|
+
epubGen,
|
|
1041
|
+
errors,
|
|
1042
|
+
gen
|
|
1043
|
+
};
|