diff --git a/abogen/chunking.py b/abogen/chunking.py index 265a1e0..9e169e5 100644 --- a/abogen/chunking.py +++ b/abogen/chunking.py @@ -148,6 +148,7 @@ def chunk_text( voice_formula=voice_formula, ).as_dict() payload["normalized_text"] = _normalize_chunk_text(paragraph) + payload["original_text"] = paragraph chunks.append(payload) _attach_display_text(text, chunks) return chunks @@ -177,6 +178,7 @@ def chunk_text( ).as_dict() payload["normalized_text"] = _normalize_chunk_text(raw_sentence) payload["display_text"] = raw_sentence + payload["original_text"] = raw_sentence chunks.append(payload) sentence_index += 1 @@ -221,9 +223,11 @@ def _attach_display_text(source: str, chunks: List[Dict[str, object]]) -> None: match = _search_source_span(source, candidate, 0) if match is None: chunk.setdefault("display_text", candidate) + chunk.setdefault("original_text", chunk.get("display_text") or candidate) continue start, end = match chunk["display_text"] = source[start:end] + chunk["original_text"] = source[start:end] cursor = end diff --git a/abogen/epub3/exporter.py b/abogen/epub3/exporter.py index 999c917..834c783 100644 --- a/abogen/epub3/exporter.py +++ b/abogen/epub3/exporter.py @@ -243,7 +243,7 @@ class EPUB3PackageBuilder: voice = marker.get("voice") else: display_text = chunk_entry.get("display_text") - text = str(display_text or chunk_entry.get("text") or "") + text = str(chunk_entry.get("text") or "") speaker_id = str(chunk_entry.get("speaker_id") or marker.get("speaker_id") or "narrator") voice = chunk_entry.get("voice") or chunk_entry.get("resolved_voice") or marker.get("voice") level = chunk_entry.get("level") or None @@ -261,11 +261,15 @@ class EPUB3PackageBuilder: group_id = _derive_group_id(raw_group_key, level) normalized_group_id = _normalize_chunk_id(group_id) if group_id else None + original_text = None + if chunk_entry is not None: + original_text = chunk_entry.get("original_text") or chunk_entry.get("display_text") + overlays.append( ChunkOverlay( id=normalized_id, text=text or self.extraction.chapters[chapter_index].text, - original_text=None, + original_text=str(original_text) if original_text is not None else None, start=_safe_float(marker.get("start")), end=_safe_float(marker.get("end")), speaker_id=speaker_id, @@ -747,6 +751,10 @@ def _restore_original_chunk_text(chapter_text: str, overlays: List[ChunkOverlay] cursor = 0 for chunk in overlays: + if chunk.original_text is not None: + prepared = _prepare_display_text(chunk.original_text) + chunk.text = prepared + continue candidate = chunk.text or "" if not candidate: continue diff --git a/abogen/web/service.py b/abogen/web/service.py index 2bb907b..8fd6c99 100644 --- a/abogen/web/service.py +++ b/abogen/web/service.py @@ -1122,6 +1122,10 @@ class ConversionService: if normalized_value is not None: chunk["normalized_text"] = str(normalized_value) + for text_key in ("display_text", "original_text"): + if text_key in entry and entry[text_key] is not None: + chunk[text_key] = str(entry[text_key]) + speaker_value = entry.get("speaker_id", entry.get("speaker")) chunk["speaker_id"] = str(speaker_value) if speaker_value else "narrator" diff --git a/tests/test_chunk_helpers.py b/tests/test_chunk_helpers.py index a677249..d37730d 100644 --- a/tests/test_chunk_helpers.py +++ b/tests/test_chunk_helpers.py @@ -59,6 +59,8 @@ def test_chunk_text_merges_title_abbreviations() -> None: assert "Doctor" in normalized_value display_value = str(chunk.get("display_text") or "") assert display_value.startswith("Dr.") + original_value = str(chunk.get("original_text") or "") + assert original_value.startswith("Dr.") def test_chunk_text_display_preserves_whitespace() -> None: @@ -77,3 +79,5 @@ def test_chunk_text_display_preserves_whitespace() -> None: assert first_display.endswith("\n\n") second_display = str(chunks[1].get("display_text") or "") assert second_display == "Third paragraph." + first_original = str(chunks[0].get("original_text") or "") + assert first_original.endswith("\n\n")