aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-24 13:50:50 -0400
committerhistoria <historiavg@proton.me>2026-08-24 13:50:50 -0400
commitdff790664389d60d16729092a58d9c0dc490a953 (patch)
tree7c29996d78b0c1ae82fa9c4dc71ed39bb70d180e /app
parentaac8febbdb45b7994e209bc74a44f8fc98fc745d (diff)
downloadtts-audiobook-generator-dff790664389d60d16729092a58d9c0dc490a953.tar.gz
remove: --chunk flag (always force client-side chunking)
Diffstat (limited to 'app')
-rw-r--r--app/converter/config.py3
-rw-r--r--app/converter/converter.py76
-rw-r--r--app/converter/tts.py61
-rw-r--r--app/tests/test_converter.py49
-rw-r--r--app/tests/test_hub.py4
-rw-r--r--app/tests/test_tts.py80
-rw-r--r--app/ui/hub.py7
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,
}