TextTranscribe / app.py
canmizrakli's picture
Upload 2 files
a41f928 verified
Raw
History Blame Contribute Delete
4.81 kB
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; }
""",
)