From 868ce6a7bd6bf8eda5ca061858d937bfc9d6b5b4 Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Fri, 26 Jun 2026 22:09:32 +0200 Subject: [PATCH] feat: show page numbers on page-break markers in Script Rehearser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a PDF audiobook is cast and saved to the Rehearser, page breaks now carry the source PDF page number. In the Stage view each divider renders as "— Page N —" instead of the generic "— Page break —". Implementation: - audiobook.js: `audiobookBuildScript()` encodes the page number in the form-feed line (\fN instead of bare \f) at each page boundary. - rehearser-parse.js: `parseScript()` now matches `line.startsWith('\f')` and extracts the trailing page number into `line.page`. - rehearser.js: `buildScriptPage()` renders "— Page N —" when `line.page` is set; `toScript()` round-trips the number back (\fN) so it survives save/reload; `importPDFScript()` also encodes page numbers (\f) when importing screenplay PDFs directly. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- static/index.html | 4 ++-- static/js/audiobook.js | 5 ++++- static/js/rehearser-parse.js | 9 ++++++--- static/js/rehearser.js | 10 ++++++---- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c9bad3..b597fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- +## [1.9.4] — 2026-06-26 + +### Changed +- **Audiobook casting — page numbers in Rehearser**: page-break markers now carry the source PDF page number. In the Script Rehearser Stage, each page divider shows **"— Page N —"** instead of the generic "— Page break —", making it easy to cross-reference the audiobook script against the book. Applies both to audiobooks cast from Read Aloud PDFs and to screenplay PDFs imported directly into the Rehearser. + +--- + ## [1.9.3] — 2026-06-26 ### Added diff --git a/VERSION b/VERSION index 77fee73..d615fd0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.3 +1.9.4 diff --git a/static/index.html b/static/index.html index 54c909b..bb4829a 100644 --- a/static/index.html +++ b/static/index.html @@ -26,7 +26,7 @@ - + @@ -308,7 +308,7 @@ - + diff --git a/static/js/audiobook.js b/static/js/audiobook.js index d7e4cf8..2612c67 100644 --- a/static/js/audiobook.js +++ b/static/js/audiobook.js @@ -1291,7 +1291,10 @@ function audiobookBuildScript(segments) { const pos = at >= 0 ? at : searchPos; if (at >= 0) searchPos = at + probe.length; while (markIdx < marks.length && pos >= marks[markIdx].offset) { - if (script.trim()) script += '\n\f\n'; // page break before this segment + if (script.trim()) { + const pn = marks[markIdx].page; + script += pn ? `\n\f${pn}\n` : '\n\f\n'; // page break with page number + } markIdx++; } } diff --git a/static/js/rehearser-parse.js b/static/js/rehearser-parse.js index f0b2685..9d9f67b 100644 --- a/static/js/rehearser-parse.js +++ b/static/js/rehearser-parse.js @@ -54,10 +54,13 @@ function parseScript(text) { continue; } - // PDF page-break marker — preserved from importPDFScript - if (line === '\f') { + // PDF page-break marker — preserved from importPDFScript / audiobook casting + // Format: bare \f or \f (e.g. \f3 = page 3) + if (line.startsWith('\f')) { flushDialog(); flushAction(); - result.push({ type: 'pagebreak', speaker: '', text: '', isDirection: true }); + const pnStr = line.slice(1).trim(); + const pageNum = pnStr && /^\d+$/.test(pnStr) ? parseInt(pnStr, 10) : null; + result.push({ type: 'pagebreak', speaker: '', text: '', page: pageNum, isDirection: true }); state = 'action'; currentSpeaker = null; continue; } diff --git a/static/js/rehearser.js b/static/js/rehearser.js index 87e3f8b..1d1c89c 100644 --- a/static/js/rehearser.js +++ b/static/js/rehearser.js @@ -204,7 +204,7 @@ function linesToScriptText() { case 'dialog': return '\n' + line.speaker + '\n' + line.text + '\n'; case 'direction': return line.speaker ? line.text + '\n' : '\n' + line.text + '\n'; - case 'pagebreak': return '\n\f\n'; + case 'pagebreak': return line.page ? `\n\f${line.page}\n` : '\n\f\n'; default: return ''; } }).join('').trim(); @@ -450,7 +450,7 @@ async function importPDFScript(file) { for (let pageIdx = 0; pageIdx < pdf.numPages; pageIdx++) { // Emit a page-break marker between PDF pages so the rehearser can paginate exactly - if (pageIdx > 0) { blank(); out.push('\f'); blank(); } + if (pageIdx > 0) { blank(); out.push(`\f${pageIdx + 1}`); blank(); } const pageLns = allLinesByPage[pageIdx] || []; for (const ln of pageLns) { @@ -2239,8 +2239,10 @@ function buildScriptPage() { ${editBtn} ${synthDot} `; - case 'pagebreak': - return `
${bulkCheck}— Page break —
`; + case 'pagebreak': { + const pbLabel = line.page ? `— Page ${line.page} —` : '— Page break —'; + return `
${bulkCheck}${pbLabel}
`; + } case 'transition': return `
${bulkCheck}${escHtml(line.text)}
`;