vumichien's picture
fix: guard gradio_client bool-schema patch against reload recursion
bb4dadb
Raw
History Blame Contribute Delete
6.24 kB
"""Whisper Speaker Diarization — Neobrutalism Gradio app (event wiring only)."""
import logging
import os
from pathlib import Path
def load_env_file(env_path=".env"):
path = Path(env_path)
if not path.exists():
return
for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
load_env_file()
from core import cache_manager # noqa: E402
cache_manager.get_model_cache_dir()
cache_manager.ensure_dirs()
cache_manager.cleanup_old_videos()
import gradio as gr # noqa: E402
import gradio_client.utils as _gcu # noqa: E402
# gradio_client 1.5–1.7 raises TypeError on plain bool JSON-schema nodes.
# Guard against double-patching: `gradio app.py` reload re-executes this module,
# and lambdas resolve their free vars at call time — without the guard,
# `_orig_to_py` gets rebound to the previously-installed lambda, causing
# infinite recursion when /info is requested.
if not getattr(_gcu, "_brut_bool_schema_patched", False):
_orig_get_type = _gcu.get_type
_orig_to_py = _gcu._json_schema_to_python_type
_gcu.get_type = lambda s: "Any" if isinstance(s, bool) else _orig_get_type(s)
_gcu._json_schema_to_python_type = lambda s, d=None: ("Any" if s else "None") if isinstance(s, bool) else _orig_to_py(s, d)
_gcu._brut_bool_schema_patched = True
from core.pipeline_runner import OUTPUT_DIR # noqa: E402
from core.ui import handlers # noqa: E402
from core.ui.layout import build_results_dock, build_stepper # noqa: E402
logging.basicConfig(level=logging.INFO)
_STYLES_DIR = Path(__file__).parent / "core" / "styles"
_THEME_CSS = (_STYLES_DIR / "neobrutalism.css").read_text(encoding="utf-8")
_SEEK_JS = (_STYLES_DIR / "seek.js").read_text(encoding="utf-8")
_HEAD = (
'<link rel="preconnect" href="https://fonts.googleapis.com">'
'<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>'
'<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=JetBrains+Mono&family=Space+Grotesk:wght@500;700&display=swap" rel="stylesheet">'
f'<script>{_SEEK_JS}</script>'
)
_THEME = gr.themes.Base(primary_hue="yellow", neutral_hue="stone").set(
body_background_fill="#FFF8E7",
body_background_fill_dark="#FFF8E7",
body_text_color="#000000",
body_text_color_dark="#000000",
button_primary_background_fill="#FFD23F",
button_primary_background_fill_dark="#FFD23F",
button_primary_text_color="#000000",
button_primary_text_color_dark="#000000",
button_secondary_background_fill="#FFFFFF",
button_secondary_background_fill_dark="#FFFFFF",
button_secondary_text_color="#000000",
button_secondary_text_color_dark="#000000",
background_fill_primary="#FFFFFF",
background_fill_primary_dark="#FFFFFF",
background_fill_secondary="#FFF8E7",
background_fill_secondary_dark="#FFF8E7",
block_background_fill="#FFFFFF",
block_background_fill_dark="#FFFFFF",
block_label_text_color="#000000",
block_label_text_color_dark="#000000",
block_title_text_color="#000000",
block_title_text_color_dark="#000000",
input_background_fill="#FFFFFF",
input_background_fill_dark="#FFFFFF",
panel_background_fill="#FFFFFF",
panel_background_fill_dark="#FFFFFF",
border_color_primary="#000000",
border_color_primary_dark="#000000",
)
# Force light variant always — drop .dark class on root + add .neobrut for CSS scoping
_NEOBRUT_BOOTSTRAP = (
"<script>"
"(function(){"
"function fix(){"
"var roots=[document.documentElement, document.body, document.querySelector('.gradio-container')];"
"roots.forEach(function(el){if(el){el.classList.remove('dark');el.classList.add('neobrut');}});"
"}"
"fix();"
"document.addEventListener('DOMContentLoaded',fix);"
"var mo=new MutationObserver(fix);"
"mo.observe(document.documentElement,{attributes:true,attributeFilter:['class']});"
"})();"
"</script>"
)
with gr.Blocks(title="Whisper Speaker Diarization", css=_THEME_CSS, head=_HEAD, theme=_THEME) as demo:
gr.HTML(_NEOBRUT_BOOTSTRAP)
gr.HTML('<div class="neobrut-brand">WHISPER × DIARIZE <span class="neobrut-brand-tag">v3.1</span></div>')
with gr.Row(elem_classes="brut-workspace"):
with gr.Column(scale=1):
stepper = build_stepper()
with gr.Column(scale=2):
dock = build_results_dock()
stepper["yt_btn"].click(
handlers.download_youtube_for_ui, [stepper["yt_url"]],
[stepper["video_in"], stepper["yt_status"]],
)
stepper["run_btn"].click(
handlers.run_unified,
[stepper["yt_url"], stepper["video_in"], stepper["language"], stepper["num_speakers"]],
[
dock["df_out"], dock["csv_out"], dock["srt_out"], dock["sysinfo"],
stepper["progress_log"], stepper["error_card"],
dock["merged_state"], dock["wav_state"],
dock["audio_player"], dock["seek_bus"], stepper["video_in"],
],
show_progress="minimal",
show_progress_on=[stepper["progress_log"]],
)
dock["edit_btn"].click(
handlers.enter_edit_mode, [dock["merged_state"]],
[dock["df_out"], dock["edit_btn"], dock["apply_btn"], dock["cancel_btn"], dock["edit_mode_state"]],
)
dock["cancel_btn"].click(
handlers.cancel_edit, [dock["merged_state"]],
[dock["df_out"], dock["edit_btn"], dock["apply_btn"], dock["cancel_btn"], dock["edit_mode_state"]],
)
dock["apply_btn"].click(
handlers.apply_renames, [dock["df_out"], dock["merged_state"]],
[dock["df_out"], dock["csv_out"], dock["srt_out"],
dock["edit_btn"], dock["apply_btn"], dock["cancel_btn"],
dock["edit_mode_state"], dock["merged_state"]],
)
dock["df_out"].select(
handlers.on_row_select, [dock["merged_state"], dock["edit_mode_state"]], [dock["seek_bus"]],
)
demo.queue(default_concurrency_limit=1)
if __name__ == "__main__":
demo.launch(debug=False, ssr_mode=False, allowed_paths=[str(OUTPUT_DIR)])