Spaces:
Paused
Paused
Commit ·
bea718a
1
Parent(s): 5ae6bba
feat: show paragraph progress during Play + warmup first paragraphs at startup
Browse files- Play status shows 'Paragraph X / N' instead of blank
- Background warmup pre-generates first paragraph of all 10 stories (stock voice)
- Uses disk cache directly so cancel_pregeneration() won't clear warmup results
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
app.py
CHANGED
|
@@ -419,6 +419,33 @@ from inference_lfm import get_lfm_model
|
|
| 419 |
get_lfm_model()
|
| 420 |
print("[MomsVoice] LFM Q&A model ready. All models preloaded.")
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
# Gradio Application Core setup
|
| 424 |
with gr.Blocks(title="MomsVoice", css=css_code) as demo:
|
|
@@ -933,7 +960,7 @@ with gr.Blocks(title="MomsVoice", css=css_code) as demo:
|
|
| 933 |
|
| 934 |
n = len(tts_chunks)
|
| 935 |
yield (gr.update(), _status_playing,
|
| 936 |
-
"",
|
| 937 |
gr.Button(visible=False), gr.Button(visible=True), gr.Button(visible=False),
|
| 938 |
gr.Slider(value=0), render_story_text(paras, 0) if paras else "",
|
| 939 |
gr.HTML(visible=False),
|
|
@@ -950,13 +977,14 @@ with gr.Blocks(title="MomsVoice", css=css_code) as demo:
|
|
| 950 |
return
|
| 951 |
para_idx = min(int(i * n_paras / total), n_paras - 1) if total > 0 else 0
|
| 952 |
is_last = (i + 1 >= total)
|
| 953 |
-
|
|
|
|
| 954 |
gr.Button(visible=False), gr.Button(visible=True), gr.Button(visible=True),
|
| 955 |
gr.Slider(value=para_idx), render_story_text(paras, para_idx),
|
| 956 |
gr.HTML(visible=is_last),
|
| 957 |
gr.HTML(visible=False), gr.Group(visible=False), gr.Button(visible=False))
|
| 958 |
|
| 959 |
-
yield (gr.update(), _status_done, "",
|
| 960 |
gr.Button(visible=True), gr.Button(visible=False), gr.Button(visible=False),
|
| 961 |
gr.Slider(value=max(n_paras - 1, 0)), render_story_text(paras, max(n_paras - 1, 0)),
|
| 962 |
gr.HTML(visible=True),
|
|
|
|
| 419 |
get_lfm_model()
|
| 420 |
print("[MomsVoice] LFM Q&A model ready. All models preloaded.")
|
| 421 |
|
| 422 |
+
# Pre-generate first paragraph audio for all stories (stock voice) for instant start
|
| 423 |
+
import threading
|
| 424 |
+
|
| 425 |
+
def _warmup_first_paragraphs():
|
| 426 |
+
"""Background: pre-generate first paragraph of each story with stock voice to disk cache."""
|
| 427 |
+
import logging as _log
|
| 428 |
+
from tts import _get_cached_audio, _save_cached_audio, _synthesize_single
|
| 429 |
+
_logger = _log.getLogger("warmup")
|
| 430 |
+
for book in mock_books:
|
| 431 |
+
try:
|
| 432 |
+
paras = load_paragraphs(book["story_path"])
|
| 433 |
+
if not paras:
|
| 434 |
+
continue
|
| 435 |
+
first_chunks = split_into_chunks(paras[0])
|
| 436 |
+
for chunk in first_chunks:
|
| 437 |
+
if _get_cached_audio(chunk, None) is not None:
|
| 438 |
+
continue # Already cached
|
| 439 |
+
wav, sr = _synthesize_single(chunk, None)
|
| 440 |
+
_save_cached_audio(chunk, None, wav, sr)
|
| 441 |
+
_logger.info("Warmed: %s (%d chunks)", book["title"], len(first_chunks))
|
| 442 |
+
except Exception as e:
|
| 443 |
+
_logger.warning("Warmup failed for %s: %s", book.get("title", "?"), e)
|
| 444 |
+
_logger.info("All first paragraphs warmed.")
|
| 445 |
+
|
| 446 |
+
threading.Thread(target=_warmup_first_paragraphs, daemon=True).start()
|
| 447 |
+
print("[MomsVoice] Background warmup started: pre-generating first paragraph for all stories.")
|
| 448 |
+
|
| 449 |
|
| 450 |
# Gradio Application Core setup
|
| 451 |
with gr.Blocks(title="MomsVoice", css=css_code) as demo:
|
|
|
|
| 960 |
|
| 961 |
n = len(tts_chunks)
|
| 962 |
yield (gr.update(), _status_playing,
|
| 963 |
+
f"<div style='text-align:center;font-size:10px;color:#4ade80;font-family:monospace;margin-top:8px;'>▶ Paragraph 1 / {n_paras}</div>",
|
| 964 |
gr.Button(visible=False), gr.Button(visible=True), gr.Button(visible=False),
|
| 965 |
gr.Slider(value=0), render_story_text(paras, 0) if paras else "",
|
| 966 |
gr.HTML(visible=False),
|
|
|
|
| 977 |
return
|
| 978 |
para_idx = min(int(i * n_paras / total), n_paras - 1) if total > 0 else 0
|
| 979 |
is_last = (i + 1 >= total)
|
| 980 |
+
para_status = f"<div style='text-align:center;font-size:10px;color:#4ade80;font-family:monospace;margin-top:8px;'>▶ Paragraph {para_idx + 1} / {n_paras}</div>"
|
| 981 |
+
yield ((sample_rate, wav), _status_playing, para_status,
|
| 982 |
gr.Button(visible=False), gr.Button(visible=True), gr.Button(visible=True),
|
| 983 |
gr.Slider(value=para_idx), render_story_text(paras, para_idx),
|
| 984 |
gr.HTML(visible=is_last),
|
| 985 |
gr.HTML(visible=False), gr.Group(visible=False), gr.Button(visible=False))
|
| 986 |
|
| 987 |
+
yield (gr.update(), _status_done, f"<div style='text-align:center;font-size:10px;color:#94a3b8;font-family:monospace;margin-top:8px;'>✅ Story complete</div>",
|
| 988 |
gr.Button(visible=True), gr.Button(visible=False), gr.Button(visible=False),
|
| 989 |
gr.Slider(value=max(n_paras - 1, 0)), render_story_text(paras, max(n_paras - 1, 0)),
|
| 990 |
gr.HTML(visible=True),
|