Spaces:
Sleeping
Sleeping
| 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(""" | |
| <div style="text-align:center; margin-bottom: 4px;"><h1>🎙️ Lecture Transcriber</h1></div> | |
| <div style="text-align:center; color:#888; margin-bottom:24px; font-size:0.95em;"> | |
| Upload an <b>.mp3</b> or <b>.m4a</b> audio file and get a formatted transcript instantly. | |
| </div> | |
| """) | |
| 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(""" | |
| <div style="text-align:center; margin-top:16px; color:#aaa; font-size:0.85em;"> | |
| Powered by <a href="https://github.com/SYSTRAN/faster-whisper" target="_blank">faster-whisper</a> | |
| · Paragraphs split by natural pauses · Runs 100% locally, no data stored. | |
| </div> | |
| """) | |
| demo.launch( | |
| theme=gr.themes.Soft(primary_hue="violet"), | |
| css=""" | |
| footer { display: none !important; } | |
| """, | |
| ) | |