feat: Add original text preservation in chunking and export processes

This commit is contained in:
JB
2025-10-15 05:41:51 -07:00
parent 6ae8b827d2
commit 0a6d09445d
4 changed files with 22 additions and 2 deletions
+4
View File
@@ -148,6 +148,7 @@ def chunk_text(
voice_formula=voice_formula, voice_formula=voice_formula,
).as_dict() ).as_dict()
payload["normalized_text"] = _normalize_chunk_text(paragraph) payload["normalized_text"] = _normalize_chunk_text(paragraph)
payload["original_text"] = paragraph
chunks.append(payload) chunks.append(payload)
_attach_display_text(text, chunks) _attach_display_text(text, chunks)
return chunks return chunks
@@ -177,6 +178,7 @@ def chunk_text(
).as_dict() ).as_dict()
payload["normalized_text"] = _normalize_chunk_text(raw_sentence) payload["normalized_text"] = _normalize_chunk_text(raw_sentence)
payload["display_text"] = raw_sentence payload["display_text"] = raw_sentence
payload["original_text"] = raw_sentence
chunks.append(payload) chunks.append(payload)
sentence_index += 1 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) match = _search_source_span(source, candidate, 0)
if match is None: if match is None:
chunk.setdefault("display_text", candidate) chunk.setdefault("display_text", candidate)
chunk.setdefault("original_text", chunk.get("display_text") or candidate)
continue continue
start, end = match start, end = match
chunk["display_text"] = source[start:end] chunk["display_text"] = source[start:end]
chunk["original_text"] = source[start:end]
cursor = end cursor = end
+10 -2
View File
@@ -243,7 +243,7 @@ class EPUB3PackageBuilder:
voice = marker.get("voice") voice = marker.get("voice")
else: else:
display_text = chunk_entry.get("display_text") 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") 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") voice = chunk_entry.get("voice") or chunk_entry.get("resolved_voice") or marker.get("voice")
level = chunk_entry.get("level") or None level = chunk_entry.get("level") or None
@@ -261,11 +261,15 @@ class EPUB3PackageBuilder:
group_id = _derive_group_id(raw_group_key, level) group_id = _derive_group_id(raw_group_key, level)
normalized_group_id = _normalize_chunk_id(group_id) if group_id else None 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( overlays.append(
ChunkOverlay( ChunkOverlay(
id=normalized_id, id=normalized_id,
text=text or self.extraction.chapters[chapter_index].text, 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")), start=_safe_float(marker.get("start")),
end=_safe_float(marker.get("end")), end=_safe_float(marker.get("end")),
speaker_id=speaker_id, speaker_id=speaker_id,
@@ -747,6 +751,10 @@ def _restore_original_chunk_text(chapter_text: str, overlays: List[ChunkOverlay]
cursor = 0 cursor = 0
for chunk in overlays: 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 "" candidate = chunk.text or ""
if not candidate: if not candidate:
continue continue
+4
View File
@@ -1122,6 +1122,10 @@ class ConversionService:
if normalized_value is not None: if normalized_value is not None:
chunk["normalized_text"] = str(normalized_value) 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")) speaker_value = entry.get("speaker_id", entry.get("speaker"))
chunk["speaker_id"] = str(speaker_value) if speaker_value else "narrator" chunk["speaker_id"] = str(speaker_value) if speaker_value else "narrator"
+4
View File
@@ -59,6 +59,8 @@ def test_chunk_text_merges_title_abbreviations() -> None:
assert "Doctor" in normalized_value assert "Doctor" in normalized_value
display_value = str(chunk.get("display_text") or "") display_value = str(chunk.get("display_text") or "")
assert display_value.startswith("Dr.") 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: 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") assert first_display.endswith("\n\n")
second_display = str(chunks[1].get("display_text") or "") second_display = str(chunks[1].get("display_text") or "")
assert second_display == "Third paragraph." assert second_display == "Third paragraph."
first_original = str(chunks[0].get("original_text") or "")
assert first_original.endswith("\n\n")