File size: 6,243 Bytes
893d6fd
2b8c67d
 
238e24b
 
 
e36de78
 
 
 
 
 
 
 
 
 
 
 
 
 
2b8c67d
e36de78
2b8c67d
 
 
e36de78
2b8c67d
1eb601c
28f8c47
893d6fd
bb4dadb
 
 
 
 
 
 
 
 
 
1eb601c
893d6fd
 
 
1eb601c
2b8c67d
e36de78
893d6fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e36de78
 
893d6fd
 
 
 
 
 
 
 
 
 
 
e36de78
 
893d6fd
 
 
2b8c67d
893d6fd
 
 
 
 
 
 
 
 
 
 
 
2b8c67d
 
 
 
327fd75
e36de78
893d6fd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""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)])