View Single Post
Old 08-12-2026, 06:49 PM   #17
Kayadelenium
Connoisseur
Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!Kayadelenium rocks like Gibraltar!
 
Kayadelenium's Avatar
 
Posts: 89
Karma: 100000
Join Date: Jul 2026
Location: Planet Earth
Device: Kobo Forma
Doitsu — both of the things you asked for are in 0.10.0, published a few minutes ago.

Code:
cargo install epubsana
1. Your plugin no longer has to apply everything.

That it does is my fault, not a choice you made: the only non-interactive mode was -y, which approves every fix. There was no way to show someone a plan and then act on the part they picked.

There is now. --apply takes a list and approves exactly those:

Code:
$ epubsana -i book.epub --dry-run
[1] WOULD APPLY Make 1 invalid NCX id a valid XML NCName in toc.ncx
[2] WOULD APPLY Drop 25 legacy <a name> attributes in chapter-04.html
[3] WOULD APPLY Drop 19 legacy <a name> attributes in chapter-05.html

$ epubsana -i book.epub --apply 1,3
[1] APPLIED Make 1 invalid NCX id a valid XML NCName in toc.ncx
[2] SKIPPED Drop 25 legacy <a name> attributes in chapter-04.html
[3] APPLIED Drop 19 legacy <a name> attributes in chapter-05.html
A selector is a plan index or a fix id (--apply 2,fix.html_entities works). Under --format json each item now carries data.index, so you round-trip the number rather than counting items.

A selector that matches nothing fails the whole run and writes no file. Applying the half of a list that happened to match would tell you something untrue about a book you are about to hand back to someone.

The two calls only line up because planning is deterministic — same input and same epubveri version, same fixes in the same order. That is now a documented guarantee rather than something that happens to be true, because your workflow depends on it.

2. Every edit now names its file.

You were right that this was the real problem, and right about where it was: the path was already there internally and the JSON emitter was dropping it. So data.changes entries are no longer bare strings:

Code:
"changes": [
  { "path": "toc.ncx", "note": "rename NCX id \"59a835d2…\" → \"id_59a835d2…\"" }
]
Which means the set of files a run touched is one expression, and you never open the repaired EPUB to find out:

Code:
touched = {c["path"] for it in items for c in it["data"]["changes"]}
Those paths are in the --dry-run output too, so you can tell a user which files a fix would touch before anything happens.

Heads-up: this will break code that reads those strings. They used to be plain strings; each one is now an object. Read .note and you get the old string back, unchanged.

I could have left changes alone and added a second field beside it with the paths in. I did not, because a duplicate field can never be taken out again once anyone depends on it — you end up with two ways of saying one thing, permanently. Breaking it is only cheap before 1.0, so it was now or never. Sorry if it costs you an edit.

One thing to watch when copying files back. The packaging fix (PKG-006) reports "path": "mimetype", but that file's content does not change — what changes is where it sits in the ZIP and whether it is compressed. Copying mimetype across will not reproduce it. If you see that fix, either re-save the container yourself or take epubsana's own output for that book. It is the only fix in the set that behaves this way.

Also worth knowing: entries epubsana did not touch are copied through byte-for-byte — same bytes, same compression, same timestamps — so an untouched file compares equal without inflating anything. Not that you should need to compare at all now.

The per-fix unified diff is not in this release.

I am not quietly dropping it — I want you to decide. You asked for it first, but then said that for a small file you would rather replace the whole thing than apply a diff, and Kevin pointed out that Sigil Checkpoints already show a user what changed. Between those two the case for it got a lot weaker.

So: does the file list cover what you need? If it does, I would rather not build something nobody ends up using. If you still want the diffs, say so and I will put them back on the list.

There are also two new fixers in 0.10.0 (empty optional Dublin Core elements, and superseded media-type names like application/vnd.ms-opentypefont/otf). Both clear usage-severity findings, so neither makes a book valid that was not — they remove lines from an epubcheck report that describe nothing.

The full --apply documentation, with the error cases spelled out, is in docs/USAGE.md:
https://github.com/veripublica/epubs.../docs/USAGE.md

If something in the JSON is still awkward to consume, tell me — it is much easier to change now than once more than one plugin depends on it.
Kayadelenium is offline   Reply With Quote