mirror of
https://github.com/danbulant/Mangades
synced 2026-06-20 23:01:28 +00:00
fix multichapter epub generation
This commit is contained in:
parent
56562f88ca
commit
b79647df02
3 changed files with 57 additions and 52 deletions
|
|
@ -1,38 +1,37 @@
|
|||
|
||||
/**
|
||||
* @typedef Chapter
|
||||
* @property {string} id
|
||||
* @property {number} number
|
||||
* @property {number?} volume
|
||||
* @property {string[]} links
|
||||
* @property {string} hash
|
||||
* @property {string} baseUrl
|
||||
* @property {string} title
|
||||
*/
|
||||
|
||||
import request, { proxy, imageproxy } from "./request";
|
||||
|
||||
const RETRY_LIMIT = 10;
|
||||
|
||||
interface Chapter {
|
||||
id: string,
|
||||
number: string,
|
||||
volume?: string,
|
||||
links: string[],
|
||||
hash: string,
|
||||
hashes: string[],
|
||||
baseUrl: string,
|
||||
title: string
|
||||
}
|
||||
|
||||
interface Opts {
|
||||
quality: string,
|
||||
file: WritableStream,
|
||||
title: string,
|
||||
id: string,
|
||||
language: string,
|
||||
author: string,
|
||||
updatedAt: string | Date,
|
||||
chapters: Chapter[],
|
||||
callback?: (chapter: string | number, link: number, finished: boolean) => void,
|
||||
onerror?: (error: Error) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Base generator, to be extended
|
||||
*/
|
||||
export class BaseGenerator {
|
||||
/**
|
||||
*
|
||||
* @param {object} opts
|
||||
* @param {string} opts.quality
|
||||
* @param {WritableStream} opts.file
|
||||
* @param {string} opts.title
|
||||
* @param {string} opts.id
|
||||
* @param {string} opts.language
|
||||
* @param {string} opts.author
|
||||
* @param {string|Date} opts.updatedAt
|
||||
* @param {Chapter[]} opts.chapters
|
||||
* @param {(chapter: number, link: number, finished: boolean) => void} [opts.callback]
|
||||
* @param {(error: Error) => void} [opts.onerror]
|
||||
*/
|
||||
constructor(opts) {
|
||||
opts: Opts;
|
||||
constructor(opts: Opts) {
|
||||
this.opts = opts;
|
||||
this.opts.quality = "data";
|
||||
}
|
||||
|
|
@ -83,9 +82,9 @@ export class BaseGenerator {
|
|||
* @param {number} link
|
||||
* @param {boolean} finished
|
||||
*/
|
||||
callback(chapter = -1, link = -1, finished = false) {
|
||||
callback(chapter: string | number = -1, link: number = -1, finished = false) {
|
||||
if(this.opts.callback) {
|
||||
this.opts.callback(chapter, parseInt(link), finished);
|
||||
this.opts.callback(chapter, Number(link), finished);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,10 @@ const enc = new TextEncoder();
|
|||
* Handles epub generation
|
||||
*/
|
||||
export class EpubGenerator extends BaseGenerator {
|
||||
writer: WritableStreamDefaultWriter<Uint8Array>;
|
||||
zip: Zip;
|
||||
hashes: { hash: string, chapter: string }[];
|
||||
|
||||
async generate() {
|
||||
this.writer = this.opts.file.getWriter();
|
||||
this.zip = new Zip();
|
||||
|
|
@ -34,7 +38,7 @@ export class EpubGenerator extends BaseGenerator {
|
|||
chapter.links = data.urls;
|
||||
chapter.hashes = data.hashes;
|
||||
chapter.hash = data.hash;
|
||||
this.hashes.push(...chapter.hashes);
|
||||
this.hashes.push(...(chapter.hashes.map(t => ({ hash: t, chapter: chapterI }))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +56,7 @@ export class EpubGenerator extends BaseGenerator {
|
|||
for(const i in chapter.links) {
|
||||
let url = chapter.links[i];
|
||||
let hash = chapter.hashes[i];
|
||||
this.callback(chapterI, i, false);
|
||||
this.callback(chapterI, Number(i), false);
|
||||
const start = performance.now();
|
||||
const res = await this.fetchImage(url, chapter);
|
||||
const image = new ZipPassThrough("OEBPS/" + hash);
|
||||
|
|
@ -67,12 +71,13 @@ export class EpubGenerator extends BaseGenerator {
|
|||
url: url
|
||||
});
|
||||
image.push(data, true);
|
||||
const textContent = new ZipPassThrough("OEBPS/" + i + ".xhtml");
|
||||
let fileI = this.hashes.findIndex(t => t.hash === hash);
|
||||
const textContent = new ZipPassThrough(`OEBPS/${fileI}.xhtml`);
|
||||
this.zip.add(textContent);
|
||||
textContent.push(enc.encode(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Page ${i + 1}</title>
|
||||
<title>Chapter ${chapterI} Page ${Number(i) + 1}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta name="EPB-UUID" content=""/>
|
||||
</head>
|
||||
|
|
@ -80,7 +85,7 @@ export class EpubGenerator extends BaseGenerator {
|
|||
<img style="margin:auto;height:100%;" src="${hash}" />
|
||||
</body>
|
||||
</html>`), true);
|
||||
this.callback(chapterI, i, true);
|
||||
this.callback(chapterI, Number(i), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,28 +118,28 @@ export class EpubGenerator extends BaseGenerator {
|
|||
|
||||
<metadata xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:dcterms="http://purl.org/dc/terms/">
|
||||
<dc:title>${this.opts.title}</dc:title>
|
||||
<dc:language>${this.opts.language || "en"}</dc:language>
|
||||
<dc:creator>${this.opts.author}</dc:creator>
|
||||
<dc:identifier id="bookid">${this.opts.id}</dc:identifier>
|
||||
<dc:type>Image</dc:type>
|
||||
<dc:title>${this.opts.title}</dc:title>
|
||||
<dc:language>${this.opts.language || "en"}</dc:language>
|
||||
<dc:creator>${this.opts.author}</dc:creator>
|
||||
<dc:identifier id="bookid">${this.opts.id}</dc:identifier>
|
||||
<dc:type>Image</dc:type>
|
||||
|
||||
<meta property="dcterms:modified">${this.opts.updatedAt.toString().split("+")[0]}Z</meta>
|
||||
<meta property="rendition:layout">pre-paginated</meta>
|
||||
<meta property="rendition:orientation">portrait</meta>
|
||||
<meta property="rendition:spread">landscape</meta>
|
||||
<meta property="dcterms:modified">${this.opts.updatedAt.toString().split("+")[0]}Z</meta>
|
||||
<meta property="rendition:layout">pre-paginated</meta>
|
||||
<meta property="rendition:orientation">portrait</meta>
|
||||
<meta property="rendition:spread">landscape</meta>
|
||||
</metadata>
|
||||
|
||||
<manifest>
|
||||
${this.hashes.map((t, i) => ` <item id="i${i}" href="${t}" fallback="fallback" media-type="image/${t.substr(t.lastIndexOf(".") + 1) === "jpg" ? "jpeg" : t.substr(t.lastIndexOf(".") + 1)}"/>`).join("\n")}
|
||||
${this.hashes.map((t, i) => ` <item id="i${i}" href="${t.hash}" fallback="fallback" media-type="image/${t.hash.substr(t.hash.lastIndexOf(".") + 1) === "jpg" ? "jpeg" : t.hash.substr(t.hash.lastIndexOf(".") + 1)}"/>`).join("\n")}
|
||||
${this.hashes.map((t, i) => ` <item id="p${i}" href="${i}.xhtml" media-type="application/xhtml+xml" />`).join("\n")}
|
||||
|
||||
<item id="ncxtoc" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
|
||||
<item id="ncxtoc" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
|
||||
</manifest>
|
||||
|
||||
<spine toc="ncxtoc">
|
||||
${this.hashes.map((t, i) => ` <itemref idref="p${i}" linear="yes" />`).join("\n")}
|
||||
<itemref idref="fallback" linear="no" />
|
||||
<itemref idref="fallback" linear="no" />
|
||||
</spine>
|
||||
|
||||
</package>`), true);
|
||||
|
|
@ -142,6 +147,7 @@ export class EpubGenerator extends BaseGenerator {
|
|||
|
||||
toc() {
|
||||
const ncx = new ZipPassThrough("OEBPS/toc.ncx");
|
||||
|
||||
this.zip.add(ncx);
|
||||
ncx.push(enc.encode(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
|
||||
|
|
@ -159,14 +165,14 @@ export class EpubGenerator extends BaseGenerator {
|
|||
</docAuthor>
|
||||
|
||||
<navMap>
|
||||
${this.opts.chapters.map((t, i) => t.links.map((link, i) => `
|
||||
<navPoint id="p${i}" playOrder="${i + 1}">
|
||||
${this.opts.chapters.map((t, i) => `
|
||||
<navPoint id="p${this.hashes.findIndex(h => h.hash === t.hashes[0])}" playOrder="${i + 1}">
|
||||
<navLabel>
|
||||
<text>${this.opts.title} Chapter ${t.number} Page ${i + 1}</text>
|
||||
<text>${this.opts.title} Chapter ${t.number}</text>
|
||||
</navLabel>
|
||||
<content src="${i}.xhtml"/>
|
||||
<content src="${this.hashes.findIndex(h => h.hash === t.hashes[0])}.xhtml"/>
|
||||
</navPoint>
|
||||
`)).flat().join("\n")}
|
||||
`).flat().join("\n")}
|
||||
</navMap>
|
||||
</ncx>`), true);
|
||||
}
|
||||
|
|
@ -6,6 +6,6 @@
|
|||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte", "src/**/*.ts"],
|
||||
"extends": "./.svelte-kit/tsconfig.json"
|
||||
}
|
||||
Loading…
Reference in a new issue