diff options
Diffstat (limited to 'app/converter/converter.py')
| -rw-r--r-- | app/converter/converter.py | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/app/converter/converter.py b/app/converter/converter.py index 0769258..3b878c8 100644 --- a/app/converter/converter.py +++ b/app/converter/converter.py @@ -220,6 +220,73 @@ def prompt_overwrite(existing: List[Path], output_name: str, print("Please answer 'y' or 'n' (or press Enter for yes).") +class ChunkClampCancelled(Exception): + """The chunk-cap popup's Cancel answer: the run stops unstarted.""" + + +def chunk_clamp_needed(entry) -> bool: + """True when ENTRY cannot narrate a full CHUNK_SIZE sub-request. + + A catalog entry declares ``chunk_words`` when its engine's admission + window caps one request below what the configured CHUNK_SIZE can + narrate (Higgs: the server pins each request's prompt plus + generation at 4096 tokens). + """ + return (entry is not None and entry.chunk_words is not None + and entry.chunk_words < config.CHUNK_SIZE) + + +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.", + ] + + +def prompt_chunk_clamp(entry, ask=None) -> Optional[int]: + """The per-run sub-request word cap for ENTRY (None = no clamp). + + Models whose catalog entry declares ``chunk_words`` below CHUNK_SIZE + (Higgs) cannot narrate a full sub-chunk in one request; the popup + offers to clamp CHUNK_SIZE for this run, keep it ("try anyway", + risking mid-chunk truncation), or cancel the run. ASK, when given, + replaces the console prompt: it is called with (message lines, words) + and returns "clamp" or "anyway" (Cancel raises inside the callback — + the hub maps it to returning to the Generate form). A closed stdin + (non-interactive run) clamps: unattended runs keep working and never + produce silently truncated audio. + """ + if not chunk_clamp_needed(entry): + return None + lines = chunk_clamp_message(entry) + if ask is not None: + answer = ask(lines, entry.chunk_words) + return entry.chunk_words if answer == "clamp" else None + for line in lines: + print(line) + while True: + try: + answer = input( + f"Set Chunk to {entry.chunk_words} for this run, try " + "anyway, or cancel? [S/t/c]: ").strip().lower() + except EOFError: + print(f"\n[WARNING] No interactive input available; clamping " + f"sub-requests to {entry.chunk_words} words for this run") + return entry.chunk_words + if answer in ("", "s", "set"): + return entry.chunk_words + if answer in ("t", "try"): + return None + if answer in ("c", "cancel"): + raise ChunkClampCancelled( + "Conversion cancelled at the chunk-size prompt") + print("Please answer 's', 't', or 'c'.") + + class AudiobookConverter: """Audiobook converter using a local TTS API.""" @@ -236,6 +303,7 @@ class AudiobookConverter: instructions: Optional[str] = None, request_options: Optional[Dict[str, str]] = None, api_url: Optional[str] = None, + chunk_size: Optional[int] = None, unload_models: Optional[bool] = None, progress: Optional[Callable[[dict], None]] = None, cancel=None): @@ -324,13 +392,18 @@ 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 self.tts = SgOmniTTSClient( chunks_dir=CHUNKS_FOLDER, model=model_id, voice=voice, ref_audio=voice_clone_ref_audio, ref_text=voice_clone_ref_text, skip_transcription=skip_transcription, instructions=instructions, language=self.language, - api_url=api_url, quiet=quiet, cancel=cancel) + api_url=api_url, chunk_size=self.chunk_size, + quiet=quiet, cancel=cancel) else: # Qwen: the voice mode picks the request shape (built-in # speaker, clone from a reference .wav, or a designed voice); |
