|
|
|
import gradio as gr |
|
import threading |
|
import queue |
|
import time |
|
import wave |
|
import os |
|
from datetime import datetime |
|
from analyzer import MeetingAnalyzer |
|
from integrations import Notifier |
|
import config |
|
import speech_recognition as sr |
|
|
|
class MeetingProcessor: |
|
def __init__(self): |
|
self.analyzer = MeetingAnalyzer() |
|
self.notifier = Notifier() |
|
self.running = False |
|
self.start_time = None |
|
self.transcript_history = [] |
|
self.summary = "" |
|
self.action_items = [] |
|
self.urgent_alerts = [] |
|
self.transcript_queue = queue.Queue() |
|
self.recording_file = None |
|
self.recognizer = sr.Recognizer() |
|
|
|
def start_processing(self): |
|
if self.running: |
|
return "Already running!" |
|
|
|
self.running = True |
|
self.start_time = time.time() |
|
self.transcript_history = [] |
|
self.summary = "" |
|
self.action_items = [] |
|
self.urgent_alerts = [] |
|
|
|
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
self.recording_file = f"meeting_{timestamp}.wav" |
|
|
|
|
|
threading.Thread(target=self._audio_capture_thread, daemon=True).start() |
|
threading.Thread(target=self._transcription_thread, daemon=True).start() |
|
|
|
return "Meeting processing started! 🎤" |
|
|
|
def _audio_capture_thread(self): |
|
"""Capture audio from microphone and save to file""" |
|
try: |
|
with sr.Microphone() as source: |
|
print("Adjusting for ambient noise...") |
|
self.recognizer.adjust_for_ambient_noise(source, duration=1) |
|
print("Microphone ready! Recording meeting...") |
|
|
|
|
|
audio = self.recognizer.listen(source, timeout=None, phrase_time_limit=300) |
|
|
|
|
|
with open(self.recording_file, "wb") as f: |
|
f.write(audio.get_wav_data()) |
|
|
|
|
|
self.transcript_queue.put(self.recording_file) |
|
|
|
except Exception as e: |
|
print(f"Audio capture error: {str(e)}") |
|
|
|
def _transcription_thread(self): |
|
"""Transcribe audio files as they become available""" |
|
while self.running: |
|
try: |
|
audio_file = self.transcript_queue.get(timeout=1.0) |
|
if audio_file: |
|
|
|
with sr.AudioFile(audio_file) as source: |
|
audio = self.recognizer.record(source) |
|
try: |
|
text = self.recognizer.recognize_google(audio) |
|
self.transcript_history.append(text) |
|
self.analyzer.process_chunk(text) |
|
|
|
|
|
if len(self.transcript_history) % 3 == 0: |
|
self.summary = self.analyzer.generate_summary() |
|
self.action_items = self.analyzer.extract_action_items() |
|
|
|
|
|
urgent_items = self.analyzer.detect_urgent_action_items() |
|
if urgent_items: |
|
self.urgent_alerts.extend(urgent_items) |
|
self.notifier.send_urgent_alert(urgent_items) |
|
|
|
except sr.UnknownValueError: |
|
print("Speech recognition could not understand audio") |
|
except sr.RequestError as e: |
|
print(f"Speech recognition error: {str(e)}") |
|
|
|
except queue.Empty: |
|
continue |
|
|
|
def stop_processing(self): |
|
if not self.running: |
|
return "Not running!" |
|
|
|
self.running = False |
|
|
|
|
|
self.summary = self.analyzer.generate_summary() |
|
self.action_items = self.analyzer.extract_action_items() |
|
|
|
|
|
self.notifier.send_comprehensive_report( |
|
summary=self.summary, |
|
action_items=self.action_items, |
|
decisions=self.analyzer.extract_decisions(), |
|
transcript="\n".join(self.transcript_history), |
|
recipients=config.NOTIFICATION_RECIPIENTS |
|
) |
|
|
|
return "Meeting processing stopped! Report sent. ✅" |
|
|
|
def get_current_status(self): |
|
if not self.running: |
|
return { |
|
"status": "Stopped", |
|
"duration": "00:00", |
|
"transcript": "", |
|
"summary": self.summary, |
|
"action_items": self.action_items, |
|
"alerts": self.urgent_alerts |
|
} |
|
|
|
elapsed = time.time() - self.start_time |
|
mins, secs = divmod(int(elapsed), 60) |
|
|
|
|
|
recent_transcript = "\n".join(self.transcript_history[-3:]) |
|
|
|
return { |
|
"status": "Recording", |
|
"duration": f"{mins:02d}:{secs:02d}", |
|
"transcript": recent_transcript, |
|
"summary": self.summary if self.summary else "Summary will appear during meeting", |
|
"action_items": self.action_items, |
|
"alerts": self.urgent_alerts |
|
} |
|
|
|
|
|
processor = MeetingProcessor() |
|
|
|
|
|
with gr.Blocks(title="Meeting Notes Generator", theme="soft") as app: |
|
gr.Markdown("# 📝 Meeting Notes Generator") |
|
gr.Markdown("Start this during any meeting to automatically capture notes and action items") |
|
|
|
with gr.Row(): |
|
start_btn = gr.Button("Start Meeting", variant="primary") |
|
stop_btn = gr.Button("Stop Meeting", variant="stop") |
|
status_text = gr.Textbox(label="Status", interactive=False, value="Ready to start meeting") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
duration_display = gr.Textbox(label="Duration", interactive=False) |
|
transcript_box = gr.Textbox(label="Live Transcript", lines=6, interactive=False) |
|
|
|
with gr.Column(): |
|
summary_box = gr.Textbox(label="Meeting Summary", lines=6, interactive=False) |
|
|
|
with gr.Row(): |
|
action_items_box = gr.Textbox(label="Action Items", lines=4, interactive=False) |
|
alerts_box = gr.Textbox(label="Urgent Alerts", lines=4, interactive=False) |
|
|
|
|
|
def update_components(): |
|
current_status = processor.get_current_status() |
|
return [ |
|
current_status["duration"], |
|
current_status["transcript"], |
|
"\n".join( |
|
f"• {item['task']} (Owner: {item['owner']}, Deadline: {item['deadline']})" |
|
for item in current_status["action_items"] |
|
), |
|
"\n".join( |
|
f"🚨 {item['task']} (Owner: {item['owner']}, Deadline: {item['deadline']})" |
|
for item in current_status["alerts"] |
|
) if current_status["alerts"] else "No urgent alerts", |
|
current_status["summary"], |
|
current_status["status"] |
|
] |
|
|
|
|
|
start_btn.click( |
|
fn=processor.start_processing, |
|
inputs=[], |
|
outputs=[status_text] |
|
) |
|
|
|
stop_btn.click( |
|
fn=processor.stop_processing, |
|
inputs=[], |
|
outputs=[status_text] |
|
) |
|
|
|
|
|
refresh_btn = gr.Button("Refresh View", variant="secondary") |
|
refresh_btn.click( |
|
update_components, |
|
inputs=[], |
|
outputs=[ |
|
duration_display, |
|
transcript_box, |
|
action_items_box, |
|
alerts_box, |
|
summary_box, |
|
status_text |
|
] |
|
) |
|
|
|
|
|
gr.Markdown("### How to use:") |
|
gr.Markdown("1. Click **Start Meeting** when your meeting begins") |
|
gr.Markdown("2. Continue with your meeting as normal") |
|
gr.Markdown("3. Click **Stop Meeting** when finished") |
|
gr.Markdown("4. View your meeting summary and action items below") |
|
gr.Markdown("5. Click **Refresh View** to update the display") |
|
|
|
|
|
app.load( |
|
update_components, |
|
inputs=[], |
|
outputs=[ |
|
duration_display, |
|
transcript_box, |
|
action_items_box, |
|
alerts_box, |
|
summary_box, |
|
status_text |
|
] |
|
) |
|
|
|
if __name__ == "__main__": |
|
app.launch() |