"""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'
' f'{num}' f'

{title}

' f'
' ) def build_stepper(languages=LANGUAGES, example_urls=EXAMPLE_URLS) -> dict: """4-card stepper: Source / Configure / Process / Review. Returns named handles.""" handles: dict = {} # Step 1 — Source 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() # Step 2 — Configure 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)", ) # Step 3 — Process 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('Downloads YouTube URL or processes uploaded file.') # Step 4 — Review 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