""" Meeting Notes Generator Automatically generates meeting notes from audio files with speaker identification, summaries, and action items. """ import streamlit as st import tempfile import os import gc from dotenv import load_dotenv # Load environment variables load_dotenv() from src.services.audio_processor import MeetingProcessor from src.ui.ui_components import ( display_meeting_header, display_meeting_summary, display_speaker_analysis, display_action_items, display_meeting_transcript ) def main(): """Main application function.""" st.set_page_config( page_title="Multilingual Meeting Notes Generator", layout="wide" ) # Initialize session state variables if "current_result" not in st.session_state: st.session_state.current_result = None # Sidebar with st.sidebar: # Configuration st.header("🔧 Configuration") # API key input fields assemblyai_key = st.text_input( "AssemblyAI API Key", type="password", help="Enter your AssemblyAI API key for audio transcription" ) openai_key = st.text_input( "OpenAI API Key", type="password", help="Enter your OpenAI API key for text analysis" ) # Check if API keys are provided api_keys_configured = assemblyai_key and openai_key if api_keys_configured: st.success("✅ API Keys configured!") else: st.warning("⚠️ Please add your API keys to get started") st.markdown("---") # Audio input options st.header("🎤 Audio Input") # File upload uploaded_file = st.file_uploader( "Choose your audio file", type=['mp3', 'wav', 'm4a', 'mp4', 'webm', 'flac'], help="Upload an audio file to generate meeting notes" ) # Process button with enhanced validation if uploaded_file and api_keys_configured: if st.button("🚀 Start Processing", type="primary", use_container_width=True): # Validate API keys format if not _validate_api_keys(assemblyai_key, openai_key): st.error("⚠️ Invalid API key format. Please check your keys.") return result = _process_uploaded_file(uploaded_file, { "assemblyai_key": assemblyai_key, "openai_key": openai_key }) if result: st.session_state.current_result = result st.rerun() elif uploaded_file and not api_keys_configured: st.error("⚠️ Please add your API keys first") # Add reset button st.markdown("---") if st.button("🗑️ Reset", help="Clear results and reset session"): _cleanup_session() st.success("Session cleared!") st.rerun() # Main interface _display_main_interface() def _process_uploaded_file(uploaded_file, config: dict): """Process the uploaded audio file with enhanced logging and error handling.""" try: temp_path = _save_uploaded_file(uploaded_file) processor = MeetingProcessor( assemblyai_api_key=config["assemblyai_key"], openai_api_key=config["openai_key"] ) # Show processing status status_container = st.empty() status_container.info("🔄 Processing audio...") result = processor.process_meeting_audio( audio_file_path=temp_path ) _cleanup_temp_file(temp_path) status_container.success("✅ Processing complete!") return result except Exception as e: error_msg = f"❌ Processing failed: {str(e)}" st.error(error_msg) return None def _save_uploaded_file(uploaded_file) -> str: """Save uploaded file to temp location.""" file_extension = uploaded_file.name.split('.')[-1] with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file_extension}") as temp_file: temp_file.write(uploaded_file.getvalue()) return temp_file.name def _cleanup_temp_file(temp_path: str) -> None: """Delete temporary file safely and perform garbage collection.""" try: os.unlink(temp_path) # Perform garbage collection gc.collect() except OSError as e: print(f"Warning: Could not delete temporary file {temp_path}: {e}") def _validate_api_keys(assemblyai_key: str, openai_key: str) -> bool: """Validate API key formats.""" # Basic validation - keys should be non-empty and have reasonable length if not assemblyai_key or len(assemblyai_key) < 20: return False if not openai_key or len(openai_key) < 20: return False return True def _cleanup_session() -> None: """Clean up session state and perform garbage collection.""" # Clear current result st.session_state.current_result = None # Perform garbage collection gc.collect() def _display_main_interface(): """Display the main interface.""" # Header with branding - center aligned st.markdown('''
''', unsafe_allow_html=True) # System status - center aligned if st.session_state.current_result: st.success("🟢 Meeting processed successfully!") else: st.info("🔵 Upload an audio file to get started") # Display results if available if st.session_state.current_result: result = st.session_state.current_result # Display main results display_meeting_header(result) display_meeting_summary(result.summary) display_speaker_analysis(result.speakers, result.segments) display_action_items(result.action_items) # Always show transcript display_meeting_transcript(result.segments, result.language, result) # Export options _display_export_options(result) # Footer - center aligned st.markdown("---") st.markdown( "" "Multilingual Meeting Notes Generator • Powered by AssemblyAI" "
" "