diff options
| author | historia <historiavg@proton.me> | 2026-08-24 13:50:50 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-24 13:50:50 -0400 |
| commit | dff790664389d60d16729092a58d9c0dc490a953 (patch) | |
| tree | 7c29996d78b0c1ae82fa9c4dc71ed39bb70d180e /app | |
| parent | aac8febbdb45b7994e209bc74a44f8fc98fc745d (diff) | |
| download | tts-audiobook-generator-dff790664389d60d16729092a58d9c0dc490a953.tar.gz | |
remove: --chunk flag (always force client-side chunking)
Diffstat (limited to 'app')
| -rw-r--r-- | app/converter/config.py | 3 | ||||
| -rw-r--r-- | app/converter/converter.py | 76 | ||||
| -rw-r--r-- | app/converter/tts.py | 61 | ||||
| -rw-r--r-- | app/tests/test_converter.py | 49 | ||||
| -rw-r--r-- | app/tests/test_hub.py | 4 | ||||
| -rw-r--r-- | app/tests/test_tts.py | 80 | ||||
| -rw-r--r-- | app/ui/hub.py | 7 |
7 files changed, 54 insertions, 226 deletions
diff --git a/app/converter/config.py b/app/converter/config.py index 6a98136..5e35ee3 100644 --- a/app/converter/config.py +++ b/app/converter/config.py @@ -8,9 +8,6 @@ MAX_RETRIES = 3 # Attempts per chunk request HEARTBEAT_INTERVAL_SECONDS = 30 # Print "still working" in console logs every N seconds # Words per TTS generation request (client-side chunking). -# The qwen and faster backends always chunk with this size -# The audio.cpp backend chunks long text itself, so this is ignored -# by default with that backend. Force chunking with --chunk CHUNK_SIZE = 250 # Default TTS backend. diff --git a/app/converter/converter.py b/app/converter/converter.py index cef4808..4c1ffad 100644 --- a/app/converter/converter.py +++ b/app/converter/converter.py @@ -140,7 +140,7 @@ class AudiobookConverter: speed: float = 1.0, single_file: bool = False, output_format: str = config.AUDIO_FORMAT, language: Optional[str] = None, backend: str = config.BACKEND, voice: Optional[str] = None, debug: bool = False, - chunk: bool = False, model_id: Optional[str] = None, + model_id: Optional[str] = None, instructions: Optional[str] = None, request_options: Optional[Dict[str, str]] = None): if speed <= 0: @@ -162,12 +162,6 @@ class AudiobookConverter: self.backend = backend self.voice = voice self.debug = bool(debug) - # Client-side chunking: the qwen and faster backends always chunk - # (their servers do one generation per request and silently truncate - # long text). The audio.cpp server chunks long text itself, so it - # defaults to one request per chapter; --chunk forces client-side - # chunking on top (possible needless double-chunking). - self.client_chunks = bool(chunk) or backend != BACKEND_AUDIOCPP # Voice design / style instruction and free-form request options # (audio.cpp only): forwarded to AudioCppTTSClient, which validates # them against the server-hosted model at connect time. @@ -185,7 +179,6 @@ class AudiobookConverter: # instructions describe or style the voice, request_options pass # per-model controls through to the server. self.tts = AudioCppTTSClient(voice=voice, language=self.language, - chunk_text=self.client_chunks, model_id=model_id, instructions=instructions, request_options=self.request_options) @@ -437,10 +430,9 @@ class AudiobookConverter: and every request/response is logged. """ total_chunks = len(chunks) - if self.client_chunks: - print(f"\n{'=' * 50}") - print(f"PROCESSING {total_chunks} CHUNKS") - print(f"{'=' * 50}") + print(f"\n{'=' * 50}") + print(f"PROCESSING {total_chunks} CHUNKS") + print(f"{'=' * 50}") results: Dict[int, Optional[Path]] = {} for chunk_num, chunk_text in enumerate(chunks, 1): @@ -461,8 +453,7 @@ class AudiobookConverter: destination = f" -> {copied.name}" if copied else "" logger.debug("Chunk %d/%d response in %.1fs%s", chunk_num, total_chunks, elapsed, destination) - if self.client_chunks: - print(f"[OK] Chunk {chunk_num:3d}/{total_chunks} completed") + print(f"[OK] Chunk {chunk_num:3d}/{total_chunks} completed") logger.info("+ Chunk %d/%d completed", chunk_num, total_chunks) else: logger.error("Chunk %d/%d failed; aborting the remaining chunks", @@ -476,25 +467,16 @@ class AudiobookConverter: break successful_chunks = sum(1 for path in results.values() if path) - if self.client_chunks: - print(f"\n{'=' * 50}") - print("CHUNK PROCESSING COMPLETE") - print(f"Successful: {successful_chunks}/{total_chunks}") - print(f"{'=' * 50}") + print(f"\n{'=' * 50}") + print("CHUNK PROCESSING COMPLETE") + print(f"Successful: {successful_chunks}/{total_chunks}") + print(f"{'=' * 50}") logger.info("Chunk processing completed: %d/%d chunks", successful_chunks, total_chunks) return results def _chapter_chunks(self, text: str) -> List[str]: - """Split chapter text into TTS requests. - - Client-side chunking splits into CHUNK_SIZE-word chunks (qwen and - faster always; audio.cpp only with --chunk). Otherwise (audio.cpp - default) the whole text is one request and the server does its own - long-form chunking. - """ - if self.client_chunks: - return chunking.split_into_chunks(text) - return [text] if text.strip() else [] + """Split chapter text into CHUNK_SIZE-word TTS requests.""" + return chunking.split_into_chunks(text) def _convert_text(self, text: str, output_path: Path, start_time: float, speed: Optional[float] = None, @@ -530,31 +512,14 @@ class AudiobookConverter: chunk_sizes = [len(chunk.split()) for chunk in chunks] avg_chunk_size = sum(chunk_sizes) / len(chunk_sizes) - if len(chunks) == 1: - logger.info("Sending the whole text as one request (%d words; " - "the server chunks long text itself)", - chunk_sizes[0]) - else: - logger.info("Split into %d chunks (avg %.0f words per chunk)", - total_chunks, avg_chunk_size) + logger.info("Split into %d chunks (avg %.0f words per chunk)", + total_chunks, avg_chunk_size) backend_labels = { BACKEND_FASTER: "faster TTS API", BACKEND_AUDIOCPP: "audio.cpp server", } backend = backend_labels.get(self.backend, "Qwen API") - if self.client_chunks: - print(f"[INFO] Processing {total_chunks} chunks via {backend}...") - else: - # The whole request is sent at once and the server does its - # own long-form chunking, so the chunk vocabulary does not - # apply; warn that this one request can take a very long time. - subject = (f"chapter {chapter[0]}/{chapter[1]}" - if chapter is not None else "text") - print(f"[INFO] Sending the {subject} to the {backend} as a " - "single request...") - print("[NOTE] It is expected for this to take a very long " - "time: the server synthesizes the entire request before " - "returning any audio.") + print(f"[INFO] Processing {total_chunks} chunks via {backend}...") results = self._synthesize_chunks(chunks, debug_dir=debug_dir) successful_chunks = sum(1 for path in results.values() if path) @@ -578,11 +543,8 @@ class AudiobookConverter: logger.info("Chapter %d/%d converted in %dm %ds (%d/%d chunks)", chapter[0], chapter[1], minutes, seconds, successful_chunks, total_chunks) - if self.client_chunks: - print(f"[INFO] Chapter {chapter[0]}/{chapter[1]} converted " - f"({successful_chunks}/{total_chunks} chunks)") - else: - print(f"[INFO] Chapter {chapter[0]}/{chapter[1]} converted") + print(f"[INFO] Chapter {chapter[0]}/{chapter[1]} converted " + f"({successful_chunks}/{total_chunks} chunks)") else: logger.info("Conversion completed in %dm %ds: %s", minutes, seconds, output_path) else: @@ -621,12 +583,6 @@ class AudiobookConverter: print(f"Speaker: {config.SPEAKER}") if self.request_options: print(f"Request options: {self.request_options}") - if self.client_chunks: - print("Chunking: client-side (--chunk; the server also chunks " - "long text itself, so this may double-chunk)") - else: - print("Chunking: server-side (one request per chapter; " - "--chunk forces client-side chunking)") print(f"Language: {self.language}") else: api_url = (config.CLONE_API_URL if self.voice_mode == VOICE_MODE_CLONE diff --git a/app/converter/tts.py b/app/converter/tts.py index 6ccef56..842cc0c 100644 --- a/app/converter/tts.py +++ b/app/converter/tts.py @@ -278,11 +278,6 @@ def whisper_backend_available() -> Optional[str]: return None -# 150 wpm is a typical spoken pace; used only to size the HTTP request -# timeout for long audio.cpp generations (not as a correctness check). -_ESTIMATED_WORDS_PER_MINUTE = 150 - - class _BaseTTSClient: """Shared chunk retry logic, heartbeat, and chunk file bookkeeping.""" @@ -327,15 +322,10 @@ class _BaseTTSClient: return None @contextlib.contextmanager - def _chunk_heartbeat(self, chunk_num: int, label: Optional[str] = None): - """Print a periodic "still working" message while a request generates. - - ``label`` overrides the default "Chunk {chunk_num}" subject, for - backends that send one request per chapter without client-side - chunking (the audio.cpp default) where "chunk" would be misleading. - """ + def _chunk_heartbeat(self, chunk_num: int): + """Print a periodic "still working" message while a request generates.""" stop = threading.Event() - subject = label if label is not None else f"Chunk {chunk_num}" + subject = f"Chunk {chunk_num}" def _beat(): start = time.time() @@ -796,20 +786,14 @@ class AudioCppTTSClient(_BaseTTSClient): the request's "options" object, which is the server's generic pass-through for per-model controls. - Chunking: the server does its own long-form text chunking for every - family (its ``text_chunk_size`` option, with a per-family default), so - by default each chapter is sent as a single request and the audio - comes back already stitched. With ``chunk_text=True`` (the --chunk CLI - flag), text is instead split client-side into CHUNK_SIZE-word - sub-requests, which may needlessly double-chunk — the warning is - printed by the CLI. - - Each response is a complete WAV file, so sub-request audio is - concatenated with the same lossless path used for the Qwen client. + Chunking: text is split client-side into sub-requests of at most + config.CHUNK_SIZE words each; each sub-request returns a complete + WAV file and the parts are concatenated with the same lossless path + used for the Qwen client. """ def __init__(self, voice: Optional[str] = None, language: Optional[str] = None, - api_url: Optional[str] = None, chunk_text: bool = False, + api_url: Optional[str] = None, model_id: Optional[str] = None, instructions: Optional[str] = None, request_options: Optional[Dict[str, str]] = None): @@ -844,10 +828,6 @@ class AudioCppTTSClient(_BaseTTSClient): # speakers gets its voice from the instruction alone (no voice field). self.design_mode = False self.instruction_voice = False - # When False (default), each chapter is sent as one request and the - # server does its own long-form chunking (text_chunk_size); when True, - # text is split client-side into CHUNK_SIZE-word sub-requests first. - self.chunk_text = bool(chunk_text) # Family and task of the selected model entry and the family's request # profile; all are resolved from GET /v1/models during _connect. self.family = "" @@ -1217,11 +1197,6 @@ class AudioCppTTSClient(_BaseTTSClient): url, data=json.dumps(payload).encode("utf-8"), headers={"Content-Type": "application/json"}, method="POST") timeout = config.API_TIMEOUT - if not self.chunk_text: - # Estimated audio duration at 150 wpm, doubled plus a minute of - # slack, bounded below by the configured per-request timeout. - estimated_seconds = 60.0 * len(text.split()) / _ESTIMATED_WORDS_PER_MINUTE - timeout = max(timeout, int(estimated_seconds * 2) + 60) try: with urllib.request.urlopen(request, timeout=timeout) as response: wav = response.read() @@ -1259,28 +1234,18 @@ class AudioCppTTSClient(_BaseTTSClient): def generate_chunk(self, text: str, chunk_num: int) -> Optional[str]: """Generate one audio chunk; returns its path in the chunks folder. - By default the whole text goes out as a single request and the - server does its own long-form chunking (see the class docstring). - With ``chunk_text=True`` (--chunk), the text is split into - sub-requests of at most ``config.CHUNK_SIZE`` words each; each - sub-request returns a complete WAV file and the parts are - concatenated into one chunk file. + The text is split into sub-requests of at most ``config.CHUNK_SIZE`` + words each; each sub-request returns a complete WAV file and the + parts are concatenated into one chunk file. """ try: - if self.chunk_text: - sub_texts = split_into_chunks(text, max_words=config.CHUNK_SIZE) - elif text.strip(): - sub_texts = [text] - else: - sub_texts = [] + sub_texts = split_into_chunks(text, max_words=config.CHUNK_SIZE) if not sub_texts: raise RuntimeError("No text to synthesize") output_path: Optional[Path] = None with tempfile.TemporaryDirectory(prefix="tts_parts_") as parts_dir, \ - self._chunk_heartbeat( - chunk_num, - label=None if self.chunk_text else "Request"): + self._chunk_heartbeat(chunk_num): part_paths = [] for sub_num, sub_text in enumerate(sub_texts, 1): wav = self._request_wav_with_retry( diff --git a/app/tests/test_converter.py b/app/tests/test_converter.py index 2fe0f5d..9b0ddb4 100644 --- a/app/tests/test_converter.py +++ b/app/tests/test_converter.py @@ -226,7 +226,6 @@ class DebugDumpTests(unittest.TestCase): self._debug_folder.start() self.debug_root = Path(self._tmp.name) self.converter = AudiobookConverter.__new__(AudiobookConverter) - self.converter.client_chunks = True self.converter.tts = MagicMock() def tearDown(self): @@ -383,7 +382,6 @@ class SynthesizeChunkLoggingTests(unittest.TestCase): def setUp(self): self.converter = AudiobookConverter.__new__(AudiobookConverter) - self.converter.client_chunks = True self.converter.tts = MagicMock() def test_failed_chunk_logs_single_error(self): @@ -403,13 +401,11 @@ class SynthesizeChunkLoggingTests(unittest.TestCase): self.assertIn("Chunk 1/1 error: boom", logs.output[0]) -class ServerSideChunkingOutputTests(unittest.TestCase): - """With client-side chunking off (audiocpp default), the console skips - the chunk vocabulary because the whole request is one server call.""" +class ChunkProgressOutputTests(unittest.TestCase): + """The console reports chunk progress while a conversion runs.""" - def _converter(self, client_chunks: bool): + def _converter(self): converter = AudiobookConverter.__new__(AudiobookConverter) - converter.client_chunks = client_chunks converter.backend = tts.BACKEND_AUDIOCPP converter.speed = 1.0 converter.output_format = "mp3" @@ -417,49 +413,20 @@ class ServerSideChunkingOutputTests(unittest.TestCase): converter.tts.process_chunk_with_retry.return_value = "chunk.wav" return converter - def test_client_chunking_prints_chunk_progress(self): + def test_prints_chunk_progress(self): buf = io.StringIO() with redirect_stdout(buf): - self._converter(client_chunks=True)._synthesize_chunks(["Hello."]) + self._converter()._synthesize_chunks(["Hello."]) out = buf.getvalue() self.assertIn("PROCESSING 1 CHUNKS", out) self.assertIn("Chunk 1/1 completed", out) self.assertIn("Successful: 1/1", out) - def test_server_side_chunking_suppresses_chunk_output(self): - buf = io.StringIO() - with redirect_stdout(buf): - self._converter(client_chunks=False)._synthesize_chunks(["Hello."]) - self.assertEqual(buf.getvalue(), "") - - def test_server_side_chunking_suppresses_chapter_chunk_suffix(self): - buf = io.StringIO() - with patch.object(converter_mod.audio, "combine_chunks", return_value=True), \ - redirect_stdout(buf): - ok = self._converter(client_chunks=False)._convert_text( - "Hello world.", Path("out.mp3"), time.time(), chapter=(2, 5)) - self.assertTrue(ok) - out = buf.getvalue() - self.assertIn("Chapter 2/5 converted", out) - self.assertNotIn("chunk", out.lower()) - - def test_single_request_run_notes_long_wait(self): - buf = io.StringIO() - with patch.object(converter_mod.audio, "combine_chunks", return_value=True), \ - redirect_stdout(buf): - ok = self._converter(client_chunks=False)._convert_text( - "Hello world.", Path("out.mp3"), time.time(), chapter=(2, 5)) - self.assertTrue(ok) - out = buf.getvalue() - self.assertIn("Sending the chapter 2/5 to the audio.cpp server as a " - "single request", out) - self.assertIn("expected for this to take a very long time", out) - - def test_client_chunking_run_keeps_chunk_phrasing(self): + def test_run_keeps_chunk_phrasing(self): buf = io.StringIO() with patch.object(converter_mod.audio, "combine_chunks", return_value=True), \ redirect_stdout(buf): - ok = self._converter(client_chunks=True)._convert_text( + ok = self._converter()._convert_text( "Hello world.", Path("out.mp3"), time.time(), chapter=(2, 5)) self.assertTrue(ok) out = buf.getvalue() @@ -468,7 +435,7 @@ class ServerSideChunkingOutputTests(unittest.TestCase): self.assertIn("Chapter 2/5 converted (1/1 chunks)", out) def test_partial_chunks_abort_without_assembling(self): - converter = self._converter(client_chunks=True) + converter = self._converter() converter.tts.process_chunk_with_retry.side_effect = ["chunk_0001.wav", None] text = " ".join(f"word{i}" for i in range(8)) with patch.object(config, "CHUNK_SIZE", 5), \ diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py index 2fe0b2f..1b7630e 100644 --- a/app/tests/test_hub.py +++ b/app/tests/test_hub.py @@ -429,8 +429,8 @@ class ConvertFlowTests(unittest.TestCase): self.addCleanup(patcher.stop) def _answer_common_options(self): - # Output format, speed, single-file, chunk, debug. - self.tui.script += ["m4b", "1.5", False, False, False] + # Output format, speed, single-file, debug. + self.tui.script += ["m4b", "1.5", False, False] # ------------------------------------------------------------------ # audio.cpp: remote server (no local checkout / server.json) diff --git a/app/tests/test_tts.py b/app/tests/test_tts.py index a2df07f..87f98df 100644 --- a/app/tests/test_tts.py +++ b/app/tests/test_tts.py @@ -906,7 +906,7 @@ class AudioCppTTSClientRequestTests(unittest.TestCase): @staticmethod def _make_client(preset_mode=False, voice="Vivian", language="English", seed=-1, - chunk_text=True, family="qwen3_tts", task="tts", + family="qwen3_tts", task="tts", instructions=None, request_options=None): client = AudioCppTTSClient.__new__(AudioCppTTSClient) client.api_url = "http://127.0.0.1:8080" @@ -915,7 +915,6 @@ class AudioCppTTSClientRequestTests(unittest.TestCase): client.voice = voice client.language = language client._seed = seed - client.chunk_text = chunk_text client.family = family client.task = task client.profile = tts.AUDIOCPP_FAMILY_PROFILES.get( @@ -972,30 +971,8 @@ class AudioCppTTSClientRequestTests(unittest.TestCase): payload = json.loads(mock_urlopen.call_args[0][0].data.decode("utf-8")) self.assertNotIn("seed", payload) - def test_whole_text_sent_as_one_request_without_client_chunking(self): - client = self._make_client(chunk_text=False) - # 9 words with CHUNK_SIZE=5 would split in two if client chunking - # were on. - text = " ".join(f"word{i}" for i in range(9)) - with patch.object(config, "CHUNK_SIZE", 5), \ - patch.object(client, "_request_wav", - return_value=self._wav_bytes()) as mock_request: - result = client.generate_chunk(text, 1) - self.assertIsNotNone(result) - self.assertEqual(mock_request.call_count, 1) - self.assertEqual(mock_request.call_args[0][0], text) - - def test_single_request_timeout_scales_with_text_length(self): - client = self._make_client(chunk_text=False) - long_text = " ".join(f"word{i}" for i in range(1500)) # ~10 min of audio - with patch("converter.tts.urllib.request.urlopen", - return_value=self._post_response(self._wav_bytes())) as mock_urlopen: - client._request_wav(long_text) - timeout = mock_urlopen.call_args[1]["timeout"] - self.assertGreater(timeout, config.API_TIMEOUT) - - def test_client_chunking_keeps_configured_timeout(self): - client = self._make_client(chunk_text=True) + def test_request_timeout_is_the_configured_api_timeout(self): + client = self._make_client() long_text = " ".join(f"word{i}" for i in range(1500)) with patch("converter.tts.urllib.request.urlopen", return_value=self._post_response(self._wav_bytes())) as mock_urlopen: @@ -1208,8 +1185,7 @@ class AudioCppTTSClientRequestTests(unittest.TestCase): class AudioCppHeartbeatTests(unittest.TestCase): - """The heartbeat label drops 'Chunk' when the server does its own - long-form chunking (chunk_text=False, the default).""" + """The heartbeat reports chunk progress while a request generates.""" def setUp(self): self._tmp = tempfile.TemporaryDirectory() @@ -1221,7 +1197,7 @@ class AudioCppHeartbeatTests(unittest.TestCase): self._tmp.cleanup() @staticmethod - def _client(chunk_text): + def _client(): client = AudioCppTTSClient.__new__(AudioCppTTSClient) client.api_url = "http://127.0.0.1:8080" client.model_id = config.AUDIOCPP_MODEL_ID @@ -1229,7 +1205,6 @@ class AudioCppHeartbeatTests(unittest.TestCase): client.voice = "Vivian" client.language = "English" client._seed = -1 - client.chunk_text = chunk_text client.family = "qwen3_tts" client.profile = tts.AUDIOCPP_DEFAULT_FAMILY_PROFILE return client @@ -1244,8 +1219,8 @@ class AudioCppHeartbeatTests(unittest.TestCase): wav_file.writeframes(b"\x01\x00" * 10) return buffer.getvalue() - def _run(self, chunk_text): - client = self._client(chunk_text) + def _run(self): + client = self._client() def slow_request(*_args, **_kwargs): time.sleep(0.12) @@ -1260,13 +1235,8 @@ class AudioCppHeartbeatTests(unittest.TestCase): self.assertTrue(result) return buf.getvalue() - def test_server_side_chunking_heartbeat_has_no_chunk_word(self): - out = self._run(chunk_text=False) - self.assertIn("Request still generating", out) - self.assertNotIn("Chunk", out) - - def test_client_side_chunking_heartbeat_keeps_chunk_word(self): - out = self._run(chunk_text=True) + def test_heartbeat_reports_chunk_progress(self): + out = self._run() self.assertIn("Chunk 1 still generating", out) @@ -1290,7 +1260,6 @@ class AudioCppTTSClientTruncationTests(unittest.TestCase): client.voice = "narrator" client.language = "English" client._seed = -1 - client.chunk_text = True client.family = "qwen3_tts" client.profile = tts.AUDIOCPP_FAMILY_PROFILES["qwen3_tts"] return client @@ -1336,7 +1305,7 @@ class BackendWiringTests(unittest.TestCase): backend=tts.BACKEND_AUDIOCPP, voice="narrator", language="ja") mock_audiocpp.assert_called_once_with(voice="narrator", language="Japanese", - chunk_text=False, model_id=None, + model_id=None, instructions=None, request_options={}) mock_faster.assert_not_called() @@ -1347,22 +1316,10 @@ class BackendWiringTests(unittest.TestCase): AudiobookConverter(voice_mode=tts.VOICE_MODE_CUSTOM, backend=tts.BACKEND_AUDIOCPP) mock_audiocpp.assert_called_once_with(voice=None, language=config.LANGUAGE, - chunk_text=False, model_id=None, + model_id=None, instructions=None, request_options={}) - def test_audiocpp_backend_chunk_flag_forces_client_chunking(self): - with patch("converter.converter.AudioCppTTSClient") as mock_audiocpp: - converter = AudiobookConverter(voice_mode=tts.VOICE_MODE_CLONE, - backend=tts.BACKEND_AUDIOCPP, - voice="narrator", chunk=True) - mock_audiocpp.assert_called_once_with(voice="narrator", - language=config.LANGUAGE, - chunk_text=True, model_id=None, - instructions=None, - request_options={}) - self.assertTrue(converter.client_chunks) - def test_audiocpp_backend_model_id_is_wired_through(self): with patch("converter.converter.AudioCppTTSClient") as mock_audiocpp: AudiobookConverter(voice_mode=tts.VOICE_MODE_CLONE, @@ -1370,7 +1327,7 @@ class BackendWiringTests(unittest.TestCase): model_id="higgs") mock_audiocpp.assert_called_once_with( voice="narrator", language=config.LANGUAGE, - chunk_text=False, model_id="higgs", instructions=None, + model_id="higgs", instructions=None, request_options={}) def test_audiocpp_backend_instructions_and_options_are_wired_through(self): @@ -1382,7 +1339,7 @@ class BackendWiringTests(unittest.TestCase): "speed": "1.1"}) mock_audiocpp.assert_called_once_with( voice=None, language=config.LANGUAGE, - chunk_text=False, model_id=None, + model_id=None, instructions="A warm adult narrator", request_options={"emotion": "neutral", "speed": "1.1"}) @@ -1411,19 +1368,10 @@ class BackendWiringTests(unittest.TestCase): voice="narrator") self.assertIsNone(converter.voice_clone_ref_audio) - def test_chapter_chunks_audiocpp_default_is_one_request(self): + def test_chapter_chunks_audiocpp_splits(self): converter = self._audiocpp_converter(voice="narrator") text = " ".join(f"word{i}" for i in range(50)) with patch.object(config, "CHUNK_SIZE", 10): - self.assertEqual(converter._chapter_chunks(text), [text]) - - def test_chapter_chunks_audiocpp_chunk_flag_splits(self): - with patch("converter.converter.AudioCppTTSClient"): - converter = AudiobookConverter(voice_mode=tts.VOICE_MODE_CLONE, - backend=tts.BACKEND_AUDIOCPP, - voice="narrator", chunk=True) - text = " ".join(f"word{i}" for i in range(50)) - with patch.object(config, "CHUNK_SIZE", 10): chunks = converter._chapter_chunks(text) self.assertGreater(len(chunks), 1) self.assertTrue(all(len(chunk.split()) <= 10 for chunk in chunks)) diff --git a/app/ui/hub.py b/app/ui/hub.py index b603dd0..0b599d0 100644 --- a/app/ui/hub.py +++ b/app/ui/hub.py @@ -441,7 +441,7 @@ def _convert_faster(stdscr) -> Optional[tuple]: def _common_options(stdscr) -> Optional[dict]: - """Collect output format, speed, single-file, chunk, debug.""" + """Collect output format, speed, single-file, debug.""" fmt_options = [(f, f) for f in AUDIO_FORMATS] fmt_default = AUDIO_FORMATS.index(config.AUDIO_FORMAT) \ if config.AUDIO_FORMAT in AUDIO_FORMATS else 0 @@ -460,10 +460,6 @@ def _common_options(stdscr) -> Optional[dict]: default=False, cancel_value=_GO_BACK) if single_file is _GO_BACK: return None - chunk = tui.confirm(stdscr, "Force client-side chunking (--chunk)?", - default=False, cancel_value=_GO_BACK) - if chunk is _GO_BACK: - return None debug = tui.confirm(stdscr, "Debug mode (dump per-chunk audio/text)?", default=False, cancel_value=_GO_BACK) if debug is _GO_BACK: @@ -472,7 +468,6 @@ def _common_options(stdscr) -> Optional[dict]: "output_format": output_format, "speed": float(speed_text), "single_file": single_file, - "chunk": chunk, "debug": debug, } |
