From 97c11e08e098cdcbaf6f6bb40cb7228bfcda8d2c Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 12 Jun 2026 17:46:03 +0800 Subject: [PATCH] Reduce post-synthesis UI flicker by removing 1s status timer. Refresh status every 60s only, shorten synth log, update log before audio, and isolate repaint regions. Co-authored-by: Cursor --- app.py | 59 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/app.py b/app.py index 4afe407..30732ea 100644 --- a/app.py +++ b/app.py @@ -6,6 +6,7 @@ Gradio Web 中控:音色锁定 → Whisper 识别 → Gemma4 润色 → ChatTT from __future__ import annotations import logging +import re import shutil import sys import uuid @@ -127,16 +128,27 @@ def ui_check_ollama() -> str: # --------------------------------------------------------------------------- # 模块 4:ChatTTS 音频合成 # --------------------------------------------------------------------------- -def ui_synthesize(polished_text: str, voice_label: str) -> tuple[str | None, str]: - """【TTS 合成】生成最终 wav 配音文件。""" +def _short_synth_log(msg: str, ok: bool) -> str: + """合成日志简短显示,避免长路径触发大面积重绘闪屏。""" + if not ok: + return f"❌ {msg}" + + chars = re.search(r"朗读\s*(\d+)\s*字", msg) + segs = re.search(r"共\s*(\d+)\s*段", msg) + if chars: + seg_note = f",{segs.group(1)} 段拼接" if segs else "" + return f"✅ 配音完成({chars.group(1)} 字{seg_note})。请用下方播放器试听、下载。" + return "✅ 配音完成。请用下方播放器试听、下载。" + + +def ui_synthesize(polished_text: str, voice_label: str) -> tuple[str, str | None]: + """【TTS 合成】生成最终 wav 配音文件。先更新日志再更新播放器,减轻闪屏。""" if not polished_text or not polished_text.strip(): - return None, "请先完成 Gemma4 润色。" + return "请先完成 Gemma4 润色。", None voice_id = label_to_voice_id(voice_label) ok, msg, wav_path = generate_voice(polished_text, voice_id=voice_id) - if ok and wav_path: - return wav_path, f"✅ {msg}" - return None, f"❌ {msg}" + return _short_synth_log(msg, ok), wav_path if ok else None # --------------------------------------------------------------------------- @@ -863,10 +875,25 @@ gradio-app, .status-err { border-left-color: #ef4444 !important; background: #2a1515 !important; } .status-err .status-body { color: #fecaca !important; } +/* 状态栏与播放器隔离重绘,减轻合成完成后的闪屏 */ +.status-row { + contain: layout style paint; + min-height: 88px; +} .gradio-container .audio-container, .gradio-container .upload-container { border: 2px dashed #4b5563 !important; background: #1a2332 !important; + contain: layout style paint; + min-height: 100px; +} +.gradio-container .audio-container audio, +.gradio-container .audio-container canvas, +.gradio-container .waveform-container { + background: #1a2332 !important; +} +.gradio-container .pipeline-step-card textarea { + contain: layout style; } footer { visibility: hidden; } @@ -918,11 +945,8 @@ def ui_check_ollama_html(force: bool = False) -> str: def ui_initial_load() -> tuple[str, str]: - """首屏立即返回,不发起网络请求,避免平板白屏等待。""" - return ( - _status_html("Ollama 节点", "后台检测中,请稍候…", "warn"), - ui_speaker_status_html(), - ) + """首屏加载:检测 Ollama + 音色(仅一次,不用高频 Timer 避免闪屏)。""" + return ui_refresh_status_html(force=False) def ui_refresh_status_html(force: bool = False) -> tuple[str, str]: @@ -998,19 +1022,13 @@ def build_app() -> gr.Blocks: outputs=[ollama_status, speaker_status], ) - # 首屏秒开:仅本地检测音色,Ollama 延后到 Timer demo.load( fn=ui_initial_load, outputs=[ollama_status, speaker_status], ) - # 1 秒后后台检测 Ollama;之后每 30s 刷新(30s 内走缓存) - status_timer = gr.Timer(value=1, active=True) - status_timer.tick( - fn=lambda: ui_refresh_status_html(force=False), - outputs=[ollama_status, speaker_status], - ) - status_timer_slow = gr.Timer(value=30, active=True) + # 仅低频刷新状态(去掉 1s Timer,避免合成后整页闪屏) + status_timer_slow = gr.Timer(value=60, active=True) status_timer_slow.tick( fn=lambda: ui_refresh_status_html(force=True), outputs=[ollama_status, speaker_status], @@ -1100,7 +1118,8 @@ def build_app() -> gr.Blocks: synth_btn.click( ui_synthesize, [polished_text, tts_voice], - [output_audio, synth_log], + [synth_log, output_audio], + show_progress="hidden", ) # ---- Tab 3: 一键生产 ----