Update app.py
Browse files
app.py
CHANGED
@@ -1,17 +1,18 @@
|
|
1 |
-
# app.py (
|
2 |
import gradio as gr
|
3 |
import threading
|
4 |
import queue
|
5 |
import time
|
|
|
|
|
6 |
from datetime import datetime
|
7 |
from analyzer import MeetingAnalyzer
|
8 |
from integrations import Notifier
|
9 |
import config
|
|
|
10 |
|
11 |
class MeetingProcessor:
|
12 |
def __init__(self):
|
13 |
-
self.recognizer = None
|
14 |
-
self.stop_listening = None
|
15 |
self.analyzer = MeetingAnalyzer()
|
16 |
self.notifier = Notifier()
|
17 |
self.running = False
|
@@ -21,8 +22,8 @@ class MeetingProcessor:
|
|
21 |
self.action_items = []
|
22 |
self.urgent_alerts = []
|
23 |
self.transcript_queue = queue.Queue()
|
24 |
-
self.
|
25 |
-
self.
|
26 |
|
27 |
def start_processing(self):
|
28 |
if self.running:
|
@@ -34,63 +35,68 @@ class MeetingProcessor:
|
|
34 |
self.summary = ""
|
35 |
self.action_items = []
|
36 |
self.urgent_alerts = []
|
37 |
-
|
|
|
|
|
|
|
38 |
|
39 |
# Start processing threads
|
40 |
threading.Thread(target=self._audio_capture_thread, daemon=True).start()
|
41 |
-
threading.Thread(target=self.
|
42 |
|
43 |
return "Meeting processing started! 🎤"
|
44 |
|
45 |
def _audio_capture_thread(self):
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
print(f"Speech recognition error: {str(e)}")
|
66 |
-
|
67 |
-
self.stop_listening = self.recognizer.listen_in_background(
|
68 |
-
source,
|
69 |
-
callback,
|
70 |
-
phrase_time_limit=config.PHRASE_TIME_LIMIT
|
71 |
-
)
|
72 |
-
|
73 |
-
# Keep the thread running while processing
|
74 |
-
while self.running:
|
75 |
-
time.sleep(0.1)
|
76 |
|
77 |
-
def
|
|
|
78 |
while self.running:
|
79 |
try:
|
80 |
-
|
81 |
-
if
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
except queue.Empty:
|
95 |
continue
|
96 |
|
@@ -100,9 +106,6 @@ class MeetingProcessor:
|
|
100 |
|
101 |
self.running = False
|
102 |
|
103 |
-
if self.stop_listening:
|
104 |
-
self.stop_listening(wait_for_stop=False)
|
105 |
-
|
106 |
# Generate final analysis
|
107 |
self.summary = self.analyzer.generate_summary()
|
108 |
self.action_items = self.analyzer.extract_action_items()
|
@@ -116,9 +119,6 @@ class MeetingProcessor:
|
|
116 |
recipients=config.NOTIFICATION_RECIPIENTS
|
117 |
)
|
118 |
|
119 |
-
# Trigger final UI update
|
120 |
-
self.ui_update_event.set()
|
121 |
-
|
122 |
return "Meeting processing stopped! Report sent. ✅"
|
123 |
|
124 |
def get_current_status(self):
|
@@ -135,14 +135,14 @@ class MeetingProcessor:
|
|
135 |
elapsed = time.time() - self.start_time
|
136 |
mins, secs = divmod(int(elapsed), 60)
|
137 |
|
138 |
-
# Only show last
|
139 |
-
recent_transcript = "\n".join(self.transcript_history[-
|
140 |
|
141 |
return {
|
142 |
"status": "Recording",
|
143 |
"duration": f"{mins:02d}:{secs:02d}",
|
144 |
"transcript": recent_transcript,
|
145 |
-
"summary": self.summary if self.summary else "Summary will appear
|
146 |
"action_items": self.action_items,
|
147 |
"alerts": self.urgent_alerts
|
148 |
}
|
@@ -151,25 +151,26 @@ class MeetingProcessor:
|
|
151 |
processor = MeetingProcessor()
|
152 |
|
153 |
# Create Gradio interface
|
154 |
-
with gr.Blocks(title="
|
155 |
-
gr.Markdown("#
|
156 |
-
gr.Markdown("Start this during any meeting to
|
157 |
|
158 |
with gr.Row():
|
159 |
start_btn = gr.Button("Start Meeting", variant="primary")
|
160 |
stop_btn = gr.Button("Stop Meeting", variant="stop")
|
161 |
-
status_text = gr.Textbox(label="Status", interactive=False)
|
162 |
-
refresh_btn = gr.Button("Refresh Now", variant="secondary")
|
163 |
|
164 |
with gr.Row():
|
165 |
with gr.Column():
|
166 |
duration_display = gr.Textbox(label="Duration", interactive=False)
|
167 |
-
transcript_box = gr.Textbox(label="Live Transcript", lines=
|
168 |
|
169 |
with gr.Column():
|
170 |
-
|
171 |
-
|
172 |
-
|
|
|
|
|
173 |
|
174 |
# Update function for components
|
175 |
def update_components():
|
@@ -186,7 +187,7 @@ with gr.Blocks(title="Real-Time Meeting Summarizer", theme="soft") as app:
|
|
186 |
for item in current_status["alerts"]
|
187 |
) if current_status["alerts"] else "No urgent alerts",
|
188 |
current_status["summary"],
|
189 |
-
"
|
190 |
]
|
191 |
|
192 |
# Button actions
|
@@ -194,24 +195,17 @@ with gr.Blocks(title="Real-Time Meeting Summarizer", theme="soft") as app:
|
|
194 |
fn=processor.start_processing,
|
195 |
inputs=[],
|
196 |
outputs=[status_text]
|
197 |
-
).then(
|
198 |
-
update_components,
|
199 |
-
inputs=[],
|
200 |
-
outputs=[
|
201 |
-
duration_display,
|
202 |
-
transcript_box,
|
203 |
-
action_items_box,
|
204 |
-
alerts_box,
|
205 |
-
summary_box,
|
206 |
-
status_text
|
207 |
-
]
|
208 |
)
|
209 |
|
210 |
stop_btn.click(
|
211 |
fn=processor.stop_processing,
|
212 |
inputs=[],
|
213 |
outputs=[status_text]
|
214 |
-
)
|
|
|
|
|
|
|
|
|
215 |
update_components,
|
216 |
inputs=[],
|
217 |
outputs=[
|
@@ -224,8 +218,16 @@ with gr.Blocks(title="Real-Time Meeting Summarizer", theme="soft") as app:
|
|
224 |
]
|
225 |
)
|
226 |
|
227 |
-
#
|
228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
update_components,
|
230 |
inputs=[],
|
231 |
outputs=[
|
@@ -238,32 +240,5 @@ with gr.Blocks(title="Real-Time Meeting Summarizer", theme="soft") as app:
|
|
238 |
]
|
239 |
)
|
240 |
|
241 |
-
# Auto-refresh thread
|
242 |
-
def auto_refresh():
|
243 |
-
while True:
|
244 |
-
# Wait for update event
|
245 |
-
processor.ui_update_event.wait()
|
246 |
-
processor.ui_update_event.clear()
|
247 |
-
|
248 |
-
# Update the UI components
|
249 |
-
app.queue().put(
|
250 |
-
update_components,
|
251 |
-
inputs=[],
|
252 |
-
outputs=[
|
253 |
-
duration_display,
|
254 |
-
transcript_box,
|
255 |
-
action_items_box,
|
256 |
-
alerts_box,
|
257 |
-
summary_box,
|
258 |
-
status_text
|
259 |
-
]
|
260 |
-
)
|
261 |
-
|
262 |
-
# Sleep briefly to avoid excessive updates
|
263 |
-
time.sleep(0.5)
|
264 |
-
|
265 |
-
# Start the auto-refresh thread
|
266 |
-
threading.Thread(target=auto_refresh, daemon=True).start()
|
267 |
-
|
268 |
if __name__ == "__main__":
|
269 |
app.launch()
|
|
|
1 |
+
# app.py (Simplified & Robust Meeting Summarizer)
|
2 |
import gradio as gr
|
3 |
import threading
|
4 |
import queue
|
5 |
import time
|
6 |
+
import wave
|
7 |
+
import os
|
8 |
from datetime import datetime
|
9 |
from analyzer import MeetingAnalyzer
|
10 |
from integrations import Notifier
|
11 |
import config
|
12 |
+
import speech_recognition as sr
|
13 |
|
14 |
class MeetingProcessor:
|
15 |
def __init__(self):
|
|
|
|
|
16 |
self.analyzer = MeetingAnalyzer()
|
17 |
self.notifier = Notifier()
|
18 |
self.running = False
|
|
|
22 |
self.action_items = []
|
23 |
self.urgent_alerts = []
|
24 |
self.transcript_queue = queue.Queue()
|
25 |
+
self.recording_file = None
|
26 |
+
self.recognizer = sr.Recognizer()
|
27 |
|
28 |
def start_processing(self):
|
29 |
if self.running:
|
|
|
35 |
self.summary = ""
|
36 |
self.action_items = []
|
37 |
self.urgent_alerts = []
|
38 |
+
|
39 |
+
# Create a unique filename for this recording
|
40 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
41 |
+
self.recording_file = f"meeting_{timestamp}.wav"
|
42 |
|
43 |
# Start processing threads
|
44 |
threading.Thread(target=self._audio_capture_thread, daemon=True).start()
|
45 |
+
threading.Thread(target=self._transcription_thread, daemon=True).start()
|
46 |
|
47 |
return "Meeting processing started! 🎤"
|
48 |
|
49 |
def _audio_capture_thread(self):
|
50 |
+
"""Capture audio from microphone and save to file"""
|
51 |
+
try:
|
52 |
+
with sr.Microphone() as source:
|
53 |
+
print("Adjusting for ambient noise...")
|
54 |
+
self.recognizer.adjust_for_ambient_noise(source, duration=1)
|
55 |
+
print("Microphone ready! Recording meeting...")
|
56 |
+
|
57 |
+
# Start recording
|
58 |
+
audio = self.recognizer.listen(source, timeout=None, phrase_time_limit=300)
|
59 |
+
|
60 |
+
# Save audio to file
|
61 |
+
with open(self.recording_file, "wb") as f:
|
62 |
+
f.write(audio.get_wav_data())
|
63 |
+
|
64 |
+
# Add to queue for processing
|
65 |
+
self.transcript_queue.put(self.recording_file)
|
66 |
+
|
67 |
+
except Exception as e:
|
68 |
+
print(f"Audio capture error: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
+
def _transcription_thread(self):
|
71 |
+
"""Transcribe audio files as they become available"""
|
72 |
while self.running:
|
73 |
try:
|
74 |
+
audio_file = self.transcript_queue.get(timeout=1.0)
|
75 |
+
if audio_file:
|
76 |
+
# Transcribe the audio file
|
77 |
+
with sr.AudioFile(audio_file) as source:
|
78 |
+
audio = self.recognizer.record(source)
|
79 |
+
try:
|
80 |
+
text = self.recognizer.recognize_google(audio)
|
81 |
+
self.transcript_history.append(text)
|
82 |
+
self.analyzer.process_chunk(text)
|
83 |
+
|
84 |
+
# Generate interim summary
|
85 |
+
if len(self.transcript_history) % 3 == 0:
|
86 |
+
self.summary = self.analyzer.generate_summary()
|
87 |
+
self.action_items = self.analyzer.extract_action_items()
|
88 |
+
|
89 |
+
# Check for urgent items
|
90 |
+
urgent_items = self.analyzer.detect_urgent_action_items()
|
91 |
+
if urgent_items:
|
92 |
+
self.urgent_alerts.extend(urgent_items)
|
93 |
+
self.notifier.send_urgent_alert(urgent_items)
|
94 |
+
|
95 |
+
except sr.UnknownValueError:
|
96 |
+
print("Speech recognition could not understand audio")
|
97 |
+
except sr.RequestError as e:
|
98 |
+
print(f"Speech recognition error: {str(e)}")
|
99 |
+
|
100 |
except queue.Empty:
|
101 |
continue
|
102 |
|
|
|
106 |
|
107 |
self.running = False
|
108 |
|
|
|
|
|
|
|
109 |
# Generate final analysis
|
110 |
self.summary = self.analyzer.generate_summary()
|
111 |
self.action_items = self.analyzer.extract_action_items()
|
|
|
119 |
recipients=config.NOTIFICATION_RECIPIENTS
|
120 |
)
|
121 |
|
|
|
|
|
|
|
122 |
return "Meeting processing stopped! Report sent. ✅"
|
123 |
|
124 |
def get_current_status(self):
|
|
|
135 |
elapsed = time.time() - self.start_time
|
136 |
mins, secs = divmod(int(elapsed), 60)
|
137 |
|
138 |
+
# Only show last 3 transcript entries
|
139 |
+
recent_transcript = "\n".join(self.transcript_history[-3:])
|
140 |
|
141 |
return {
|
142 |
"status": "Recording",
|
143 |
"duration": f"{mins:02d}:{secs:02d}",
|
144 |
"transcript": recent_transcript,
|
145 |
+
"summary": self.summary if self.summary else "Summary will appear during meeting",
|
146 |
"action_items": self.action_items,
|
147 |
"alerts": self.urgent_alerts
|
148 |
}
|
|
|
151 |
processor = MeetingProcessor()
|
152 |
|
153 |
# Create Gradio interface
|
154 |
+
with gr.Blocks(title="Meeting Notes Generator", theme="soft") as app:
|
155 |
+
gr.Markdown("# 📝 Meeting Notes Generator")
|
156 |
+
gr.Markdown("Start this during any meeting to automatically capture notes and action items")
|
157 |
|
158 |
with gr.Row():
|
159 |
start_btn = gr.Button("Start Meeting", variant="primary")
|
160 |
stop_btn = gr.Button("Stop Meeting", variant="stop")
|
161 |
+
status_text = gr.Textbox(label="Status", interactive=False, value="Ready to start meeting")
|
|
|
162 |
|
163 |
with gr.Row():
|
164 |
with gr.Column():
|
165 |
duration_display = gr.Textbox(label="Duration", interactive=False)
|
166 |
+
transcript_box = gr.Textbox(label="Live Transcript", lines=6, interactive=False)
|
167 |
|
168 |
with gr.Column():
|
169 |
+
summary_box = gr.Textbox(label="Meeting Summary", lines=6, interactive=False)
|
170 |
+
|
171 |
+
with gr.Row():
|
172 |
+
action_items_box = gr.Textbox(label="Action Items", lines=4, interactive=False)
|
173 |
+
alerts_box = gr.Textbox(label="Urgent Alerts", lines=4, interactive=False)
|
174 |
|
175 |
# Update function for components
|
176 |
def update_components():
|
|
|
187 |
for item in current_status["alerts"]
|
188 |
) if current_status["alerts"] else "No urgent alerts",
|
189 |
current_status["summary"],
|
190 |
+
current_status["status"]
|
191 |
]
|
192 |
|
193 |
# Button actions
|
|
|
195 |
fn=processor.start_processing,
|
196 |
inputs=[],
|
197 |
outputs=[status_text]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
)
|
199 |
|
200 |
stop_btn.click(
|
201 |
fn=processor.stop_processing,
|
202 |
inputs=[],
|
203 |
outputs=[status_text]
|
204 |
+
)
|
205 |
+
|
206 |
+
# Manual refresh button
|
207 |
+
refresh_btn = gr.Button("Refresh View", variant="secondary")
|
208 |
+
refresh_btn.click(
|
209 |
update_components,
|
210 |
inputs=[],
|
211 |
outputs=[
|
|
|
218 |
]
|
219 |
)
|
220 |
|
221 |
+
# Instructions
|
222 |
+
gr.Markdown("### How to use:")
|
223 |
+
gr.Markdown("1. Click **Start Meeting** when your meeting begins")
|
224 |
+
gr.Markdown("2. Continue with your meeting as normal")
|
225 |
+
gr.Markdown("3. Click **Stop Meeting** when finished")
|
226 |
+
gr.Markdown("4. View your meeting summary and action items below")
|
227 |
+
gr.Markdown("5. Click **Refresh View** to update the display")
|
228 |
+
|
229 |
+
# Initialize with current status
|
230 |
+
app.load(
|
231 |
update_components,
|
232 |
inputs=[],
|
233 |
outputs=[
|
|
|
240 |
]
|
241 |
)
|
242 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
243 |
if __name__ == "__main__":
|
244 |
app.launch()
|