readalign 1.0.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.
- readalign-1.0.0/.gitignore +20 -0
- readalign-1.0.0/CHANGELOG.md +34 -0
- readalign-1.0.0/LICENSE +21 -0
- readalign-1.0.0/PKG-INFO +332 -0
- readalign-1.0.0/README.md +299 -0
- readalign-1.0.0/pyproject.toml +84 -0
- readalign-1.0.0/scripts/fetch_samples.py +196 -0
- readalign-1.0.0/src/readalign/__init__.py +3 -0
- readalign-1.0.0/src/readalign/__main__.py +8 -0
- readalign-1.0.0/src/readalign/align.py +340 -0
- readalign-1.0.0/src/readalign/asr.py +277 -0
- readalign-1.0.0/src/readalign/audio.py +362 -0
- readalign-1.0.0/src/readalign/cli.py +495 -0
- readalign-1.0.0/src/readalign/clock.py +42 -0
- readalign-1.0.0/src/readalign/epub.py +671 -0
- readalign-1.0.0/src/readalign/errors.py +27 -0
- readalign-1.0.0/src/readalign/fs.py +22 -0
- readalign-1.0.0/src/readalign/members.py +72 -0
- readalign-1.0.0/src/readalign/package.py +287 -0
- readalign-1.0.0/src/readalign/refine.py +175 -0
- readalign-1.0.0/src/readalign/report.py +153 -0
- readalign-1.0.0/src/readalign/sentences.py +78 -0
- readalign-1.0.0/src/readalign/sidecar.py +142 -0
- readalign-1.0.0/src/readalign/smil.py +77 -0
- readalign-1.0.0/src/readalign/textnorm.py +158 -0
- readalign-1.0.0/tests/conftest.py +138 -0
- readalign-1.0.0/tests/test_align.py +110 -0
- readalign-1.0.0/tests/test_asr.py +127 -0
- readalign-1.0.0/tests/test_cli.py +100 -0
- readalign-1.0.0/tests/test_epub.py +215 -0
- readalign-1.0.0/tests/test_output.py +261 -0
- readalign-1.0.0/tests/test_sidecar_report.py +122 -0
- readalign-1.0.0/tests/test_text.py +64 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*.egg-info/
|
|
4
|
+
.eggs/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.venv/
|
|
8
|
+
venv/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
htmlcov/
|
|
13
|
+
*.epub
|
|
14
|
+
!examples/aesop/*.epub
|
|
15
|
+
readalign-report.json
|
|
16
|
+
*-sidecars/
|
|
17
|
+
.readalign-cache/
|
|
18
|
+
examples/output/
|
|
19
|
+
.DS_Store
|
|
20
|
+
Thumbs.db
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 - 2026-09-18
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- `readalign build` turns a DRM-free audiobook and the matching DRM-free EPUB into one EPUB 3
|
|
8
|
+
with conformant Media Overlays: SMIL per content document, `media-overlay` on the manifest
|
|
9
|
+
items, `media:duration` per overlay plus a total, active-class metadata and a highlight
|
|
10
|
+
stylesheet. The overlay's `seq` elements mirror the document's nesting, so both skippability
|
|
11
|
+
(footnotes, endnotes, page numbers, sidebars, annotations) and escapability (tables, lists,
|
|
12
|
+
figures) work. The archive is written mimetype-first and uncompressed as OCF requires.
|
|
13
|
+
- `readalign check` validates the overlays in any EPUB: fragment ids resolve, audio is
|
|
14
|
+
manifested, no `clipEnd` runs past the duration ffprobe reports, declared durations match the
|
|
15
|
+
clips. `--json` for scripting.
|
|
16
|
+
- Transcription with faster-whisper, CUDA when available and CPU otherwise, word timestamps,
|
|
17
|
+
and a per-file transcript cache keyed by content and settings. A GPU that reports itself
|
|
18
|
+
present but cannot load its CUDA libraries falls back to the CPU with one warning line
|
|
19
|
+
naming what to install, rather than a traceback.
|
|
20
|
+
- Anchored alignment: unique shared n-grams, longest increasing subsequence, diff only in the
|
|
21
|
+
gaps. No global dynamic-programming matrix, and audio is streamed rather than buffered on the
|
|
22
|
+
way both in and out, so a 7.5 hour book builds in about 130 MB of memory and opening the
|
|
23
|
+
205 MB result costs 33 MB. Two runs of the same build produce byte-identical archives.
|
|
24
|
+
- Sidecars: one WebVTT per chapter and audio file, plus `align.json`. `readalign-report.json`
|
|
25
|
+
records coverage, drift, per-document statistics and every region that did not match.
|
|
26
|
+
- Handles one file spanning several chapters, several files in one chapter, audio chapter counts
|
|
27
|
+
that disagree with the spine, narrator front and back matter with no text counterpart, notes
|
|
28
|
+
and page-break markers, existing ids, non-UTF8 XHTML, and clip ends past the real duration.
|
|
29
|
+
- DRM-protected input (`.aax`, `.aa`, `.acsm`, an encrypted EPUB) exits 2 and is never opened.
|
|
30
|
+
- Wrong input gets one line and an exit code, never a traceback: a missing file, an unzipped
|
|
31
|
+
book passed as a directory, something that is not a zip, an EPUB 2 package, an empty audio
|
|
32
|
+
directory, or a file ffprobe cannot read.
|
|
33
|
+
- Optional `readalign[refine]` extra adds a CTC forced-alignment pass behind `--refine`. The
|
|
34
|
+
MMS-300m aligner it uses is CC BY-NC 4.0, so it is never downloaded by the default path.
|
readalign-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Christopher Bosch
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
readalign-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: readalign
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Turn an audiobook and the matching ebook you own into one EPUB 3 with Media Overlays.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Booyaka101/readalign
|
|
6
|
+
Project-URL: Repository, https://github.com/Booyaka101/readalign
|
|
7
|
+
Project-URL: Issues, https://github.com/Booyaka101/readalign/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/Booyaka101/readalign/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Christopher Bosch <cbosch101@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: accessibility,audiobook,epub,epub3,forced-alignment,media-overlays,read-along,smil,whisper
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
21
|
+
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: faster-whisper>=1.1
|
|
24
|
+
Requires-Dist: lxml>=5.0
|
|
25
|
+
Requires-Dist: numpy>=1.24
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.16; extra == 'dev'
|
|
29
|
+
Provides-Extra: refine
|
|
30
|
+
Requires-Dist: torch>=2.1; extra == 'refine'
|
|
31
|
+
Requires-Dist: torchaudio>=2.1; extra == 'refine'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# readalign
|
|
35
|
+
|
|
36
|
+
Turn an audiobook you own and the matching ebook you own into a single EPUB 3 with conformant
|
|
37
|
+
Media Overlays: the words highlight as the narrator reads them, in any reading system that
|
|
38
|
+
supports EPUB 3 read-aloud.
|
|
39
|
+
|
|
40
|
+
Everything runs on your machine. There is no account, no API key and no upload. The only network
|
|
41
|
+
access is the one-time download of the speech model.
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
readalign build --audio audiobook/ --epub book.epub --out read-along.epub
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## What it produces
|
|
48
|
+
|
|
49
|
+
- **an EPUB 3.3 file** containing your ebook, the audio, one SMIL overlay per chapter, the
|
|
50
|
+
`media-overlay` links, `media:duration` metadata and a highlight stylesheet. It passes
|
|
51
|
+
epubcheck 5.2.1 with zero messages.
|
|
52
|
+
- **`readalign-report.json`** next to the output: coverage, drift, per-document statistics, which
|
|
53
|
+
documents got no overlay and which regions never matched.
|
|
54
|
+
- **sidecars** (`<name>-sidecars/`): one WebVTT file per chapter and audio file, plus `align.json`
|
|
55
|
+
with every sentence's id, clip times, confidence and text. A rerun clears the `.vtt` files and
|
|
56
|
+
`align.json` it wrote last time, so nothing stale is left behind. Turn them off with
|
|
57
|
+
`--no-sidecars`.
|
|
58
|
+
|
|
59
|
+
## Requirements
|
|
60
|
+
|
|
61
|
+
- Python 3.11 or newer
|
|
62
|
+
- **ffmpeg and ffprobe on `PATH`** (readalign shells out to them; it does not vendor a copy).
|
|
63
|
+
Get them from <https://ffmpeg.org/download.html>.
|
|
64
|
+
- An NVIDIA GPU is optional. With one, a 7 hour book transcribes in about 12 minutes. On CPU the
|
|
65
|
+
same book takes hours, so use a smaller `--model` there. If a GPU is present but its CUDA
|
|
66
|
+
libraries will not load, readalign says so and carries on on the CPU.
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
pip install readalign
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
or, without installing anything permanently:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
uvx readalign build --audio ... --epub ... --out ...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
A public-domain pair is bundled in `examples/aesop`: two minutes of a LibriVox recording and the
|
|
83
|
+
matching Project Gutenberg text. From a checkout:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
readalign build \
|
|
87
|
+
--audio examples/aesop/aesop-section-37.mp3 \
|
|
88
|
+
--epub examples/aesop/aesop-two-fables.epub \
|
|
89
|
+
--out aligned.epub --model tiny.en --language en --device cpu
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Two minutes of audio is not worth spinning up a GPU for, hence `--device cpu`. Real output
|
|
93
|
+
from that command, on a cold cache:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
readalign 1.0.0
|
|
97
|
+
reading examples/aesop/aesop-two-fables.epub
|
|
98
|
+
2 content documents, 18 sentences
|
|
99
|
+
preparing 1 audio file(s)
|
|
100
|
+
[1/1] aesop-section-37.mp3 -> stream copy
|
|
101
|
+
transcribing
|
|
102
|
+
loading whisper model 'tiny.en' on cpu (int8)
|
|
103
|
+
[1/1] aesop-section-37.mp3: transcribing 1:58
|
|
104
|
+
298 words so far, 1:58/1:58 audio in 0:03 (35.8x realtime)
|
|
105
|
+
298 words in 3.3s on cpu
|
|
106
|
+
aligning
|
|
107
|
+
aligning 245 book tokens against 300 spoken tokens
|
|
108
|
+
wrote D:\Repos\ideas\readalign\aligned.epub (0.9 MB, 2 overlays, 0:01:11.640)
|
|
109
|
+
wrote 3 sidecar file(s) to D:\Repos\ideas\readalign\aligned-sidecars
|
|
110
|
+
sentences 18/18 timed (100.0%)
|
|
111
|
+
interpolated 0 low confidence 0
|
|
112
|
+
token match 95.1%
|
|
113
|
+
drift max 0.00s, p95 0.00s, threshold 2.50s
|
|
114
|
+
report D:\Repos\ideas\readalign\readalign-report.json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Run it again and the transcribing lines become `298 words from cache`.
|
|
118
|
+
|
|
119
|
+
The recording opens and closes with the LibriVox announcement, which has no counterpart in the
|
|
120
|
+
text. readalign trims it: the first overlay starts at 20.5 seconds.
|
|
121
|
+
|
|
122
|
+
To fetch the sample yourself, or a full-length pair to try:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
python scripts/fetch_samples.py short # rebuilds examples/aesop
|
|
126
|
+
python scripts/fetch_samples.py long # Frankenstein, 7.5 hours over 25 files
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Checking the result
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
$ readalign check aligned.epub
|
|
133
|
+
checking D:\Repos\ideas\readalign\aligned.epub
|
|
134
|
+
overlays 2
|
|
135
|
+
sentences 18
|
|
136
|
+
clip time 0.02 hours
|
|
137
|
+
media:duration 0.02 hours declared
|
|
138
|
+
result ok
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`check` walks the package document, opens every overlay, and verifies that each `text` element
|
|
142
|
+
points at a fragment that exists in a manifested content document, that each `audio` element
|
|
143
|
+
points at a manifested audio file, that `clipEnd` never runs past the real duration ffprobe
|
|
144
|
+
reports for that file, and that the declared `media:duration` values match the clips. It exits
|
|
145
|
+
non-zero if anything fails, and takes `--json` if you want to script it.
|
|
146
|
+
|
|
147
|
+
It is worth running the official validator too:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
java -jar epubcheck.jar aligned.epub
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## A full-length run
|
|
154
|
+
|
|
155
|
+
The 25-file LibriVox recording of *Frankenstein* (7 h 26 m) against the Standard Ebooks edition,
|
|
156
|
+
on an RTX 4090:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
reading frankenstein-se.epub
|
|
160
|
+
38 content documents, 3403 sentences
|
|
161
|
+
preparing 25 audio file(s)
|
|
162
|
+
transcribing
|
|
163
|
+
loading whisper model 'large-v3-turbo' on cuda (float16)
|
|
164
|
+
[1/25] frankenstein_00_shelley_64kb.mp3: transcribing 32:56
|
|
165
|
+
...
|
|
166
|
+
75799 words in 718.6s on cuda
|
|
167
|
+
aligning
|
|
168
|
+
aligning 78488 book tokens against 75827 spoken tokens
|
|
169
|
+
wrote frankenstein.epub (205.0 MB, 30 overlays, 6:59:25.256)
|
|
170
|
+
wrote 44 sidecar file(s) to frankenstein-sidecars
|
|
171
|
+
sentences 3250/3403 timed (95.5%)
|
|
172
|
+
interpolated 9 low confidence 10
|
|
173
|
+
token match 94.0%
|
|
174
|
+
drift max 3.98s, p95 0.00s, threshold 2.50s
|
|
175
|
+
untimed matter 128 sentences before the narration, 25 after
|
|
176
|
+
no overlay 8 document(s): text/titlepage.xhtml, text/imprint.xhtml, ...
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Twelve minutes of transcription, about ninety seconds for everything else. The result passes
|
|
180
|
+
epubcheck with no messages and `readalign check` reports the same 3250 sentences the build wrote.
|
|
181
|
+
|
|
182
|
+
That run exits 3, not 0, and the reason is worth understanding. The 8 documents with no overlay
|
|
183
|
+
are the title page, the imprint, Shelley's 1831 introduction, the preface, the dedication, the
|
|
184
|
+
epigraph, the colophon and the copyright page. The narrator reads none of them, so readalign
|
|
185
|
+
times none of them. The 153 untimed sentences are the same material. The max drift of 3.98s comes
|
|
186
|
+
from two sentences in the endnotes, which the narrator also never reads: one endnote matched a
|
|
187
|
+
phrase in the closing credits, so the endnote heading next to it got placed inside a 4 second
|
|
188
|
+
window instead of being left alone. Everything the narrator actually reads has a drift of zero.
|
|
189
|
+
`--drift-threshold 4` makes that run exit 0; the report tells you exactly which sentences moved
|
|
190
|
+
the number, so you can decide rather than guess.
|
|
191
|
+
|
|
192
|
+
## Drift, and what the number means
|
|
193
|
+
|
|
194
|
+
Drift is how far a sentence's start could be wrong, in seconds.
|
|
195
|
+
|
|
196
|
+
- A sentence anchored on its own narrated words has drift **0**. Its start is a word timestamp
|
|
197
|
+
from the recogniser, accurate to a few tens of milliseconds.
|
|
198
|
+
- A sentence the narrator skipped, or garbled past recognition, is placed proportionally in the
|
|
199
|
+
gap between its neighbours. Its drift is the width of that gap: the worst it can be out by.
|
|
200
|
+
- A sentence whose start had to be pushed forward to keep the overlay in order carries the
|
|
201
|
+
distance it was pushed. That is the signal for text matched to the wrong occurrence of itself.
|
|
202
|
+
|
|
203
|
+
`--drift-threshold` (default 2.5s) is a gate on the maximum, not the average. The EPUB is always
|
|
204
|
+
written; exceeding the threshold only changes the exit code to 3 so a script can catch it.
|
|
205
|
+
|
|
206
|
+
## How it works
|
|
207
|
+
|
|
208
|
+
1. **ffprobe** reads every audio file. FLAC, WAV, ALAC and WMA are transcoded to AAC because
|
|
209
|
+
EPUB 3 only takes MP3, MP4 audio and Opus; MP3, M4A/M4B and Opus are stream-copied, so no
|
|
210
|
+
audio is re-encoded unless it has to be.
|
|
211
|
+
2. **faster-whisper** transcribes with word timestamps. Transcripts are cached per file, keyed by
|
|
212
|
+
content and settings, so a second run over the same audiobook costs nothing.
|
|
213
|
+
3. Both sides are normalised to comparable tokens: case folded, accents stripped, punctuation
|
|
214
|
+
dropped, digits and roman numerals spelled out ("Chapter XVIII" and "chapter eighteen" become
|
|
215
|
+
the same tokens).
|
|
216
|
+
4. **Alignment** pins the two streams together with n-grams that occur exactly once on each side,
|
|
217
|
+
keeps the longest increasing chain of them, and runs a real diff only in the small regions
|
|
218
|
+
between anchors. That is what lets a 20 hour book align in a few hundred megabytes rather than
|
|
219
|
+
needing a 200k x 200k dynamic programming matrix.
|
|
220
|
+
5. The XHTML is rewritten so each sentence is wrapped in `<span id="ra-1-7">`. Existing ids are
|
|
221
|
+
never touched, and sentence boundaries that fall inside `<em>` or `<a>` are split without
|
|
222
|
+
changing a character of the text.
|
|
223
|
+
6. The overlay's `seq` elements mirror the document's own nesting. Footnotes, endnotes, page
|
|
224
|
+
numbers, sidebars and annotations carry their `epub:type`, so a reading system that offers
|
|
225
|
+
"skip footnotes" can, and tables, lists and figures nest the same way, so "escape this table"
|
|
226
|
+
works from three levels in.
|
|
227
|
+
7. Overlays, manifest items and metadata go into the package document, and the archive is
|
|
228
|
+
written mimetype-first and uncompressed as OCF requires.
|
|
229
|
+
|
|
230
|
+
## Options
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
--audio PATH a file, a directory of numbered files, or a glob
|
|
234
|
+
--epub PATH the matching DRM-free EPUB
|
|
235
|
+
--out PATH the EPUB to write
|
|
236
|
+
--model NAME faster-whisper model (default: large-v3-turbo)
|
|
237
|
+
--language CODE ISO code of the narration (default: detect)
|
|
238
|
+
--drift-threshold SECS exit 3 if any sentence drifts more than this (default: 2.5)
|
|
239
|
+
--sidecars/--no-sidecars write WebVTT and align.json next to the output (default: on)
|
|
240
|
+
--dry-run probe the inputs and print the plan, transcribe nothing
|
|
241
|
+
--narrator NAME value for the media:narrator metadata
|
|
242
|
+
--refine sharpen boundaries with the MMS CTC aligner (see below)
|
|
243
|
+
--device auto|cuda|cpu where to run the recogniser (default: auto)
|
|
244
|
+
--beam-size N whisper beam size (default: 5)
|
|
245
|
+
--no-vad disable the voice-activity filter
|
|
246
|
+
--audio-bitrate RATE bitrate when audio has to be transcoded (default: 96k)
|
|
247
|
+
--work-dir PATH scratch directory instead of a temporary one
|
|
248
|
+
--cache-dir PATH transcript cache location
|
|
249
|
+
--no-cache neither read nor write the transcript cache
|
|
250
|
+
--quiet only print errors and the summary
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Multiple files are ordered by natural sort, so `part2.mp3` comes before `part10.mp3`. A single
|
|
254
|
+
file covering the whole book is fine, and so is a chapter split over several files; readalign
|
|
255
|
+
works from a continuous timeline and does not assume one file per chapter.
|
|
256
|
+
|
|
257
|
+
### The optional refine pass
|
|
258
|
+
|
|
259
|
+
`pip install 'readalign[refine]'` adds torch and torchaudio, and `--refine` then runs a CTC
|
|
260
|
+
forced-alignment pass over each sentence boundary to sharpen it.
|
|
261
|
+
|
|
262
|
+
**The MMS-300m aligner that pass uses is licensed CC BY-NC 4.0, which means non-commercial use
|
|
263
|
+
only.** It is never downloaded unless you pass `--refine`, nothing in the default path touches
|
|
264
|
+
it, and any run that uses it records the restriction in the report's warnings.
|
|
265
|
+
|
|
266
|
+
## Limitations
|
|
267
|
+
|
|
268
|
+
- **No DRM, ever.** An `.aax`, `.aa` or `.acsm` file, or an EPUB with an `META-INF/encryption.xml`,
|
|
269
|
+
exits 2 with `readalign does not handle DRM-protected files`. readalign will not strip
|
|
270
|
+
protection and has no plans to.
|
|
271
|
+
- Output is only as good as the recognition. Heavy accents, music beds, dramatised multi-voice
|
|
272
|
+
productions and poetry read with long pauses all lower the token match rate; watch that number
|
|
273
|
+
in the report.
|
|
274
|
+
- Abridged recordings work, in the sense that the parts that are read get timed and the rest is
|
|
275
|
+
reported as untimed. They will never reach full coverage.
|
|
276
|
+
- Audio is embedded in the EPUB, so the file is as large as the audiobook. Frankenstein at 64 kbps
|
|
277
|
+
is 205 MB. There is no streaming or external-audio option in this version.
|
|
278
|
+
- The audio timeline is shared across files. A sentence never spans two audio files; if one would,
|
|
279
|
+
it is clipped at the file boundary.
|
|
280
|
+
- Sentence splitting is tuned for English-style punctuation. Other languages align fine but may
|
|
281
|
+
produce longer or shorter units than a native reader would choose.
|
|
282
|
+
|
|
283
|
+
## Reader support
|
|
284
|
+
|
|
285
|
+
Media Overlays are part of EPUB 3, but support is uneven. Readium-based readers (Thorium Reader
|
|
286
|
+
on desktop, Colibrio-based web readers) and Apple Books implement them. Kindle does not read EPUB
|
|
287
|
+
overlays at all, and several popular mobile readers ignore the `media-overlay` attribute and just
|
|
288
|
+
show the book. If a reader shows the text but plays no audio, it does not support overlays; the
|
|
289
|
+
file is still a valid EPUB and still readable.
|
|
290
|
+
|
|
291
|
+
readalign's output is validated against epubcheck 5.2.1, and `readalign check` is validated
|
|
292
|
+
against the reference Media Overlays title from the EPUB test suite at
|
|
293
|
+
<https://epubtest.org/test-books>.
|
|
294
|
+
|
|
295
|
+
## Exit codes
|
|
296
|
+
|
|
297
|
+
| code | meaning |
|
|
298
|
+
| ---- | ------- |
|
|
299
|
+
| 0 | success |
|
|
300
|
+
| 1 | bad input, missing file, missing ffmpeg, nothing could be aligned |
|
|
301
|
+
| 2 | the input is DRM-protected |
|
|
302
|
+
| 3 | the EPUB was written but max drift exceeded `--drift-threshold` |
|
|
303
|
+
| 130 | interrupted |
|
|
304
|
+
|
|
305
|
+
Failures print one line, not a traceback.
|
|
306
|
+
|
|
307
|
+
## Development
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
git clone https://github.com/Booyaka101/readalign
|
|
311
|
+
cd readalign
|
|
312
|
+
pip install -e '.[dev]'
|
|
313
|
+
python -m pytest tests -q
|
|
314
|
+
python -m ruff check .
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The suite runs in about a second and needs neither a GPU nor network access; it builds its own
|
|
318
|
+
EPUBs and synthesises word timings. A few cases need ffmpeg and skip without it, and one is
|
|
319
|
+
skipped unless `READALIGN_MO_TESTBOOK` points at a copy of the epubtest.org Media Overlays book.
|
|
320
|
+
|
|
321
|
+
## Distribution
|
|
322
|
+
|
|
323
|
+
The first distribution step is PyPI: `python -m build` then `twine upload dist/*`, which makes
|
|
324
|
+
`pip install readalign` and `uvx readalign` work for everyone. Nothing else in the project depends
|
|
325
|
+
on a hosted service.
|
|
326
|
+
|
|
327
|
+
## Licence
|
|
328
|
+
|
|
329
|
+
MIT. See [LICENSE](LICENSE).
|
|
330
|
+
|
|
331
|
+
The bundled sample in `examples/aesop` is public domain: the recording from LibriVox, the text
|
|
332
|
+
from Project Gutenberg. See `examples/aesop/README.md` for the sources.
|