commit 12380e6fd5a9908b24fb985ab2ad8d5953734840 truex <truex@equwal.com> 2026-09-26 23:32:50 -0700 Tell when SubRead waits for pages that could not be read "Make subtitles with SubRead" waits until all pages of the book are read. startMakeWhenRead ran only after a page that was read. After "OCR could not start..." or "2 pages could not be read.", the menu still said "Reading pages 38/40 first...", and SubRead never started. readBook now calls startMakeWhenRead also when the reading ends, and when it starts. waitMessage (messages.ts) gives "Reading pages N/M first..." while the reading runs. After the reading it gives "SubRead waits for 2 pages that could not be read." with Retry in the menu. Retry reads those pages, and startMakeWhenRead then starts SubRead after the last one.
README.md | 6 ++++-- src/main.ts | 22 ++++++++++++++++++---- src/messages.ts | 15 +++++++++++++++ test/messages.test.ts | 23 +++++++++++++++++++++++ 4 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md index ae5297d..f3d5b20 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,10 @@ is open. 2. In the menu, load the subtitles, or on Android press "Make subtitles with SubRead": pick the audiobook. SubRead starts when all pages are read ("Reading pages N/M first..."), and makes the `.srt` from the - audio and the text of the book. The result loads at once, is kept for - this PDF, and can be shared as a file. + audio and the text of the book. When pages could not be read, the + menu says "SubRead waits for 2 pages that could not be read." with + Retry, and SubRead starts when Retry reads them. The result loads at + once, is kept for this PDF, and can be shared as a file. 3. Start the player. The status strip shows its position. The page follows. diff --git a/src/main.ts b/src/main.ts index 3fb0ff4..eac2625 100644 --- a/src/main.ts +++ b/src/main.ts @@ -58,7 +58,7 @@ import { subreadQuestion, subreadResultText, UPDATING_CACHE_TEXT, - waitPagesText, + waitMessage, withForceOcr, type Message, } from './messages'; @@ -598,6 +598,9 @@ async function readPage( /** The OCR of the reading that runs now. */ let readingOcr: OcrJob<HTMLCanvasElement> | undefined; +/** True while readBook reads the pages. */ +let readingRuns = false; + /** * Reads every page of the book: the current page first, then the pages * after it, then the pages before it. The cues are aligned again as pages @@ -611,6 +614,7 @@ async function readBook(retry = false): Promise<void> { const { pdf, file } = state; if (!pdf || !file) return; const seq = ++state.readSeq; + readingRuns = true; // The OCR of the reading before stops at once. It does not finish its // page: two quick changes of a setting ran two OCR jobs at the same time. readingOcr?.stop(); @@ -622,6 +626,8 @@ async function readBook(retry = false): Promise<void> { state.cueParts = []; state.markedCue = -1; } + // A SubRead job that waits shows the reading again, for example after Retry. + startMakeWhenRead(); const total = pdf.numPages; const pending = new Set( Array.from({ length: total }, (_, i) => i).filter((i) => !state.pages[i]), @@ -697,7 +703,12 @@ async function readBook(retry = false): Promise<void> { if (seq === state.readSeq) say(`Cannot read the pages: ${String(err)}`); } finally { ocr.stop(); - if (seq === state.readSeq) setReading(readingEndMessage(failed, startFailed)); + if (seq === state.readSeq) { + readingRuns = false; + setReading(readingEndMessage(failed, startFailed)); + // A SubRead job that waits now waits for pages that could not be read. + startMakeWhenRead(); + } } } @@ -880,14 +891,17 @@ ui.makeSubs.addEventListener('click', async () => { /** * Starts the SubRead job that waits, when all pages of its book are read. - * Else tells how far the reading is. readBook calls this after each page. + * Else tells how far the reading is, or after the reading, how many pages + * could not be read, with Retry (waitMessage). readBook calls this after + * each page and at its end, so a Retry that reads those pages starts the job. */ function startMakeWhenRead(): void { const wait = state.makeWait; const pdf = state.pdf; if (!wait || !pdf || wait.book !== state.book) return; if (!allPagesRead()) { - ui.makeStatus.textContent = waitPagesText(state.pages.filter((p) => p).length, pdf.numPages); + const read = state.pages.filter((p) => p).length; + fill(ui.makeStatus, waitMessage(read, pdf.numPages, readingRuns)); return; } state.makeWait = null; diff --git a/src/messages.ts b/src/messages.ts index 71010ab..9a8029e 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -184,6 +184,21 @@ export function waitPagesText(read: number, total: number): string { return `Reading pages ${read}/${total} first...`; } +/** + * The make status while a SubRead job waits for the pages of its book. + * `reading` is true while the reading of the pages runs. After the reading, + * the pages that are not read could not be read: the job waits until Retry + * reads them. Before, the status kept "Reading pages 38/40 first...", and + * SubRead never started. + */ +export function waitMessage(read: number, total: number, reading: boolean): Message { + if (reading) return { text: waitPagesText(read, total) }; + return { + text: `SubRead waits for ${count(total - read, 'page')} that could not be read.`, + action: RETRY, + }; +} + /** What SubRead made: the cues, the language and the share of the lines found in the book. */ export function subreadResultText(r: { cues?: number; diff --git a/test/messages.test.ts b/test/messages.test.ts index 0a9e440..116ad76 100644 --- a/test/messages.test.ts +++ b/test/messages.test.ts @@ -26,10 +26,33 @@ import { subreadErrorText, subreadQuestion, subreadResultText, + waitMessage, waitPagesText, withForceOcr, } from '../src/messages'; +describe('waitMessage', () => { + const retry = { id: 'retry-reading', text: 'Retry' }; + + it('tells how far the reading is while it runs', () => { + expect(waitMessage(3, 40, true)).toEqual({ text: 'Reading pages 3/40 first...' }); + }); + + it('tells after the reading that SubRead waits for the pages that could not be read', () => { + // The finding: after "2 pages could not be read." the menu still said + // "Reading pages 38/40 first...", and SubRead never started. + expect(waitMessage(38, 40, false)).toEqual({ + text: 'SubRead waits for 2 pages that could not be read.', + action: retry, + }); + // OCR could not start: no page of the scan is read. + expect(waitMessage(0, 1, false)).toEqual({ + text: 'SubRead waits for 1 page that could not be read.', + action: retry, + }); + }); +}); + function state(over: Partial<ClockState>): ClockState { return { positionMs: 0, playing: false, seeked: false, error: null, ...over }; }