| import os |
| import gradio as gr |
| import re |
| import logging |
| from dotenv import load_dotenv |
| from src.core.transformer import TranscriptTransformer |
| from src.utils.pdf_processor import PDFProcessor |
| from src.utils.text_processor import TextProcessor |
|
|
| |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
|
| load_dotenv() |
| logging.info("Environment variables loaded.") |
|
|
| |
| TRANSLATIONS = { |
| "en": { |
| "title": "AI Script Generator", |
| "subtitle": "Transform transcripts and PDFs into timed, structured teaching scripts using AI", |
| "input_type_label": "Input Type", |
| "input_type_options": ["PDF", "Raw Text"], |
| "upload_pdf_label": "Upload Transcript (PDF)", |
| "paste_text_label": "Paste Transcript Text", |
| "paste_text_placeholder": "Paste your transcript text here...", |
| "guiding_prompt_label": "Guiding Prompt (Optional)", |
| "guiding_prompt_placeholder": "Additional instructions to customize the output. Examples: 'Use a more informal tone', 'Focus only on section X', 'Generate the content in Spanish', 'Include more practical programming examples', etc.", |
| "guiding_prompt_info": "The Guiding Prompt allows you to provide specific instructions to modify the generated content, like output/desired LANGUAGE. You can use it to change the tone, style, focus ONLY on specific sections of the text, specify the output language (e.g., 'Generate in Spanish/French/German'), or give any other instruction that helps personalize the final result.", |
| "duration_label": "Target Lecture Duration (minutes)", |
| "examples_label": "Include Practical Examples", |
| "thinking_model_label": "Use Experimental Thinking Model (Gemini Only)", |
| "submit_button": "Transform Transcript", |
| "output_label": "Generated Teaching Transcript", |
| "error_no_pdf": "Error: No PDF file uploaded", |
| "error_no_text": "Error: No text provided", |
| "error_prefix": "Error processing transcript: ", |
| "language_selector": "Language / Idioma", |
| "show_timestamps": "Show Timestamps", |
| "hide_timestamps": "Hide Timestamps" |
| }, |
| "es": { |
| "title": "Generador de Guiones IA", |
| "subtitle": "Transforma transcripciones y PDFs en guiones de enseñanza estructurados y cronometrados usando IA", |
| "input_type_label": "Tipo de Entrada", |
| "input_type_options": ["PDF", "Texto"], |
| "upload_pdf_label": "Subir Transcripción (PDF)", |
| "paste_text_label": "Pegar Texto de Transcripción", |
| "paste_text_placeholder": "Pega tu texto de transcripción aquí...", |
| "guiding_prompt_label": "Instrucciones Guía (Opcional)", |
| "guiding_prompt_placeholder": "Instrucciones adicionales para personalizar el resultado. Ejemplos: 'Usa un tono más informal', 'Enfócate solo en la sección X', 'Genera el contenido en inglés', 'Incluye más ejemplos prácticos de programación', etc.", |
| "guiding_prompt_info": "Las Instrucciones Guía te permiten proporcionar indicaciones específicas para modificar el contenido generado, como el IDIOMA deseado. Puedes usarlas para cambiar el tono, estilo, enfocarte SOLO en secciones específicas del texto, especificar el idioma de salida (ej., 'Generar en inglés/francés/alemán'), o dar cualquier otra instrucción que ayude a personalizar el resultado final.", |
| "duration_label": "Duración Objetivo de la Clase (minutos)", |
| "examples_label": "Incluir Ejemplos Prácticos", |
| "thinking_model_label": "Usar Modelo de Pensamiento Experimental (Solo Gemini)", |
| "submit_button": "Transformar Transcripción", |
| "output_label": "Guión de Enseñanza Generado", |
| "error_no_pdf": "Error: No se ha subido ningún archivo PDF", |
| "error_no_text": "Error: No se ha proporcionado texto", |
| "error_prefix": "Error al procesar la transcripción: ", |
| "language_selector": "Language / Idioma", |
| "show_timestamps": "Mostrar Marcas de Tiempo", |
| "hide_timestamps": "Ocultar Marcas de Tiempo" |
| } |
| } |
|
|
| |
| LANGUAGE_PROMPTS = { |
| "en": "", |
| "es": "Generate the content in Spanish. Genera todo el contenido en español." |
| } |
|
|
| class TranscriptTransformerApp: |
| def __init__(self): |
| logging.info("Initializing TranscriptTransformerApp...") |
| self.pdf_processor = PDFProcessor() |
| self.text_processor = TextProcessor() |
| self.current_language = "en" |
| self.last_generated_content = "" |
| self.content_with_timestamps = "" |
| self.content_without_timestamps = "" |
| logging.info("TranscriptTransformerApp initialized.") |
|
|
| def process_transcript(self, |
| language: str, |
| input_type: str, |
| file_obj: gr.File = None, |
| raw_text_input: str = "", |
| initial_prompt: str = "", |
| target_duration: int = 30, |
| include_examples: bool = True, |
| use_gemini: bool = True, |
| use_thinking_model: bool = False) -> str: |
| """ |
| Process uploaded transcript and transform it into a teaching transcript |
| |
| Args: |
| language: Selected UI language |
| input_type: Type of input (PDF or Raw Text) |
| file_obj: Uploaded PDF file (if input_type is PDF) |
| raw_text_input: Raw text input (if input_type is Raw Text) |
| initial_prompt: Additional guiding instructions for the content generation |
| target_duration: Target lecture duration in minutes |
| include_examples: Whether to include practical examples |
| use_gemini: Whether to use Gemini API instead of OpenAI |
| use_thinking_model: Requires use_gemini=True |
| |
| Returns: |
| str: Generated teaching transcript |
| """ |
| logging.info(f"Processing transcript. Language: {language}, InputType: {input_type}, HasFile: {file_obj is not None}, HasText: {bool(raw_text_input)}, Duration: {target_duration}, Examples: {include_examples}, Gemini: {use_gemini}, ThinkingModel: {use_thinking_model}") |
| try: |
| |
| if use_thinking_model: |
| logging.info("Thinking model selected, forcing use_gemini=True") |
| use_gemini = True |
| |
| self.transformer = TranscriptTransformer( |
| use_gemini=use_gemini, |
| use_thinking_model=use_thinking_model |
| ) |
| |
| |
| if input_type == TRANSLATIONS[language]["input_type_options"][0]: |
| if file_obj is None: |
| return TRANSLATIONS[language]["error_no_pdf"] |
| raw_text = self.pdf_processor.extract_text(file_obj.name) |
| else: |
| if not raw_text_input.strip(): |
| return TRANSLATIONS[language]["error_no_text"] |
| raw_text = raw_text_input |
| |
| |
| modified_prompt = initial_prompt |
| |
| |
| language_keywords = ["spanish", "español", "english", "inglés", "french", "francés", "german", "alemán"] |
| user_specified_language = any(keyword in initial_prompt.lower() for keyword in language_keywords) |
| |
| |
| if not user_specified_language and language in LANGUAGE_PROMPTS and LANGUAGE_PROMPTS[language]: |
| if modified_prompt: |
| modified_prompt += " " + LANGUAGE_PROMPTS[language] |
| else: |
| modified_prompt = LANGUAGE_PROMPTS[language] |
| |
| |
| lecture_transcript = self.transformer.transform_to_lecture( |
| text=raw_text, |
| target_duration=target_duration, |
| include_examples=include_examples, |
| initial_prompt=modified_prompt |
| ) |
| |
| |
| self.content_with_timestamps = lecture_transcript |
| logging.info("Generated content stored (with timestamps).") |
| |
| |
| self.content_without_timestamps = self.remove_timestamps(lecture_transcript) |
| logging.info("Generated content stored (without timestamps).") |
| |
| |
| self.last_generated_content = lecture_transcript |
| |
| logging.info("Transcript processing successful.") |
| return lecture_transcript |
| |
| except Exception as e: |
| logging.error(f"Error processing transcript: {e}", exc_info=True) |
| return f"{TRANSLATIONS[language]['error_prefix']}{str(e)}" |
| |
| def remove_timestamps(self, text): |
| """Remove all timestamps (e.g., [00:00]) from the text""" |
| logging.info("Removing timestamps...") |
| |
| result = re.sub(r'\[\d{1,2}:\d{2}(:\d{2})?\]', '', text) |
| logging.info("Timestamps removed.") |
| return result |
| |
| def toggle_timestamps(self, show_timestamps): |
| """Toggle visibility of timestamps in output""" |
| logging.info(f"Toggling timestamps visibility. Show: {show_timestamps}") |
| if show_timestamps: |
| logging.info("Returning content WITH timestamps.") |
| return self.content_with_timestamps |
| else: |
| logging.info("Returning content WITHOUT timestamps.") |
| return self.content_without_timestamps |
| |
| def update_ui_language(self, language): |
| """Update UI elements based on selected language""" |
| logging.info(f"Updating UI language to: {language}") |
| self.current_language = language |
| |
| translations = TRANSLATIONS[language] |
| |
| return [ |
| translations["title"], |
| translations["subtitle"], |
| translations["input_type_label"], |
| gr.update(choices=translations["input_type_options"], value=translations["input_type_options"][0]), |
| translations["upload_pdf_label"], |
| translations["paste_text_label"], |
| translations["paste_text_placeholder"], |
| translations["guiding_prompt_label"], |
| translations["guiding_prompt_placeholder"], |
| translations["guiding_prompt_info"], |
| translations["duration_label"], |
| translations["examples_label"], |
| translations["thinking_model_label"], |
| translations["submit_button"], |
| translations["output_label"] |
| ] |
| logging.info("UI language updated.") |
|
|
| def launch(self): |
| """Launch the Gradio interface""" |
| logging.info("Configuring Gradio interface...") |
| |
| example_pdf = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "sample2.pdf") |
| logging.info(f"Example PDF path: {example_pdf}") |
| |
| with gr.Blocks(title=TRANSLATIONS["en"]["title"]) as interface: |
| |
| with gr.Row(): |
| with gr.Column(scale=4): |
| title_md = gr.Markdown("# " + TRANSLATIONS["en"]["title"]) |
| with gr.Column(scale=1): |
| language_selector = gr.Dropdown( |
| choices=["🇺🇸 English", "🇪🇸 Español"], |
| value="🇺🇸 English", |
| label=TRANSLATIONS["en"]["language_selector"], |
| elem_id="language-selector", |
| interactive=True |
| ) |
| |
| |
| subtitle_md = gr.Markdown(TRANSLATIONS["en"]["subtitle"]) |
| |
| |
| with gr.Row(): |
| input_type = gr.Radio( |
| choices=TRANSLATIONS["en"]["input_type_options"], |
| label=TRANSLATIONS["en"]["input_type_label"], |
| value=TRANSLATIONS["en"]["input_type_options"][0] |
| ) |
| |
| |
| with gr.Row(): |
| with gr.Column(visible=True) as pdf_column: |
| file_input = gr.File( |
| label=TRANSLATIONS["en"]["upload_pdf_label"], |
| file_types=[".pdf"] |
| ) |
| |
| with gr.Column(visible=False) as text_column: |
| text_input = gr.Textbox( |
| label=TRANSLATIONS["en"]["paste_text_label"], |
| lines=10, |
| placeholder=TRANSLATIONS["en"]["paste_text_placeholder"] |
| ) |
| |
| |
| with gr.Row(): |
| initial_prompt = gr.Textbox( |
| label=TRANSLATIONS["en"]["guiding_prompt_label"], |
| lines=3, |
| value="", |
| placeholder=TRANSLATIONS["en"]["guiding_prompt_placeholder"], |
| info=TRANSLATIONS["en"]["guiding_prompt_info"] |
| ) |
| |
| |
| with gr.Row(): |
| target_duration = gr.Number( |
| label=TRANSLATIONS["en"]["duration_label"], |
| value=30, |
| minimum=2, |
| maximum=60, |
| step=1 |
| ) |
| |
| include_examples = gr.Checkbox( |
| label=TRANSLATIONS["en"]["examples_label"], |
| value=True |
| ) |
| |
| use_thinking_model = gr.Checkbox( |
| label=TRANSLATIONS["en"]["thinking_model_label"], |
| value=True |
| ) |
| |
| |
| with gr.Row(): |
| submit_btn = gr.Button(TRANSLATIONS["en"]["submit_button"]) |
| |
| |
| output = gr.Textbox( |
| label=TRANSLATIONS["en"]["output_label"], |
| lines=25 |
| ) |
| |
| |
| with gr.Row(): |
| timestamps_checkbox = gr.Checkbox( |
| label=TRANSLATIONS["en"]["show_timestamps"], |
| value=True, |
| interactive=True |
| ) |
| |
| |
| lang_map = { |
| "🇺🇸 English": "en", |
| "🇪🇸 Español": "es" |
| } |
| |
| |
| def update_input_visibility(language_display, choice): |
| language = lang_map.get(language_display, "en") |
| return [ |
| gr.update(visible=(choice == TRANSLATIONS[language]["input_type_options"][0])), |
| gr.update(visible=(choice == TRANSLATIONS[language]["input_type_options"][1])) |
| ] |
| |
| |
| def get_language_code(language_display): |
| logging.info(f"Getting language code for display value: {language_display}") |
| code = lang_map.get(language_display, "en") |
| logging.info(f"Language code: {code}") |
| return code |
| |
| |
| def update_ui_with_display(language_display): |
| logging.info(f"Update UI triggered for language: {language_display}") |
| language = get_language_code(language_display) |
| self.current_language = language |
| |
| translations = TRANSLATIONS[language] |
| |
| return [ |
| "# " + translations["title"], |
| translations["subtitle"], |
| translations["input_type_label"], |
| gr.update(choices=translations["input_type_options"], value=translations["input_type_options"][0], label=translations["input_type_label"]), |
| gr.update(label=translations["upload_pdf_label"]), |
| gr.update(label=translations["paste_text_label"], placeholder=translations["paste_text_placeholder"]), |
| gr.update(label=translations["guiding_prompt_label"], placeholder=translations["guiding_prompt_placeholder"], info=translations["guiding_prompt_info"]), |
| gr.update(label=translations["duration_label"]), |
| gr.update(label=translations["examples_label"]), |
| gr.update(label=translations["thinking_model_label"]), |
| translations["submit_button"], |
| gr.update(label=translations["output_label"]), |
| gr.update(label=translations["show_timestamps"]) |
| ] |
| logging.info("UI elements update values prepared.") |
| |
| input_type.change( |
| fn=lambda lang_display, choice: update_input_visibility(lang_display, choice), |
| inputs=[language_selector, input_type], |
| outputs=[pdf_column, text_column] |
| ) |
| |
| |
| language_selector.change( |
| fn=update_ui_with_display, |
| inputs=language_selector, |
| outputs=[ |
| title_md, subtitle_md, |
| input_type, input_type, |
| file_input, text_input, |
| initial_prompt, |
| target_duration, include_examples, use_thinking_model, |
| submit_btn, output, |
| timestamps_checkbox |
| ] |
| ) |
| |
| |
| timestamps_checkbox.change( |
| fn=self.toggle_timestamps, |
| inputs=[timestamps_checkbox], |
| outputs=[output] |
| ) |
| |
| |
| submit_btn.click( |
| fn=lambda lang_display, *args: self.process_transcript(get_language_code(lang_display), *args), |
| inputs=[ |
| language_selector, |
| input_type, |
| file_input, |
| text_input, |
| initial_prompt, |
| target_duration, |
| include_examples, |
| use_thinking_model |
| ], |
| outputs=output |
| ) |
| |
| |
| logging.info("Setting up Gradio Examples...") |
| gr.Examples( |
| examples=[[example_pdf, "", "", 30, True, True]], |
| inputs=[file_input, text_input, initial_prompt, target_duration, include_examples, use_thinking_model] |
| ) |
| logging.info("Gradio Examples configured.") |
| |
| logging.info("Launching Gradio interface...") |
| |
| |
| interface.launch(share=True) |
| logging.info("Gradio interface launched.") |
|
|
| if __name__ == "__main__": |
| logging.info("Starting application...") |
| app = TranscriptTransformerApp() |
| app.launch() |
| logging.info("Application finished.") |