diff options
| author | historia <historiavg@proton.me> | 2026-09-09 23:25:52 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-09-09 23:25:52 -0400 |
| commit | 31459b281b6a5368c692b3c42c91e522995ebd57 (patch) | |
| tree | ea6a53f4252e6a08d954c4b2fc9369d8db376d0a /app/converter/converter.py | |
| parent | 130dcd988e0554a6343c92fd45d808fd508789b3 (diff) | |
| download | tts-audiobook-generator-31459b281b6a5368c692b3c42c91e522995ebd57.tar.gz | |
feat: smart chunking to avoid chunk boundaries mid-sentence
Diffstat (limited to 'app/converter/converter.py')
| -rw-r--r-- | app/converter/converter.py | 36 |
1 files changed, 25 insertions, 11 deletions
diff --git a/app/converter/converter.py b/app/converter/converter.py index 2b849a2..f8815d8 100644 --- a/app/converter/converter.py +++ b/app/converter/converter.py @@ -251,11 +251,11 @@ def chunk_clamp_needed(entry) -> bool: def chunk_clamp_message(entry) -> List[str]: """The popup text for a chunk_words-capped ENTRY (short lines).""" return [ - f"{entry.label} can narrate at most ~{entry.chunk_words} words " - "per request: the server caps", - "each request's prompt plus generation at a fixed window. " - "Longer sub-chunks", - "may cut off mid-sentence.", + f"{entry.label} narrates best at ~{entry.chunk_words} words " + "per request on this server:", + "each request's prompt plus generation must fit a fixed " + "window, so longer", + "sub-chunks may cut off mid-sentence.", ] @@ -305,6 +305,9 @@ class AudiobookConverter: # Class-level default so a partially-constructed instance (tests build # these with __new__) behaves like a plain console run. _progress = None + # Same for the per-run chunk override: without __init__ there is no + # clamp, so chunking follows the config.CHUNK_SIZE setting. + chunk_size = None def __init__(self, voice_mode: str = VOICE_MODE_CUSTOM, voice_clone_ref_audio: Optional[str] = None, voice_clone_ref_text: Optional[str] = None, skip_transcription: bool = False, @@ -340,6 +343,12 @@ class AudiobookConverter: self.backend = backend self.voice = voice self.debug = bool(debug) + # Per-run chunk-word override (the pre-flight popup's clamp for + # models whose engine caps one request below a full sub-chunk, + # SGLang-Omni's Higgs): caps the chapter chunks themselves so + # progress and debug dumps span one generation request each. + # None follows the config.CHUNK_SIZE setting. + self.chunk_size = chunk_size # The run's model selection (audio.cpp: a server entry id, sglomni: # the resolved catalog key, None elsewhere) — the startup banner # reports it. @@ -404,10 +413,9 @@ class AudiobookConverter: "catalog key for --model (see the backend docs).") model_id = entry.key self.model_id = model_id - # CHUNK_SIZE carries a per-run override (the pre-flight chunk - # popup's clamp for models whose engine caps one request below - # a full sub-chunk); None follows the config.CHUNK_SIZE setting. - self.chunk_size = chunk_size + # The catalog/request shape is resolved below; the per-run + # chunk override (self.chunk_size) was recorded above and + # reaches the client here. self.tts = SgOmniTTSClient( chunks_dir=CHUNKS_FOLDER, model=model_id, voice=voice, ref_audio=voice_clone_ref_audio, @@ -839,8 +847,14 @@ class AudiobookConverter: return results def _chapter_chunks(self, text: str) -> List[str]: - """Split chapter text into CHUNK_SIZE-word TTS requests.""" - return chunking.split_into_chunks(text) + """Split chapter text into word-capped TTS request chunks. + + Each chunk is one progress unit; when the pre-flight popup set + a per-run clamp (models whose engine caps one request below + CHUNK_SIZE), it caps these chunks too so a chunk is never + wider than one generation request. + """ + return chunking.split_into_chunks(text, max_words=self.chunk_size) def _convert_text(self, text: str, output_path: Path, start_time: float, speed: Optional[float] = None, |
