aboutsummaryrefslogtreecommitdiff
path: root/converter/chunking.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-17 18:32:14 -0400
committerhistoria <historiavg@proton.me>2026-08-17 18:32:14 -0400
commita30bd534151f757dad38763ae479fcd465d2ba0d (patch)
treed2185a9e12abb13b76944c6d39b1bd64f5f70e24 /converter/chunking.py
parent2bb8f721092fe2f4d6ae6e425181094c1f0da57a (diff)
downloadtts-audiobook-generator-a30bd534151f757dad38763ae479fcd465d2ba0d.tar.gz
refactor(converter): extract logic into package
Diffstat (limited to 'converter/chunking.py')
-rw-r--r--converter/chunking.py59
1 files changed, 59 insertions, 0 deletions
diff --git a/converter/chunking.py b/converter/chunking.py
new file mode 100644
index 0000000..f649310
--- /dev/null
+++ b/converter/chunking.py
@@ -0,0 +1,59 @@
+"""Split extracted book text into TTS-sized chunks."""
+
+import re
+from typing import List
+
+from . import config
+
+
+def split_into_chunks(text: str, max_words: int = config.CHUNK_SIZE_WORDS) -> List[str]:
+ """Split text into chunks of at most ``max_words`` words.
+
+ Splits on sentence boundaries. Sentences longer than the limit are split
+ further at clause punctuation (which is kept attached for TTS prosody).
+ A single sentence with no clause punctuation longer than the limit is
+ kept intact as one oversized chunk.
+ """
+ if not text.strip():
+ return []
+
+ sentences = re.split(r"(?<=[.!?])\s+", text)
+ chunks = []
+ current_chunk = ""
+ current_words = 0
+
+ for sentence in sentences:
+ sentence_words = len(sentence.split())
+
+ if sentence_words > max_words:
+ if current_chunk:
+ chunks.append(current_chunk.strip())
+ current_chunk = ""
+ current_words = 0
+
+ # Split long sentences at clause boundaries, keeping punctuation.
+ parts = re.split(r"(?<=[,;:])\s*", sentence)
+ for part in parts:
+ part_words = len(part.split())
+ if current_words + part_words <= max_words:
+ current_chunk += part + " "
+ current_words += part_words
+ else:
+ if current_chunk:
+ chunks.append(current_chunk.strip())
+ current_chunk = part + " "
+ current_words = part_words
+ else:
+ if current_words + sentence_words <= max_words:
+ current_chunk += sentence + " "
+ current_words += sentence_words
+ else:
+ if current_chunk:
+ chunks.append(current_chunk.strip())
+ current_chunk = sentence + " "
+ current_words = sentence_words
+
+ if current_chunk.strip():
+ chunks.append(current_chunk.strip())
+
+ return [chunk for chunk in chunks if chunk.strip()]