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>
38 lines
1.7 KiB
Python
38 lines
1.7 KiB
Python
"""Compare the TUI's block list with the web reader's (web_blocks.cjs output).
|
|
|
|
python compare_blocks.py web.json [name-part ...]
|
|
|
|
What must match is the number and tag of the blocks (they make the position anchors
|
|
shared by all clients). Text differences are reported too: they are cosmetic, but they
|
|
show where the TUI's HTML parser (lxml) reads a book differently from Chrome's.
|
|
Exit status 1 when a book's block list differs in length or tags.
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from diora_tui.blocks import load_flat_book
|
|
|
|
|
|
def main(ref_file, only):
|
|
ref = json.load(open(ref_file, encoding="utf-8"))
|
|
failed = False
|
|
for file, web in ref.items():
|
|
if only and not any(o in file for o in only):
|
|
continue
|
|
book = load_flat_book(Path(file))
|
|
wb, tb = web["blocks"], book.blocks
|
|
structure = len(wb) == len(tb) and all(w["tag"] == t.tag for w, t in zip(wb, tb))
|
|
texts = [i for i, (w, t) in enumerate(zip(wb, tb)) if w["text"] != t.text]
|
|
chars = [i for i, (w, t) in enumerate(zip(wb, tb)) if (w["start"], w["len"]) != (t.char_start, t.char_len)]
|
|
total_ok = web["total"] == book.total_chars
|
|
failed |= not structure
|
|
print(f"{'OK ' if structure else 'FAIL'} {os.path.basename(file)[:50]}: web {len(wb)} / tui {len(tb)} blocks, "
|
|
f"{len(texts)} with other text" + (f" (first: {texts[0]})" if texts else "") +
|
|
f"; chars {'equal' if not chars and total_ok else f'{len(chars)} blocks differ, total web {web['total']} / tui {book.total_chars}'}")
|
|
return 1 if failed else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1], sys.argv[2:]))
|