| """Two-pane brutalist layout: 4-step stepper + tabbed results dock.""" |
|
|
| import pandas as pd |
| import gradio as gr |
|
|
|
|
| LANGUAGES = [ |
| ("Auto-detect", None), ("English", "en"), ("Japanese", "ja"), ("Chinese", "zh"), |
| ("Spanish", "es"), ("French", "fr"), ("German", "de"), ("Korean", "ko"), |
| ("Portuguese", "pt"), ("Russian", "ru"), ("Italian", "it"), ("Vietnamese", "vi"), |
| ("Arabic", "ar"), ("Hindi", "hi"), ("Indonesian", "id"), ("Dutch", "nl"), |
| ("Polish", "pl"), ("Turkish", "tr"), ("Thai", "th"), ("Ukrainian", "uk"), |
| ] |
|
|
| EXAMPLE_URLS = [ |
| "https://www.youtube.com/watch?v=j7BfEzAFuYc&t=32s", |
| "https://www.youtube.com/watch?v=-UX0X45sYe4", |
| "https://www.youtube.com/watch?v=7minSgqi-Gw", |
| ] |
|
|
|
|
| def _step_header(num: str, title: str) -> gr.HTML: |
| return gr.HTML( |
| f'<div class="brut-step-header">' |
| f'<span class="brut-step-num">{num}</span>' |
| f'<h3 class="brut-step-title">{title}</h3>' |
| f'</div>' |
| ) |
|
|
|
|
| def build_stepper(languages=LANGUAGES, example_urls=EXAMPLE_URLS) -> dict: |
| """4-card stepper: Source / Configure / Process / Review. Returns named handles.""" |
| handles: dict = {} |
|
|
| |
| with gr.Group(elem_classes=["brut-step-card", "step-1"]): |
| _step_header("01", "SOURCE") |
| handles["yt_url"] = gr.Textbox(label="YouTube URL", lines=1, placeholder="https://www.youtube.com/watch?v=...") |
| handles["video_in"] = gr.Video(label="Video / Audio file") |
| gr.Examples(examples=example_urls, inputs=[handles["yt_url"]], label="YouTube examples") |
| with gr.Accordion("Advanced YouTube download", open=False): |
| handles["yt_btn"] = gr.Button("Download YouTube video") |
| handles["yt_status"] = gr.Markdown() |
|
|
| |
| with gr.Group(elem_classes=["brut-step-card", "step-2"]): |
| _step_header("02", "CONFIGURE") |
| handles["language"] = gr.Dropdown( |
| choices=languages, value=None, |
| label="Spoken language (auto-detect if blank)", |
| ) |
| handles["num_speakers"] = gr.Number( |
| value=0, precision=0, |
| label="Number of speakers (0 = auto-detect via pyannote)", |
| ) |
|
|
| |
| with gr.Group(elem_classes=["brut-step-card", "step-3"]): |
| _step_header("03", "PROCESS") |
| handles["run_btn"] = gr.Button("PROCESS", variant="primary", elem_classes="brut-primary") |
| gr.HTML('<small style="font-family: var(--brut-font-mono); font-size: 12px;">Downloads YouTube URL or processes uploaded file.</small>') |
|
|
| |
| with gr.Group(elem_classes=["brut-step-card", "step-4"]): |
| _step_header("04", "REVIEW") |
| handles["progress_log"] = gr.Markdown(value="", elem_classes="brut-log") |
| handles["error_card"] = gr.Markdown(value="", elem_classes="brut-error-card") |
|
|
| return handles |
|
|
|
|
| def build_results_dock() -> dict: |
| """Tabbed results dock: Transcript / Audio / Downloads / Diagnostics.""" |
| handles: dict = {} |
|
|
| with gr.Tabs(elem_classes="brut-tabs"): |
| with gr.TabItem("Transcript"): |
| handles["df_out"] = gr.DataFrame( |
| label="Transcript", |
| value=pd.DataFrame(columns=["Start", "End", "Speaker", "Text"]), |
| show_label=True, |
| row_count=(0, "dynamic"), |
| wrap=True, |
| elem_classes="brut-df", |
| interactive=False, |
| ) |
| with gr.Row(): |
| handles["edit_btn"] = gr.Button("EDIT SPEAKERS") |
| handles["apply_btn"] = gr.Button("APPLY RENAMES", variant="primary", visible=False) |
| handles["cancel_btn"] = gr.Button("CANCEL", visible=False) |
|
|
| with gr.TabItem("Audio"): |
| handles["audio_player"] = gr.Audio( |
| label="Converted WAV (16kHz mono)", |
| interactive=False, |
| show_download_button=True, |
| elem_id="brut-audio-mount", |
| elem_classes="brut-audio-player", |
| value=None, |
| ) |
| handles["seek_bus"] = gr.HTML(value="", elem_id="brut-seek-bus") |
|
|
| with gr.TabItem("Downloads"): |
| handles["csv_out"] = gr.File(label="Download CSV") |
| handles["srt_out"] = gr.File(label="Download SRT") |
|
|
| with gr.TabItem("Diagnostics"): |
| handles["sysinfo"] = gr.Markdown() |
|
|
| handles["merged_state"] = gr.State(value=None) |
| handles["edit_mode_state"] = gr.State(value=False) |
| handles["wav_state"] = gr.State(value=None) |
|
|
| return handles |
|
|