Spaces:
Sleeping
Sleeping
File size: 5,505 Bytes
f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d ad814c6 f3f662d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | import streamlit as st
import torch
from transformers import pipeline
import tempfile
import os
st.set_page_config(page_title="Meeting Audio Analyzer", page_icon="🎙️", layout="wide")
st.title("🎙️ Meeting Audio Analyzer")
st.caption("Upload a meeting recording — get a full transcript, summary, action items, and key topics.")
@st.cache_resource
def load_models():
transcriber = pipeline(
"automatic-speech-recognition",
model="openai/whisper-base",
chunk_length_s=30,
stride_length_s=5,
return_timestamps=False,
device=0 if torch.cuda.is_available() else -1,
)
summarizer = pipeline(
"summarization",
model="sshleifer/distilbart-cnn-12-6",
device=0 if torch.cuda.is_available() else -1,
)
return transcriber, summarizer
with st.spinner("Loading models (first run takes ~2 minutes)..."):
transcriber, summarizer = load_models()
def chunk_text(text, max_tokens=900):
words = text.split()
chunks, current = [], []
for word in words:
current.append(word)
if len(current) >= max_tokens:
chunks.append(" ".join(current))
current = []
if current:
chunks.append(" ".join(current))
return chunks
def summarize_transcript(transcript):
if not transcript.strip():
return "No transcript available."
word_count = len(transcript.split())
if word_count <= 900:
result = summarizer(transcript, max_length=200, min_length=60, do_sample=False)
return result[0]["summary_text"]
chunks = chunk_text(transcript)
chunk_summaries = []
for chunk in chunks:
r = summarizer(chunk, max_length=150, min_length=40, do_sample=False)
chunk_summaries.append(r[0]["summary_text"])
combined = " ".join(chunk_summaries)
if len(combined.split()) > 900:
combined = " ".join(combined.split()[:900])
final = summarizer(combined, max_length=250, min_length=80, do_sample=False)
return final[0]["summary_text"]
def extract_action_items(transcript):
keywords = [
"will ", "should ", "need to ", "must ", "action:", "todo:",
"follow up", "follow-up", "assign", "deadline", "by next",
"responsible", "let's ", "we'll ", "i'll ", "you'll ",
]
sentences = [s.strip() for s in transcript.replace("\n", " ").split(".") if len(s.strip()) > 15]
actions = [f"• {s}." for s in sentences if any(k in s.lower() for k in keywords)]
return "\n".join(actions[:10]) if actions else "No specific action items detected."
def extract_key_topics(summary):
stop_words = {
"the","a","an","is","are","was","were","be","been","being","have",
"has","had","do","does","did","will","would","could","should","may",
"might","and","but","or","of","in","on","at","by","for","with",
"to","from","this","that","it","its","they","we","you","he","she",
"also","if","any","then","what","which","who","how","all","each",
"very","just","too","than","both","about","into","through","these",
}
words = summary.lower().split()
freq = {}
for w in words:
w = w.strip(".,!?;:()'\"")
if w and w not in stop_words and len(w) > 3:
freq[w] = freq.get(w, 0) + 1
top = sorted(freq, key=freq.get, reverse=True)[:8]
return " • ".join(t.title() for t in top) if top else "Could not extract topics."
uploaded_file = st.file_uploader(
"Upload your meeting audio",
type=["mp3", "wav", "m4a", "ogg", "webm", "flac"],
)
if uploaded_file is not None:
st.audio(uploaded_file)
if st.button("Analyze Meeting", type="primary", use_container_width=True):
suffix = os.path.splitext(uploaded_file.name)[1] or ".mp3"
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
tmp.write(uploaded_file.read())
tmp_path = tmp.name
try:
with st.spinner("Transcribing audio..."):
result = transcriber(tmp_path)
transcript = result["text"].strip()
if not transcript:
st.error("Transcription produced no text. Try a clearer audio file.")
else:
with st.spinner("Analyzing meeting content..."):
summary = summarize_transcript(transcript)
actions = extract_action_items(transcript)
topics = extract_key_topics(summary)
word_count = len(transcript.split())
st.success(f"Done! {word_count} words transcribed — ~{word_count // 130 + 1} min read")
tab1, tab2, tab3, tab4 = st.tabs(["Summary", "Action Items", "Key Topics", "Full Transcript"])
with tab1:
st.subheader("Meeting Summary")
st.write(summary)
with tab2:
st.subheader("Action Items")
st.text(actions)
with tab3:
st.subheader("Key Topics")
st.write(topics)
with tab4:
st.subheader("Full Transcript")
st.text_area("", transcript, height=400, label_visibility="collapsed")
except Exception as e:
st.error(f"Error during processing: {str(e)}")
finally:
os.unlink(tmp_path)
st.divider()
st.caption("Models: Whisper Base · DistilBART CNN — runs fully locally, no API keys needed.") |