import gradio as gr from faster_whisper import WhisperModel from docx import Document import tempfile import os # Load model once at startup (cached after first run) print("Loading Whisper model...") model = WhisperModel("medium", compute_type="int8") print("Model ready!") def build_paragraphs(segments, pause_threshold=1.5): """ Group Whisper segments into paragraphs. A new paragraph starts whenever the gap between the end of one segment and the start of the next exceeds `pause_threshold` seconds. """ paragraphs = [] current_sentences = [] prev_end = None for seg in segments: if prev_end is not None and (seg.start - prev_end) >= pause_threshold: # Long pause → flush current paragraph if current_sentences: paragraphs.append(" ".join(current_sentences).strip()) current_sentences = [] current_sentences.append(seg.text.strip()) prev_end = seg.end # Flush remaining if current_sentences: paragraphs.append(" ".join(current_sentences).strip()) return paragraphs def transcribe_audio(audio_file, output_format): """Transcribe an uploaded audio file and return a downloadable file + preview text.""" if audio_file is None: return None, "⚠️ Please upload an audio file." # Transcribe — consume iterator once segments, _ = model.transcribe(audio_file) paragraphs = build_paragraphs(list(segments)) base_name = os.path.splitext(os.path.basename(audio_file))[0] if output_format == "Word (.docx)": # ── Build Word document ─────────────────────────────────────────────── doc = Document() doc.add_heading("Lecture Transcript", level=1) for para in paragraphs: doc.add_paragraph(para) tmp = tempfile.NamedTemporaryFile( delete=False, suffix=".docx", prefix=f"{base_name}_transcript_", ) tmp.close() doc.save(tmp.name) preview = "\n\n".join(paragraphs) return tmp.name, preview else: # ── Plain text ──────────────────────────────────────────────────────── full_text = "\n\n".join(paragraphs) tmp = tempfile.NamedTemporaryFile( delete=False, suffix=".txt", prefix=f"{base_name}_transcript_", mode="w", encoding="utf-8", ) tmp.write(full_text) tmp.close() return tmp.name, full_text # ── Gradio UI ───────────────────────────────────────────────────────────────── with gr.Blocks(title="🎙️ Lecture Transcriber") as demo: gr.HTML("""

🎙️ Lecture Transcriber

Upload an .mp3 or .m4a audio file and get a formatted transcript instantly.
""") with gr.Row(): with gr.Column(scale=1): audio_input = gr.Audio( label="Upload Audio File", type="filepath", sources=["upload"], ) format_radio = gr.Radio( choices=["Plain Text (.txt)", "Word (.docx)"], value="Plain Text (.txt)", label="Output Format", ) transcribe_btn = gr.Button("✨ Transcribe", variant="primary", size="lg") with gr.Column(scale=1): text_output = gr.Textbox( label="Transcript Preview", placeholder="Your transcript will appear here, split into paragraphs by natural pauses...", lines=16, ) file_output = gr.File(label="⬇️ Download Transcript") transcribe_btn.click( fn=transcribe_audio, inputs=[audio_input, format_radio], outputs=[file_output, text_output], show_progress="full", ) gr.HTML("""
Powered by faster-whisper · Paragraphs split by natural pauses · Runs 100% locally, no data stored.
""") demo.launch( theme=gr.themes.Soft(primary_hue="violet"), css=""" footer { display: none !important; } """, )