""" Defines the main Gradio UI layout and configuration for DuoSubs subtitle merging Hugging Face web app. This module sets up the UI components, event handlers, and manages the model loading and merging process. It includes device selection, model configuration, and subtitle file handling. """ import gradio as gr from .constants import ( DEFAULT_SUB_EXT, MERGING_MODE_INFO, MERGING_MODE_LIST, MODEL_NAME_LIST, SUB_EXT_LIST, SUB_EXT_LIST_WITH_DOT, TITLE_HTML, ) from .events import ( cancel_merging, start_merging, states_after_merging, states_during_merging, validate_excluded_subtitle_file, ) from .utils import create_model_pools, open_html model_pool = create_model_pools(MODEL_NAME_LIST) def create_main_gr_blocks_ui( cache_delete_frequency: int = 3600, cache_delete_age: int = 14400, merging_concurrency_limit: int = 5 ) -> gr.Blocks: """ Builds and returns the main Gradio Blocks UI for DuoSubs. Args: cache_delete_frequency (int): How often to delete cache (seconds). Default is 1 hour or 3600 seconds. cache_delete_age (int): Age threshold for cache deletion (seconds). Default is 4 hour or 14400 seconds. merging_concurrency_limit (int): Maximum number of concurrent merges allowed. Default is 5. Returns: gradio.Blocks: The constructed Gradio UI. """ main_block = gr.Blocks( title="DuoSubs", theme=gr.themes.Ocean(), delete_cache=(cache_delete_frequency, cache_delete_age), analytics_enabled=False ) ui: gr.Blocks with main_block as ui: global model_pool cancel_state = gr.State([False]) title_content = open_html(TITLE_HTML) gr.HTML(title_content) with gr.Row(): with gr.Column(scale=9): ( primary_file, secondary_file, merged_file, merge_button, cancel_button ) = _create_subtitles_io_block() with gr.Column(scale=11): gr.Markdown("### âš™ī¸ Configurations") with gr.Tab("Model Selection"): model_name = _create_model_configurations_block() with gr.Tab("Alignment Behavior"): merging_mode = _create_alignment_behaviour_block() with gr.Tab("Output Styling"): ( retain_newline, secondary_above_primary ) = _create_output_styling_block() with gr.Tab("File Exports"): ( omit_subtitles, combined_format, primary_format, secondary_format ) = _create_file_exports_block() omit_subtitles.change( fn=validate_excluded_subtitle_file, inputs=omit_subtitles ) merge_button.click( fn=states_during_merging, inputs=cancel_state, outputs=[merge_button, cancel_button] ).then( fn=wrapped_start_merging, inputs=[ primary_file, secondary_file, model_name, merging_mode, retain_newline, secondary_above_primary, omit_subtitles, combined_format, primary_format, secondary_format, cancel_state ], outputs=merged_file, concurrency_limit=merging_concurrency_limit ).then( fn=states_after_merging, inputs=cancel_state, outputs=[merge_button, cancel_button] ) cancel_button.click( fn=cancel_merging, inputs=cancel_state, outputs=cancel_button, concurrency_limit=None ) return ui def _create_subtitles_io_block( ) -> tuple[gr.File, gr.File, gr.File, gr.Button, gr.Button]: """ Creates subtitle file input/output UI components. This function sets up the UI for uploading primary and secondary subtitle files, buttons to initiate and cancel the merging process, and creates the merged output file. Returns: tuple[gradio.File, gradio.File, gradio.File, gradio.Button, gradio.Button]: - primary_file - secondary_file - merged_file - merge_button - cancel_button """ gr.Markdown("### 📄 Input Subtitles") with gr.Row(): primary_file = gr.File( label="Primary Subtitle File", file_types=SUB_EXT_LIST_WITH_DOT ) secondary_file = gr.File( label="Secondary Subtitle File", file_types=SUB_EXT_LIST_WITH_DOT ) gr.Markdown("### đŸ“Ļ Output Zip") merged_file = gr.File(label="Processed Subtitles (in zip)") with gr.Row(): merge_button = gr.Button("Merge") cancel_button = gr.Button("Cancel", interactive=False) return primary_file, secondary_file, merged_file, merge_button, cancel_button def _create_model_configurations_block() -> gr.Dropdown: """ Creates model selection configuration UI components. This function sets up the UI for selecting the model name. Returns: gradio.Dropdown: model name """ with gr.Column(): model_name = gr.Dropdown( choices=MODEL_NAME_LIST, label="Sentence Transformer Model", value=MODEL_NAME_LIST[0], info=( "Model for computing subtitle sentence similarity, " "with float32 precision" ) ) return model_name def _create_alignment_behaviour_block() -> gr.Radio: """ Creates alignment behavior UI components. This function sets up a radio buttons for selecting the subtitles merging mode. Returns: gradio.Radio: Radio buttons for merging mode. """ mode_content = open_html(MERGING_MODE_INFO) with gr.Column(): merging_mode = gr.Radio( label="Merging Mode", choices=MERGING_MODE_LIST, value=MERGING_MODE_LIST[0], info="Please refer to **â„šī¸ Info** below for more information" # noqa: RUF001 ) with gr.Accordion("â„šī¸ Info"): # noqa: RUF001 gr.HTML(mode_content) return merging_mode def _create_output_styling_block() -> tuple[gr.Checkbox, gr.Checkbox]: """ Creates output styling UI components. This function sets up checkboxes for retaining newlines in the original subtitles and placing secondary subtitles above primary subtitles in the merged output. Returns: tuple[gradio.Checkbox, gradio.Checkbox]: - retain_newline - secondary_above_primary """ with gr.Column(): retain_newline = gr.Checkbox( label="Retain Newlines", value=False, info="**Retain line breaks** from the original subtitles" ) secondary_above_primary = gr.Checkbox( label="Secondary subtitle above primary subtitle", value=False, info="Place **secondary** subtitle **above** the **primary**" ) return retain_newline, secondary_above_primary def _create_file_exports_block( ) -> tuple[gr.CheckboxGroup, gr.Dropdown, gr.Dropdown, gr.Dropdown]: """ Creates file export UI components. This function sets up checkboxes for excluding certain subtitle files from the ZIP output, and dropdowns for selecting the format of combined, primary, and secondary subtitles. Returns: tuple[gradio.CheckboxGroup, gradio.Dropdown, gradio.Dropdown, gradio.Dropdown]: - omit_subtitles - combined_format - primary_format - secondary_format """ with gr.Column(): omit_subtitles = gr.CheckboxGroup( ["Combined", "Primary", "Secondary"], type="value", label="Excluded Subtitle Files from ZIP" ) with gr.Column(): gr.Markdown("Subtitle Format") combined_format = gr.Dropdown( choices=SUB_EXT_LIST, value=DEFAULT_SUB_EXT, label="Combined" ) primary_format = gr.Dropdown( choices=SUB_EXT_LIST, value=DEFAULT_SUB_EXT, label="Primary" ) secondary_format = gr.Dropdown( choices=SUB_EXT_LIST, value=DEFAULT_SUB_EXT, label="Secondary" ) return omit_subtitles, combined_format, primary_format, secondary_format def wrapped_start_merging( primary_subtitles: str, secondary_subtitles: str, model_name: str, merging_mode: str, retain_newline: bool, secondary_above_primary: bool, omit_subtitles: list[str], combined_format: str, primary_format:str, secondary_format: str, cancel_state: list[bool], progress: gr.Progress | None = None ) -> str | None: """ Wrapper for starting the merging process with all required arguments. Args: primary_subtitles (str): Path to primary subtitle file. secondary_subtitles (str): Path to secondary subtitle file. model_name (str): Name of the model to use. merging_mode (str): Subttitle merging mode (based on the value of MergingMode). retain_newline (bool): Whether to retain newlines in output. secondary_above_primary (bool): Whether to place secondary subtitle above primary. omit_subtitles (list[str]): List of subtitle types to omit from output (based on the value of OmitFile). combined_format (str): Format for combined subtitles (based on the value of SubtitleFormat). primary_format (str): Format for primary subtitles (based on the value of SubtitleFormat). secondary_format (str): Format for secondary subtitles (based on the value of SubtitleFormat). gpu_list (list[str]): List of available GPU names. loaded_model_device (list[str]): List tracking loaded model device. loaded_model_name (list[str]): List tracking loaded model name. cancel_state (list[bool]): List tracking cancellation state. request (gradio.Request): Gradio request object. progress (gradio.Progress) : Gradio progress object (optional). Defaults to None. Returns: str | None: Path to the output ZIP file, or None if cancelled. """ return start_merging( model_pool, primary_subtitles, secondary_subtitles, model_name, 256, merging_mode, retain_newline, secondary_above_primary, omit_subtitles, combined_format, primary_format, secondary_format, cancel_state, progress )