Blocktext, Block-Index und Zeichenoffsets stimmen nun bei sechs echten Büchern exakt mit parseEpub des Web-Readers überein. Grundlage für einen echten Fortschrittsanteil (fraction_for_anchor) statt 0.0. Parser html5lib statt lxml, Rohinhalt statt ebooklib-get_content (formatiert um), UTF-8 wie JSZip. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
55 lines
2.7 KiB
JavaScript
55 lines
2.7 KiB
JavaScript
// The web reader's own block list for EPUBs, from the real app code (static/js/app/40-epub.js
|
|
// parseEpub + the reader's block selector) in headless Chromium. Reference for compare_blocks.py.
|
|
//
|
|
// node web_blocks.cjs <diora-web checkout> <out.json> book.epub [more.epub ...]
|
|
//
|
|
// Needs playwright-core (e.g. NODE_PATH=<diora-web>/node_modules) and a Chromium
|
|
// (PLAYWRIGHT_BROWSERS_PATH as for the e2e tests).
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { chromium } = require('playwright-core');
|
|
|
|
(async () => {
|
|
const [web, out, ...books] = process.argv.slice(2);
|
|
const browser = await chromium.launch();
|
|
const page = await browser.newPage();
|
|
await page.goto('about:blank');
|
|
await page.addScriptTag({ path: path.join(web, 'static/js/jszip.min.js') });
|
|
await page.addScriptTag({ path: path.join(web, 'static/js/app/40-epub.js') });
|
|
const result = {};
|
|
for (const file of books) {
|
|
const b64 = fs.readFileSync(file).toString('base64');
|
|
result[file] = await page.evaluate(async (b64) => {
|
|
const bin = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
const book = await parseEpub(bin.buffer);
|
|
const el = document.createElement('div');
|
|
el.innerHTML = book.chapters.join('');
|
|
document.body.appendChild(el);
|
|
const SEL = 'p, h1, h2, h3, h4, h5, h6, li, blockquote, dt, dd, figcaption, div:not(:has(*))'; // EPUB_BLOCK_SELECTOR, 44-books.js
|
|
const blocks = Array.from(el.querySelectorAll(SEL));
|
|
const chapters = Array.from(el.querySelectorAll('[data-epub-src]'));
|
|
// _bookCharIndex (44-books.js): offset of each block's first text node among all text nodes
|
|
const offsets = new Map();
|
|
let pos = 0;
|
|
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
for (let n; (n = walker.nextNode());) { offsets.set(n, pos); pos += n.nodeValue.length; }
|
|
const starts = [];
|
|
blocks.forEach((b, i) => {
|
|
const first = document.createTreeWalker(b, NodeFilter.SHOW_TEXT).nextNode();
|
|
starts[i] = first ? offsets.get(first) : (i ? starts[i - 1] + blocks[i - 1].textContent.length : 0);
|
|
});
|
|
const res = blocks.map((b, i) => ({
|
|
start: starts[i],
|
|
tag: b.tagName.toLowerCase(),
|
|
text: b.textContent.replace(/\s+/g, ' ').trim(),
|
|
len: b.textContent.length,
|
|
ch: chapters.findIndex((c) => c.contains(b)),
|
|
}));
|
|
el.remove();
|
|
return { title: book.title, chapters: chapters.length, total: pos || 1, blocks: res };
|
|
}, b64);
|
|
console.error(path.basename(file), result[file].blocks.length, 'blocks');
|
|
}
|
|
fs.writeFileSync(out, JSON.stringify(result));
|
|
await browser.close();
|
|
})().catch((e) => { console.error(e); process.exit(1); });
|