bindery-cli 0.27.0__tar.gz → 0.29.0__tar.gz
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.
- bindery_cli-0.29.0/.gitignore +21 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/CLAUDE.md +14 -11
- bindery_cli-0.27.0/README.md → bindery_cli-0.29.0/PKG-INFO +81 -12
- bindery_cli-0.27.0/PKG-INFO → bindery_cli-0.29.0/README.md +59 -28
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/logo.svg +1 -1
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/patchnotes.md +153 -11
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/pyproject.toml +12 -5
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/roadmap.md +187 -7
- bindery_cli-0.29.0/scripts/FastSweep.java +102 -0
- bindery_cli-0.29.0/scripts/fast_sweep.py +169 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/spec.md +64 -9
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/__init__.py +2 -2
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/audit.py +186 -4
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/cli.py +454 -7
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/epub.py +415 -2
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/transforms.py +52 -8
- bindery_cli-0.29.0/testing_facility/top500candidates/REPORT.md +20 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_audit.py +200 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_cli.py +315 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_epub.py +290 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_transforms.py +46 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/uv.lock +15 -7
- bindery_cli-0.27.0/.gitignore +0 -12
- bindery_cli-0.27.0/scripts/FastSweep.java +0 -40
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/.github/workflows/ci.yml +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/.github/workflows/publish.yml +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/AGENTS.md +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/LICENSE +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/docs/screenshots/library-sweep.png +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/run_tests.sh +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_block_in_inline.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_css_illegal_tags.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_duplicate_playorder.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_empty_body.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_illegal_tags.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_invalid_ids.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_invalid_st_tag.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_invalid_value_attr.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_missing_images.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/scripts/find_missing_title.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/__main__.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/library.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/pagination.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/reserialize.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/validate.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/src/bindery/watermark.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/agda.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/agda_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/last_man_out.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/last_man_out_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/leaves_of_grass.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/leaves_of_grass_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/leaves_of_grass_st_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/leaves_of_grass_tags_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/muse_of_nightmares.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/muse_of_nightmares_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/poor_mans_fight.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/poor_mans_fight_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_block_in_inline.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_duplicate_playorder.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_empty_body.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_illegal_tags.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_invalid_ids.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_invalid_value.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/test_missing_title.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/unworthy_gods.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/test_facility/unworthy_gods_fixed.epub +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/testing_facility/books_to_fix/.gitkeep +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_library.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_pagination.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_reserialize.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_validate.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_version.py +0 -0
- {bindery_cli-0.27.0 → bindery_cli-0.29.0}/tests/test_watermark.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
.venv/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
|
|
10
|
+
# Ignore actual books used for testing to prevent IP leaks
|
|
11
|
+
testing_facility/books_to_fix/*
|
|
12
|
+
!testing_facility/books_to_fix/.gitkeep
|
|
13
|
+
testing_facility/top500candidates/*.epub
|
|
14
|
+
|
|
15
|
+
# Compiled Java scratch (FastSweep harness)
|
|
16
|
+
*.class
|
|
17
|
+
|
|
18
|
+
# Local virtualenvs (each carries its own auto-generated inner .gitignore,
|
|
19
|
+
# which is the only reason they were invisible until now)
|
|
20
|
+
venv/
|
|
21
|
+
.venv_ci/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# CLAUDE.md (
|
|
1
|
+
# CLAUDE.md (bindery-cli)
|
|
2
2
|
|
|
3
3
|
Per-project guidance. Overrides the global file where they conflict.
|
|
4
4
|
|
|
@@ -14,13 +14,13 @@ Born from the 2026 library audit (see the user memory `calibre-library-epubcheck
|
|
|
14
14
|
|
|
15
15
|
- **Minimal Dependencies.** Runtime deps are exactly: `tqdm` (progress/output),
|
|
16
16
|
`vir-tui` (shared TUI rendering) and `cquarry` (read-only Calibre `metadata.db` access,
|
|
17
|
-
adopted in v0.16.0)
|
|
18
|
-
|
|
19
|
-
exercises the current library. `html5lib` remains the one approved
|
|
17
|
+
adopted in v0.16.0), pinned as PyPI ranges in pyproject.toml (`vir-tui>=2.2.0`,
|
|
18
|
+
`cquarry>=1.9.0`); `uv.lock` records the resolved versions. `html5lib` remains the one approved
|
|
20
19
|
heavy-parsing exception (used only for the `--reserialize` fix, imported lazily so every
|
|
21
20
|
other mode runs without it). Tests use the standard `unittest` framework. epubcheck is an
|
|
22
|
-
external CLI dependency
|
|
23
|
-
|
|
21
|
+
external CLI dependency expected on PATH; `--install-to-calibre` needs no external
|
|
22
|
+
binary, it re-registers the format through cquarry's write module (the calibredb
|
|
23
|
+
subprocess was retired in v0.24.0). Before adding any further Python package, stop and ask.
|
|
24
24
|
- **Semantics-preserving transforms by default, everything else fenced behind a flag.**
|
|
25
25
|
The always-on core is exactly five well-formedness fixes (prolog junk, duplicate
|
|
26
26
|
`xmlns`, bare `&`, named entities, void self-closing) plus the NCX pipeline; every core
|
|
@@ -28,7 +28,8 @@ Born from the 2026 library audit (see the user memory `calibre-library-epubcheck
|
|
|
28
28
|
visible content. Two kinds of exceptions exist, both opt-in and off unless requested:
|
|
29
29
|
* **Structural repairs** (`--fix-empty-body`, `--fix-missing-title`, `--fix-id-colons`,
|
|
30
30
|
`--fix-page-map`, `--strip-epub3-attrs`, `--downgrade-epub3-tags`,
|
|
31
|
-
`--unwrap-block-in-inline`, `--strip-invalid-value`, `--unwrap-illegal-tags
|
|
31
|
+
`--unwrap-block-in-inline`, `--strip-invalid-value`, `--unwrap-illegal-tags`,
|
|
32
|
+
`--prune-missing-resources`, `--strip-broken-anchors`, `--encode-url-spaces`;
|
|
32
33
|
transforms.py, threaded through epub.py): they alter markup structure or fabricate
|
|
33
34
|
minimal content. v0.14–v0.16 ran these unconditionally, which broke this rule;
|
|
34
35
|
v0.17.0 restored it. `--unwrap-illegal-tags` additionally protects any illegal-tag
|
|
@@ -50,8 +51,10 @@ Born from the 2026 library audit (see the user memory `calibre-library-epubcheck
|
|
|
50
51
|
visible (they clear errors), so a run with no measurable improvement is a noop and
|
|
51
52
|
nothing is applied. Changing either bar means re-running the library dry run.
|
|
52
53
|
- **Library writes are sacred.** Replacement must stay atomic (temp in same dir, then
|
|
53
|
-
`os.replace`), touch only the `.epub`, preserve mode, and be dry-run by default.
|
|
54
|
-
|
|
54
|
+
`os.replace`), touch only the `.epub`, preserve mode, and be dry-run by default.
|
|
55
|
+
Calibre format installation goes through cquarry's write module (`--install-to-calibre`;
|
|
56
|
+
the calibredb subprocess was retired in v0.24.0). Never write to the library without
|
|
57
|
+
`--apply`. Test every change on `/tmp` copies first.
|
|
55
58
|
|
|
56
59
|
## Layout
|
|
57
60
|
|
|
@@ -59,11 +62,11 @@ Born from the 2026 library audit (see the user memory `calibre-library-epubcheck
|
|
|
59
62
|
- `src/bindery/pagination.py`: the opt-in lossy page-number strip (runhead detection, page-layer decision, block-centric removal/merge, safety nets).
|
|
60
63
|
- `src/bindery/watermark.py`: the opt-in lossy watermark strip (anchored and anchorless signature removal).
|
|
61
64
|
- `src/bindery/reserialize.py`: structural repair via `html5lib`.
|
|
62
|
-
- `src/bindery/audit.py`: read-only body-text audits (`content`, `pagenumbers`, `emptytext`, `ocr`, and `monolithic` since v0.21.0, `--max-doc-chars N`) behind the `audit` subcommand (v0.15.0). Since v0.18.0 library mode resolves EPUB paths through `cquarry.get_format_path()` and can apply a tag to flagged books via `cquarry.write.WritableCalibreDB` (only with explicit `--tag`; the only sanctioned write path). Since v0.19.0 `audit --id BOOK_ID` audits a single library book through cquarry's single-entity `get_book()` fetch (no library-wide cache; supports `--tag`; incompatible with the directory argument; comma lists since v0.23.0). Since v0.22.0 every archive entry is fully read (CRC + decompression) before analysis — a damaged entry reports its own CORRUPT verdict instead of feeding emptytext — and manifest/NCX references to absent files classify as `convention` (bloated ToC, consecutive span) or `fragment` (broken span). Hazard: the scan loop reuses `tag` as its per-book display column, so the `--tag` argument is captured as `audit_tag` at function entry — do not collapse them again.
|
|
65
|
+
- `src/bindery/audit.py`: read-only body-text audits (`content`, `pagenumbers`, `emptytext`, `ocr`, and `monolithic` since v0.21.0, `--max-doc-chars N`) behind the `audit` subcommand (v0.15.0). Since v0.18.0 library mode resolves EPUB paths through `cquarry.get_format_path()` and can apply a tag to flagged books via `cquarry.write.WritableCalibreDB` (only with explicit `--tag`; the only sanctioned write path). Since v0.19.0 `audit --id BOOK_ID` audits a single library book through cquarry's single-entity `get_book()` fetch (no library-wide cache; supports `--tag`; incompatible with the directory argument; comma lists since v0.23.0). Since v0.22.0 every archive entry is fully read (CRC + decompression) before analysis — a damaged entry reports its own CORRUPT verdict instead of feeding emptytext — and manifest/NCX references to absent files classify as `convention` (bloated ToC, consecutive span) or `fragment` (broken span). Hazard: the scan loop reuses `tag` as its per-book display column, so the `--tag` argument is captured as `audit_tag` at function entry — do not collapse them again. Since v0.29.0 `audit --json FILE` writes per-file analyzer verdicts in the `library --json` shape (directory, library, and single-book modes; with `--id`, exactly one id; emptytext omitted from a record when the archive verdict owns the body-text story).
|
|
63
66
|
- `src/bindery/epub.py`: archive rewrite, NCX uid sync, RepairReport, mismatch detection, and the opt-in structural-repair plumbing (including the CSS precondition scan).
|
|
64
67
|
- `src/bindery/validate.py`: epubcheck wrapper, the `gate` (improvement) and `no_worse` (no-regression, for the lossy strips) acceptance bars.
|
|
65
68
|
- `src/bindery/library.py`: Calibre walk, atomic replace, backups, and native format installation. Since v0.19.0 `CalibreIdResolver` resolves the book id from `metadata.db` through cquarry (one lazy path→id map per run; since v0.20.0 the map comes from `CalibreDB.format_path_index()`, re-normalized with `resolve().lower()` for the resolver's symlinked-directory and case-insensitive matching) — the `(id)` directory-name regex is only the no-catalog fallback. Since v0.24.0 `install_format()` places the repaired file atomically and updates the `data` row through `cquarry.write.WritableCalibreDB` (remove+add in one `batch()`; `add_format` refuses duplicates); the external `calibredb` CLI is gone. Never reintroduce directory-name guessing as the primary source: renamed/mismatched directories would replace the wrong book.
|
|
66
|
-
- `src/bindery/cli.py`: `repair` and `library` subcommands, including `--all` and `--install-to-calibre
|
|
69
|
+
- `src/bindery/cli.py`: `repair` and `library` subcommands, including `--all` and `--install-to-calibre`; since v0.29.0 also the `run` verbs — `phase1` (pre-import vetting over loose files: the audit battery composed with one gated repair sweep; read-only until `--apply-lossy`, which IS the lossy-strip consent) and `phase3` (the scoped post-import apply step: `library --id --sweep --only all --apply --all --install-to-calibre` plus a pre/post summary; mechanically refuses unscoped sweeps with exit 2). Both take `--non-interactive` and surface open questions as `decisions_needed` in their JSON, never prompts. They drive the shipped `run_library` through the real parser, so no flag can drift between the verb and the subcommand it wraps.
|
|
67
70
|
- `tests/`: transforms, end-to-end repair, atomic replace, pagination, watermarks, the audit analyzers (content/pagenumbers/emptytext/ocr/monolithic, archive integrity, spine classification), the library sweep and `CalibreIdResolver`, validate, reserialize, CLI wiring, and the version pin.
|
|
68
71
|
|
|
69
72
|
## Conventions
|
|
@@ -1,16 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bindery-cli
|
|
3
|
+
Version: 0.29.0
|
|
4
|
+
Summary: Audit EPUB body text and repair broken EPUBs with deterministic, epubcheck-gated fixes; atomic in-place replacement in a Calibre library.
|
|
5
|
+
Author: vrnvctss
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: calibre,ebook,epub,epubcheck,html-repair,repair,watermarks
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.14
|
|
16
|
+
Requires-Dist: cquarry>=1.9.0
|
|
17
|
+
Requires-Dist: tqdm
|
|
18
|
+
Requires-Dist: vir-tui>=2.2.0
|
|
19
|
+
Provides-Extra: reserialize
|
|
20
|
+
Requires-Dist: html5lib>=1.1; extra == 'reserialize'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
1
23
|
<div align="center">
|
|
2
|
-
<img src="logo.svg" width="96" height="96" alt="
|
|
3
|
-
<h1>
|
|
24
|
+
<img src="logo.svg" width="96" height="96" alt="bindery-cli logo"/>
|
|
25
|
+
<h1>bindery-cli</h1>
|
|
4
26
|
<p>Audit EPUB body text and repair broken EPUBs with safe, deterministic fixes, validated by epubcheck, with optional in-place replacement in a Calibre library.</p>
|
|
5
27
|
</div>
|
|
6
28
|
|
|
7
29
|
<p align="center">
|
|
8
|
-
<img src="docs/screenshots/library-sweep.png" alt="
|
|
30
|
+
<img src="docs/screenshots/library-sweep.png" alt="bindery-cli's dry-run library sweep: per-book epubcheck results and a summary table ending in 'no files written'">
|
|
9
31
|
</p>
|
|
10
32
|
|
|
11
33
|
## What it fixes
|
|
12
34
|
|
|
13
|
-
|
|
35
|
+
bindery-cli makes accidentally broken markup well-formed again. It does not rewrite or reflow content. The **default pass** applies only a small set of deterministic, semantics-preserving well-formedness fixes that real-world EPUBs (especially Calibre conversions) trip over — nothing else changes without an explicit flag:
|
|
14
36
|
|
|
15
37
|
- **Unclosed void elements** (`<link>`, `<br>`, `<img>`, ...) get self-closed.
|
|
16
38
|
- **Undeclared named entities** (` `, `°`, `é`, ...) become numeric character references that every XML parser understands.
|
|
@@ -38,6 +60,9 @@ Everything below is off until its flag is passed (or all at once via `--all`). T
|
|
|
38
60
|
- **`--strip-epub3-attrs`**: scrubs the EPUB3-only attributes epubcheck rejects on an EPUB2 package — `page-progression-direction`, `epub:type`, `aria-label` (a fixed, documented set; rendering is unchanged, and lookalikes like `type` or the wider aria family survive).
|
|
39
61
|
- **`--downgrade-epub3-tags`**: downgrades EPUB3/HTML5 semantic elements to their EPUB2 equivalents — `figure`/`section` to `div`, `figcaption` to `p` — keeping existing classes and appending the semantic name (`class="figure"`) as the styling hook. Tag names a stylesheet styles as an element selector are protected book-wide, so styled formatting is never destroyed.
|
|
40
62
|
- **`--unwrap-illegal-tags`**: strips completely invalid or deprecated HTML tags that break EPUB3 validation (`<st>`, `<sentence>`, `<o>`, `<w>`, `<pagebreak>`) while retaining their inner text. Any of those names styled as an *element selector* by an EPUB stylesheet (`.css` entries and inline `<style>` blocks alike; class/id selectors like `.st`/`#w` don't count) is protected for the whole book, guaranteeing format preservation.
|
|
63
|
+
- **`--prune-missing-resources`**: removes references to files the archive does not contain (RSC-007/PKG-010): dead `<link>` elements, anchors' `href` to absent files (anchor text preserved), absent `<img>` sources (replaced by their alt text when they carry one), and orphaned non-spine OPF manifest items. Spine documents are never pruned: a missing spine document is reported as a damaged fragment, not silently dropped.
|
|
64
|
+
- **`--strip-broken-anchors`**: strips href attributes that cannot resolve, keeping the anchor text byte-for-byte: a `#fragment` the target document does not define (RSC-020/RSC-012; NCX navTargets fall back to the document target, so chapter navigation survives) and unresolvable URI schemes (`kindle:`, `file:`).
|
|
65
|
+
- **`--encode-url-spaces`**: percent-encodes raw spaces in `src`/`href` attribute values across the package (OPF manifest, NCX, content documents). A URL with a literal space is not a valid URL (RSC-020) and unresolvable on strict readers; the encoded form denotes the same file.
|
|
41
66
|
|
|
42
67
|
|
|
43
68
|
Three opt-in fixes are **lossy** and stand apart from the semantics-preserving rest:
|
|
@@ -61,7 +86,7 @@ The lossy modes (`--strip-pagination`, `--strip-broken-tags`, and `--strip-water
|
|
|
61
86
|
|
|
62
87
|
## Install
|
|
63
88
|
|
|
64
|
-
Python 3.14+, plus epubcheck on `PATH` for the gate. Dependencies: `tqdm` for progress output, plus the
|
|
89
|
+
Python 3.14+, plus epubcheck on `PATH` for the gate. Dependencies: `tqdm` for progress output, plus the VirInvictus libraries `vir-tui` (TUI rendering) and `cquarry` (read-only Calibre database access); `html5lib` remains an optional extra, needed only for `--reserialize`.
|
|
65
90
|
|
|
66
91
|
```sh
|
|
67
92
|
uv tool install bindery-cli # core tools
|
|
@@ -70,14 +95,16 @@ uv tool install "bindery-cli[reserialize]" # incl. --reserialize
|
|
|
70
95
|
|
|
71
96
|
## Usage
|
|
72
97
|
|
|
73
|
-
|
|
74
|
-
reports content flaws without touching anything,
|
|
75
|
-
library tree (dry run by default; `--apply` replaces accepted books atomically in place)
|
|
98
|
+
Four verbs: `bindery repair` fixes a single EPUB epubcheck-gated, `bindery audit`
|
|
99
|
+
reports content flaws without touching anything, `bindery library` sweeps a Calibre
|
|
100
|
+
library tree (dry run by default; `--apply` replaces accepted books atomically in place),
|
|
101
|
+
and `bindery run` composes the acquisition pathway's EPUB slices (phase-1 pre-import
|
|
102
|
+
vetting, phase-3 scoped post-import repair) for a calling agent.
|
|
76
103
|
The sections below take each in turn.
|
|
77
104
|
|
|
78
105
|
## Auditing
|
|
79
106
|
|
|
80
|
-
|
|
107
|
+
bindery-cli includes a comprehensive auditing tool to inspect EPUB body text for non-schema flaws that epubcheck cannot catch. It extracts and analyzes the visible text to detect content issues, producing console reports that can be used to filter your library or feed into `bindery repair`.
|
|
81
108
|
|
|
82
109
|
Scan a Calibre library for specific issues:
|
|
83
110
|
|
|
@@ -112,6 +139,12 @@ cd ~/docs/Calibre\ Library && bindery audit all --id 1234,1235
|
|
|
112
139
|
```
|
|
113
140
|
`--id` composes with `--tag`: the book is tagged only if the audit flags it.
|
|
114
141
|
|
|
142
|
+
**Machine-readable reports:** `--json FILE` writes the run's verdicts in the same shape `library --json` uses: one record per book with a `status` (`clean`/`problem`/`error`) and per-analyzer verdicts (`problem`, `status`, `details`), plus the always-on archive/spine verdicts (OK when they were silent). This is the contract downstream consumers of the pre-import vetting slice read.
|
|
143
|
+
```sh
|
|
144
|
+
bindery audit all ~/Downloads/epubs --json vetting-report.json
|
|
145
|
+
```
|
|
146
|
+
With `--id`, `--json` accepts exactly one book id (each single-book run writes the file wholesale).
|
|
147
|
+
|
|
115
148
|
**Spine-integrity reporting:** Both `audit` and `library` reports now classify manifest/NCX references that point to absent files. A `convention` verdict means the ToC is bloated but the present documents form a consecutive chapter span (e.g., the Wandering Inn official-build pattern, safe). A `fragment` verdict means the span itself is broken.
|
|
116
149
|
|
|
117
150
|
**Archive integrity:** every audit fully reads each archive entry (CRC + decompression), so a damaged download is reported CORRUPT — with the first broken entry named — instead of being mislabeled EMPTY by `emptytext`. `library --sweep` splits its `unreadable` bucket into `not_a_zip` / `truncated` / `encrypted` / `corrupt_entry`, so the right disease is visible without leaving the sweep.
|
|
@@ -162,7 +195,7 @@ bindery library ~/docs/Calibre\ Library --only all --apply --all --install-to-ca
|
|
|
162
195
|
- `--only {fatals,ncx,all}` restricts the candidate set. `ncx` targets NCX-001 mismatches (detected without epubcheck); `fatals` needs `--audit`.
|
|
163
196
|
- `--id <ids>` limits the sweep to a comma-separated list of Calibre book IDs, skipping the full library walk.
|
|
164
197
|
- `--audit CSV` (the `fatals,errors,warnings,path` format produced by an epubcheck sweep) skips clean books so a run is fast. Paths are resolved on both sides, and a CSV that matches nothing triggers a loud warning instead of silently selecting zero books.
|
|
165
|
-
- `--sweep` replaces the CSV step entirely: it runs a live epubcheck sweep for candidate selection and reuses each result as that book's before-measurement, so no book is checked twice.
|
|
198
|
+
- `--sweep` replaces the CSV step entirely: it runs a live epubcheck sweep for candidate selection and reuses each result as that book's before-measurement, so no book is checked twice. bindery-cli first tries a persistent Java daemon (`EpubcheckDaemon`) to eliminate the per-book JVM startup; in practice the daemon cannot boot on most installs (its classpath carries only epubcheck's launcher jar, not the `lib/` dependencies epubcheck itself puts on the classpath), and the run silently falls back to the standard per-book epubcheck subprocess at the usual seconds per book. Budget accordingly. Fixing the classpath is a recorded decision (it would also change the counts the gate measures), not a shipped behavior. Combine with `--only fatals` for a self-contained "find and fix the broken books" run.
|
|
166
199
|
- `--json FILE` writes a machine-readable report of the whole run (per-book status, before/after counts, applied flag, summary totals). `--manual-list FILE` writes the paths of every book that was not auto-repaired, one per line, ready for manual follow-up.
|
|
167
200
|
- `--apply` is required to write; the default is a dry run. `--backup DIR` mirrors originals before replacing; `--backup-inplace` writes `.epub.bak` beside each file.
|
|
168
201
|
- `--install-to-calibre` resolves the Calibre book id from `metadata.db` via cquarry (one read-only path→id map per run), so a hand-renamed `Author/Title (id)/` directory can never send the repaired file to the wrong book. The directory-name regex is only a no-catalog fallback; with neither, the file is saved atomically in place.
|
|
@@ -173,11 +206,47 @@ bindery library ~/docs/Calibre\ Library --only all --apply --all --install-to-ca
|
|
|
173
206
|
- Exit codes: 0 for a clean sweep, 1 for a usage error, 2 when any book was rejected, unreadable, or failed epubcheck (for scripts and cron).
|
|
174
207
|
- `repair` refuses to overwrite an existing output file unless `--force` is given.
|
|
175
208
|
|
|
209
|
+
## Run slices
|
|
210
|
+
|
|
211
|
+
`bindery run` wraps the acquisition pathway's EPUB steps as verbs. They add no
|
|
212
|
+
repair classes of their own: they compose the shipped audit and library runners
|
|
213
|
+
in the documented order, and both speak `--json` for a calling agent.
|
|
214
|
+
|
|
215
|
+
**`run phase1 DIR`** is the pre-import vetting slice for a directory of loose
|
|
216
|
+
files: the audit battery (corruption sweep, content battery, monolithic) in one
|
|
217
|
+
decompression pass per book, then the gated repair sweep (epubcheck, watermark
|
|
218
|
+
detection, repairability) over the same directory. The verb is read-only until
|
|
219
|
+
`--apply-lossy` is passed, and that flag IS the recorded lossy-strip consent;
|
|
220
|
+
`--backup DIR` mirrors originals first (keep backups outside the vetted
|
|
221
|
+
directory). Consent questions never block the verb: a read-only run that finds
|
|
222
|
+
repairable books records an `apply_lossy` entry in the JSON's
|
|
223
|
+
`decisions_needed` instead of prompting, and `--non-interactive` declares that
|
|
224
|
+
contract for callers. Exit codes per the `library` contract: 0 clean, 1 usage,
|
|
225
|
+
2 when any book is flagged, rejected, unreadable, or failed epubcheck (a
|
|
226
|
+
consent question alone is not trouble).
|
|
227
|
+
|
|
228
|
+
```sh
|
|
229
|
+
bindery run phase1 ~/Downloads --json phase1-report.json # read-only
|
|
230
|
+
bindery run phase1 ~/Downloads --apply-lossy --backup /tmp/phase1-backups
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**`run phase3 --ids IDS`** (from the library directory) is the post-import
|
|
234
|
+
scoped repair sweep: exactly `library --id IDS --sweep --only all --apply --all
|
|
235
|
+
--install-to-calibre` plus a pre/post epubcheck summary. It mechanically
|
|
236
|
+
refuses an unscoped library-wide sweep with exit 2 (a full-library sweep is a
|
|
237
|
+
dedicated hours-long task, never a verb call), and books left partial or
|
|
238
|
+
unreadable surface as `decisions_needed` in the JSON.
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
cd ~/docs/Calibre\ Library && bindery run phase3 --ids 5071,5072 --json phase3-report.json
|
|
242
|
+
```
|
|
243
|
+
|
|
176
244
|
## Companion scripts
|
|
177
245
|
|
|
178
|
-
`scripts/` holds standalone, read-only utilities that are useful for EPUB maintenance but fall outside
|
|
246
|
+
`scripts/` holds standalone, read-only utilities that are useful for EPUB maintenance but fall outside bindery-cli's repair contract (fixing what they find would be a content change, which bindery-cli makes only via the opt-in `--strip-pagination`):
|
|
179
247
|
|
|
180
248
|
- `find_missing_images.py`: scans a library tree and reports every book whose `<img>` tags point at files that do not exist inside the archive (a common defect in converted EPUBs). Reads the archives in place; nothing is unpacked or written. The library path is set at the bottom of the script.
|
|
249
|
+
- `fast_sweep.py`: compiles and drives `FastSweep.java`, a parallel JVM epubcheck sweep that pays JVM startup once instead of per book (a 7,000-book dry-run sweep drops from hours to minutes). `--mode=audit` emits the `fatals,errors,warnings,path` CSV that `bindery library --audit` reads; `--mode=extract` emits per-book error codes for aggregation (`--summary` prints the per-code report). The epubcheck jar is located from the `epubcheck` launcher or `EPUBCHECK_JAR`; the harness compiles once (`--release 25`) and is cached on mtime.
|
|
181
250
|
|
|
182
251
|
## Development
|
|
183
252
|
|
|
@@ -193,7 +262,7 @@ MIT. See [LICENSE](LICENSE).
|
|
|
193
262
|
|
|
194
263
|
## Support
|
|
195
264
|
|
|
196
|
-
If
|
|
265
|
+
If bindery-cli's useful to you and you'd like to chip in:
|
|
197
266
|
|
|
198
267
|
- liberapay · [liberapay.com/bdkl](https://liberapay.com/bdkl/)
|
|
199
268
|
- bitcoin
|
|
@@ -1,32 +1,16 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: bindery-cli
|
|
3
|
-
Version: 0.27.0
|
|
4
|
-
Summary: High-speed EPUB repair surgeon with a persistent Java validation daemon, safe HTML unwrapping, and native Calibre integration.
|
|
5
|
-
Author: vrnvctss
|
|
6
|
-
License: MIT
|
|
7
|
-
License-File: LICENSE
|
|
8
|
-
Keywords: calibre,ebook,epub,epubcheck,html-repair,repair,watermarks
|
|
9
|
-
Requires-Python: >=3.14
|
|
10
|
-
Requires-Dist: cquarry>=1.9.0
|
|
11
|
-
Requires-Dist: tqdm
|
|
12
|
-
Requires-Dist: vir-tui>=2.2.0
|
|
13
|
-
Provides-Extra: reserialize
|
|
14
|
-
Requires-Dist: html5lib>=1.1; extra == 'reserialize'
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
|
|
17
1
|
<div align="center">
|
|
18
|
-
<img src="logo.svg" width="96" height="96" alt="
|
|
19
|
-
<h1>
|
|
2
|
+
<img src="logo.svg" width="96" height="96" alt="bindery-cli logo"/>
|
|
3
|
+
<h1>bindery-cli</h1>
|
|
20
4
|
<p>Audit EPUB body text and repair broken EPUBs with safe, deterministic fixes, validated by epubcheck, with optional in-place replacement in a Calibre library.</p>
|
|
21
5
|
</div>
|
|
22
6
|
|
|
23
7
|
<p align="center">
|
|
24
|
-
<img src="docs/screenshots/library-sweep.png" alt="
|
|
8
|
+
<img src="docs/screenshots/library-sweep.png" alt="bindery-cli's dry-run library sweep: per-book epubcheck results and a summary table ending in 'no files written'">
|
|
25
9
|
</p>
|
|
26
10
|
|
|
27
11
|
## What it fixes
|
|
28
12
|
|
|
29
|
-
|
|
13
|
+
bindery-cli makes accidentally broken markup well-formed again. It does not rewrite or reflow content. The **default pass** applies only a small set of deterministic, semantics-preserving well-formedness fixes that real-world EPUBs (especially Calibre conversions) trip over — nothing else changes without an explicit flag:
|
|
30
14
|
|
|
31
15
|
- **Unclosed void elements** (`<link>`, `<br>`, `<img>`, ...) get self-closed.
|
|
32
16
|
- **Undeclared named entities** (` `, `°`, `é`, ...) become numeric character references that every XML parser understands.
|
|
@@ -54,6 +38,9 @@ Everything below is off until its flag is passed (or all at once via `--all`). T
|
|
|
54
38
|
- **`--strip-epub3-attrs`**: scrubs the EPUB3-only attributes epubcheck rejects on an EPUB2 package — `page-progression-direction`, `epub:type`, `aria-label` (a fixed, documented set; rendering is unchanged, and lookalikes like `type` or the wider aria family survive).
|
|
55
39
|
- **`--downgrade-epub3-tags`**: downgrades EPUB3/HTML5 semantic elements to their EPUB2 equivalents — `figure`/`section` to `div`, `figcaption` to `p` — keeping existing classes and appending the semantic name (`class="figure"`) as the styling hook. Tag names a stylesheet styles as an element selector are protected book-wide, so styled formatting is never destroyed.
|
|
56
40
|
- **`--unwrap-illegal-tags`**: strips completely invalid or deprecated HTML tags that break EPUB3 validation (`<st>`, `<sentence>`, `<o>`, `<w>`, `<pagebreak>`) while retaining their inner text. Any of those names styled as an *element selector* by an EPUB stylesheet (`.css` entries and inline `<style>` blocks alike; class/id selectors like `.st`/`#w` don't count) is protected for the whole book, guaranteeing format preservation.
|
|
41
|
+
- **`--prune-missing-resources`**: removes references to files the archive does not contain (RSC-007/PKG-010): dead `<link>` elements, anchors' `href` to absent files (anchor text preserved), absent `<img>` sources (replaced by their alt text when they carry one), and orphaned non-spine OPF manifest items. Spine documents are never pruned: a missing spine document is reported as a damaged fragment, not silently dropped.
|
|
42
|
+
- **`--strip-broken-anchors`**: strips href attributes that cannot resolve, keeping the anchor text byte-for-byte: a `#fragment` the target document does not define (RSC-020/RSC-012; NCX navTargets fall back to the document target, so chapter navigation survives) and unresolvable URI schemes (`kindle:`, `file:`).
|
|
43
|
+
- **`--encode-url-spaces`**: percent-encodes raw spaces in `src`/`href` attribute values across the package (OPF manifest, NCX, content documents). A URL with a literal space is not a valid URL (RSC-020) and unresolvable on strict readers; the encoded form denotes the same file.
|
|
57
44
|
|
|
58
45
|
|
|
59
46
|
Three opt-in fixes are **lossy** and stand apart from the semantics-preserving rest:
|
|
@@ -77,7 +64,7 @@ The lossy modes (`--strip-pagination`, `--strip-broken-tags`, and `--strip-water
|
|
|
77
64
|
|
|
78
65
|
## Install
|
|
79
66
|
|
|
80
|
-
Python 3.14+, plus epubcheck on `PATH` for the gate. Dependencies: `tqdm` for progress output, plus the
|
|
67
|
+
Python 3.14+, plus epubcheck on `PATH` for the gate. Dependencies: `tqdm` for progress output, plus the VirInvictus libraries `vir-tui` (TUI rendering) and `cquarry` (read-only Calibre database access); `html5lib` remains an optional extra, needed only for `--reserialize`.
|
|
81
68
|
|
|
82
69
|
```sh
|
|
83
70
|
uv tool install bindery-cli # core tools
|
|
@@ -86,14 +73,16 @@ uv tool install "bindery-cli[reserialize]" # incl. --reserialize
|
|
|
86
73
|
|
|
87
74
|
## Usage
|
|
88
75
|
|
|
89
|
-
|
|
90
|
-
reports content flaws without touching anything,
|
|
91
|
-
library tree (dry run by default; `--apply` replaces accepted books atomically in place)
|
|
76
|
+
Four verbs: `bindery repair` fixes a single EPUB epubcheck-gated, `bindery audit`
|
|
77
|
+
reports content flaws without touching anything, `bindery library` sweeps a Calibre
|
|
78
|
+
library tree (dry run by default; `--apply` replaces accepted books atomically in place),
|
|
79
|
+
and `bindery run` composes the acquisition pathway's EPUB slices (phase-1 pre-import
|
|
80
|
+
vetting, phase-3 scoped post-import repair) for a calling agent.
|
|
92
81
|
The sections below take each in turn.
|
|
93
82
|
|
|
94
83
|
## Auditing
|
|
95
84
|
|
|
96
|
-
|
|
85
|
+
bindery-cli includes a comprehensive auditing tool to inspect EPUB body text for non-schema flaws that epubcheck cannot catch. It extracts and analyzes the visible text to detect content issues, producing console reports that can be used to filter your library or feed into `bindery repair`.
|
|
97
86
|
|
|
98
87
|
Scan a Calibre library for specific issues:
|
|
99
88
|
|
|
@@ -128,6 +117,12 @@ cd ~/docs/Calibre\ Library && bindery audit all --id 1234,1235
|
|
|
128
117
|
```
|
|
129
118
|
`--id` composes with `--tag`: the book is tagged only if the audit flags it.
|
|
130
119
|
|
|
120
|
+
**Machine-readable reports:** `--json FILE` writes the run's verdicts in the same shape `library --json` uses: one record per book with a `status` (`clean`/`problem`/`error`) and per-analyzer verdicts (`problem`, `status`, `details`), plus the always-on archive/spine verdicts (OK when they were silent). This is the contract downstream consumers of the pre-import vetting slice read.
|
|
121
|
+
```sh
|
|
122
|
+
bindery audit all ~/Downloads/epubs --json vetting-report.json
|
|
123
|
+
```
|
|
124
|
+
With `--id`, `--json` accepts exactly one book id (each single-book run writes the file wholesale).
|
|
125
|
+
|
|
131
126
|
**Spine-integrity reporting:** Both `audit` and `library` reports now classify manifest/NCX references that point to absent files. A `convention` verdict means the ToC is bloated but the present documents form a consecutive chapter span (e.g., the Wandering Inn official-build pattern, safe). A `fragment` verdict means the span itself is broken.
|
|
132
127
|
|
|
133
128
|
**Archive integrity:** every audit fully reads each archive entry (CRC + decompression), so a damaged download is reported CORRUPT — with the first broken entry named — instead of being mislabeled EMPTY by `emptytext`. `library --sweep` splits its `unreadable` bucket into `not_a_zip` / `truncated` / `encrypted` / `corrupt_entry`, so the right disease is visible without leaving the sweep.
|
|
@@ -178,7 +173,7 @@ bindery library ~/docs/Calibre\ Library --only all --apply --all --install-to-ca
|
|
|
178
173
|
- `--only {fatals,ncx,all}` restricts the candidate set. `ncx` targets NCX-001 mismatches (detected without epubcheck); `fatals` needs `--audit`.
|
|
179
174
|
- `--id <ids>` limits the sweep to a comma-separated list of Calibre book IDs, skipping the full library walk.
|
|
180
175
|
- `--audit CSV` (the `fatals,errors,warnings,path` format produced by an epubcheck sweep) skips clean books so a run is fast. Paths are resolved on both sides, and a CSV that matches nothing triggers a loud warning instead of silently selecting zero books.
|
|
181
|
-
- `--sweep` replaces the CSV step entirely: it runs a live epubcheck sweep for candidate selection and reuses each result as that book's before-measurement, so no book is checked twice.
|
|
176
|
+
- `--sweep` replaces the CSV step entirely: it runs a live epubcheck sweep for candidate selection and reuses each result as that book's before-measurement, so no book is checked twice. bindery-cli first tries a persistent Java daemon (`EpubcheckDaemon`) to eliminate the per-book JVM startup; in practice the daemon cannot boot on most installs (its classpath carries only epubcheck's launcher jar, not the `lib/` dependencies epubcheck itself puts on the classpath), and the run silently falls back to the standard per-book epubcheck subprocess at the usual seconds per book. Budget accordingly. Fixing the classpath is a recorded decision (it would also change the counts the gate measures), not a shipped behavior. Combine with `--only fatals` for a self-contained "find and fix the broken books" run.
|
|
182
177
|
- `--json FILE` writes a machine-readable report of the whole run (per-book status, before/after counts, applied flag, summary totals). `--manual-list FILE` writes the paths of every book that was not auto-repaired, one per line, ready for manual follow-up.
|
|
183
178
|
- `--apply` is required to write; the default is a dry run. `--backup DIR` mirrors originals before replacing; `--backup-inplace` writes `.epub.bak` beside each file.
|
|
184
179
|
- `--install-to-calibre` resolves the Calibre book id from `metadata.db` via cquarry (one read-only path→id map per run), so a hand-renamed `Author/Title (id)/` directory can never send the repaired file to the wrong book. The directory-name regex is only a no-catalog fallback; with neither, the file is saved atomically in place.
|
|
@@ -189,11 +184,47 @@ bindery library ~/docs/Calibre\ Library --only all --apply --all --install-to-ca
|
|
|
189
184
|
- Exit codes: 0 for a clean sweep, 1 for a usage error, 2 when any book was rejected, unreadable, or failed epubcheck (for scripts and cron).
|
|
190
185
|
- `repair` refuses to overwrite an existing output file unless `--force` is given.
|
|
191
186
|
|
|
187
|
+
## Run slices
|
|
188
|
+
|
|
189
|
+
`bindery run` wraps the acquisition pathway's EPUB steps as verbs. They add no
|
|
190
|
+
repair classes of their own: they compose the shipped audit and library runners
|
|
191
|
+
in the documented order, and both speak `--json` for a calling agent.
|
|
192
|
+
|
|
193
|
+
**`run phase1 DIR`** is the pre-import vetting slice for a directory of loose
|
|
194
|
+
files: the audit battery (corruption sweep, content battery, monolithic) in one
|
|
195
|
+
decompression pass per book, then the gated repair sweep (epubcheck, watermark
|
|
196
|
+
detection, repairability) over the same directory. The verb is read-only until
|
|
197
|
+
`--apply-lossy` is passed, and that flag IS the recorded lossy-strip consent;
|
|
198
|
+
`--backup DIR` mirrors originals first (keep backups outside the vetted
|
|
199
|
+
directory). Consent questions never block the verb: a read-only run that finds
|
|
200
|
+
repairable books records an `apply_lossy` entry in the JSON's
|
|
201
|
+
`decisions_needed` instead of prompting, and `--non-interactive` declares that
|
|
202
|
+
contract for callers. Exit codes per the `library` contract: 0 clean, 1 usage,
|
|
203
|
+
2 when any book is flagged, rejected, unreadable, or failed epubcheck (a
|
|
204
|
+
consent question alone is not trouble).
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
bindery run phase1 ~/Downloads --json phase1-report.json # read-only
|
|
208
|
+
bindery run phase1 ~/Downloads --apply-lossy --backup /tmp/phase1-backups
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**`run phase3 --ids IDS`** (from the library directory) is the post-import
|
|
212
|
+
scoped repair sweep: exactly `library --id IDS --sweep --only all --apply --all
|
|
213
|
+
--install-to-calibre` plus a pre/post epubcheck summary. It mechanically
|
|
214
|
+
refuses an unscoped library-wide sweep with exit 2 (a full-library sweep is a
|
|
215
|
+
dedicated hours-long task, never a verb call), and books left partial or
|
|
216
|
+
unreadable surface as `decisions_needed` in the JSON.
|
|
217
|
+
|
|
218
|
+
```sh
|
|
219
|
+
cd ~/docs/Calibre\ Library && bindery run phase3 --ids 5071,5072 --json phase3-report.json
|
|
220
|
+
```
|
|
221
|
+
|
|
192
222
|
## Companion scripts
|
|
193
223
|
|
|
194
|
-
`scripts/` holds standalone, read-only utilities that are useful for EPUB maintenance but fall outside
|
|
224
|
+
`scripts/` holds standalone, read-only utilities that are useful for EPUB maintenance but fall outside bindery-cli's repair contract (fixing what they find would be a content change, which bindery-cli makes only via the opt-in `--strip-pagination`):
|
|
195
225
|
|
|
196
226
|
- `find_missing_images.py`: scans a library tree and reports every book whose `<img>` tags point at files that do not exist inside the archive (a common defect in converted EPUBs). Reads the archives in place; nothing is unpacked or written. The library path is set at the bottom of the script.
|
|
227
|
+
- `fast_sweep.py`: compiles and drives `FastSweep.java`, a parallel JVM epubcheck sweep that pays JVM startup once instead of per book (a 7,000-book dry-run sweep drops from hours to minutes). `--mode=audit` emits the `fatals,errors,warnings,path` CSV that `bindery library --audit` reads; `--mode=extract` emits per-book error codes for aggregation (`--summary` prints the per-code report). The epubcheck jar is located from the `epubcheck` launcher or `EPUBCHECK_JAR`; the harness compiles once (`--release 25`) and is cached on mtime.
|
|
197
228
|
|
|
198
229
|
## Development
|
|
199
230
|
|
|
@@ -209,7 +240,7 @@ MIT. See [LICENSE](LICENSE).
|
|
|
209
240
|
|
|
210
241
|
## Support
|
|
211
242
|
|
|
212
|
-
If
|
|
243
|
+
If bindery-cli's useful to you and you'd like to chip in:
|
|
213
244
|
|
|
214
245
|
- liberapay · [liberapay.com/bdkl](https://liberapay.com/bdkl/)
|
|
215
246
|
- bitcoin
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128" role="img" aria-label="bindery-cli logo">
|
|
2
2
|
<rect width="128" height="128" rx="24" fill="#181616"/>
|
|
3
3
|
<!-- book spine / cover being rebound -->
|
|
4
4
|
<rect x="30" y="26" width="68" height="76" rx="5" fill="none" stroke="#c5c9c5" stroke-width="5"/>
|