import assemblyai as aai import streamlit as st import uuid import gc import base64 from pathlib import Path import os from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() # Set API key from environment variable aai.settings.api_key = os.getenv("ASSEMBLYAI_API_KEY") # Configure page st.set_page_config( page_title="AssemblyAI Audio Analysis", page_icon="🎵", layout="wide", initial_sidebar_state="expanded" ) # Initialize session state if "id" not in st.session_state: st.session_state.id = uuid.uuid4() st.session_state.file_cache = {} # Application styling with dark theme st.markdown(""" """, unsafe_allow_html=True) def get_logo_base64(): """Convert logo file to base64 string for embedding""" logo_path = Path("audio-analysis-toolkit/assets/logo.png") if logo_path.exists(): try: with open(logo_path, "rb") as img_file: return base64.b64encode(img_file.read()).decode() except Exception: return "" return "" def reset_chat(): """Reset chat session""" st.session_state.messages = [] gc.collect() def timestamp_string(milliseconds): """Convert milliseconds to HH:MM:SS format""" seconds = milliseconds // 1000 minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) return f"{hours:02}:{minutes:02}:{seconds:02}" def display_transcription(transcript): """Display transcription with timestamps""" st.markdown('
', unsafe_allow_html=True) st.subheader("📝 Full Transcription") sentences = transcript.get_sentences() for sentence in sentences: col1, col2 = st.columns([0.8, 7]) with col1: # Compact timestamp styling st.markdown(f"""
{timestamp_string(sentence.start)}
""", unsafe_allow_html=True) with col2: st.markdown(f'
{sentence.text}
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) def display_summary(transcript): """Display summary""" st.markdown('
', unsafe_allow_html=True) st.subheader("📋 Summary") st.write(transcript.summary) st.markdown('
', unsafe_allow_html=True) def display_speakers(transcript): """Display speaker analysis""" st.markdown('
', unsafe_allow_html=True) st.subheader("\U0001F465 Speaker Analysis") # Count speakers speakers = set() for utterance in transcript.utterances: speakers.add(str(utterance.speaker)) total_speakers = len(speakers) total_utterances = len(transcript.utterances) # Simple metrics row col1, col2 = st.columns(2) with col1: st.markdown(f"""
Total Speakers
{total_speakers}
""", unsafe_allow_html=True) with col2: st.markdown(f"""
Total Utterances
{total_utterances}
""", unsafe_allow_html=True) st.subheader("Speaker Dialogue") for utterance in transcript.utterances: col1, col2 = st.columns([1, 5]) with col1: st.markdown(f'Speaker {utterance.speaker}', unsafe_allow_html=True) with col2: st.write(utterance.text) st.markdown('
', unsafe_allow_html=True) def display_sentiment(transcript): """Display sentiment analysis""" st.markdown('
', unsafe_allow_html=True) st.subheader("\U0001F60A Sentiment Analysis") # Count sentiments sentiment_counts = {"POSITIVE": 0, "NEUTRAL": 0, "NEGATIVE": 0} for sent in transcript.sentiment_analysis: sentiment = str(sent.sentiment).upper() if "POSITIVE" in sentiment: sentiment_counts["POSITIVE"] += 1 elif "NEGATIVE" in sentiment: sentiment_counts["NEGATIVE"] += 1 elif "NEUTRAL" in sentiment: sentiment_counts["NEUTRAL"] += 1 # Simple metrics row col1, col2, col3 = st.columns(3) with col1: st.markdown(f"""
😊 Positive
{sentiment_counts['POSITIVE']}
""", unsafe_allow_html=True) with col2: st.markdown(f"""
😐 Neutral
{sentiment_counts['NEUTRAL']}
""", unsafe_allow_html=True) with col3: st.markdown(f"""
😞 Negative
{sentiment_counts['NEGATIVE']}
""", unsafe_allow_html=True) st.subheader("Detailed Sentiment") for sent in transcript.sentiment_analysis: timestamp = timestamp_string(sent.start) text = f"**{timestamp}** - Speaker {sent.speaker}: {sent.text}" if "NEUTRAL" in str(sent.sentiment).upper(): st.info(text) elif "POSITIVE" in str(sent.sentiment).upper(): st.success(text) else: st.error(text) st.markdown('
', unsafe_allow_html=True) def display_topics(transcript): """Display topic analysis""" st.markdown('
', unsafe_allow_html=True) st.subheader("🏷️ Topic Analysis") sorted_topics = sorted(transcript.iab_categories.summary.items(), key=lambda x: x[1], reverse=True) if sorted_topics: for topic, relevance in sorted_topics[:10]: percentage = relevance * 100 # Create a clean layout with topic name and percentage st.markdown(f"""
{topic} {percentage:.1f}%
""", unsafe_allow_html=True) else: st.info("Topics analysis not available for this audio.") st.markdown('
', unsafe_allow_html=True) def display_chat(transcript): """Display chat interface""" st.markdown('
', unsafe_allow_html=True) st.subheader("💬 Ask Questions About Your Audio") # Initialize chat history if "messages" not in st.session_state: st.session_state.messages = [] # Display welcome message if no messages if not st.session_state.messages: # Add spacing above welcome message to center it better st.markdown("

", unsafe_allow_html=True) st.markdown("""

Start a conversation about your audio

Ask questions about the content, speakers, sentiment, or get insights from your audio analysis.

""", unsafe_allow_html=True) # Display chat messages from history for message in st.session_state.messages: with st.chat_message(message["role"]): st.markdown(message["content"]) # # Add spacing to position chat input (moved up by input's height) # st.markdown("



", unsafe_allow_html=True) # Chat input - appears naturally at bottom, always visible if prompt := st.chat_input("What would you like to know about this audio?"): # Add user message to chat history st.session_state.messages.append({"role": "user", "content": prompt}) # Display user message with st.chat_message("user"): st.markdown(prompt) # Display assistant response with st.chat_message("assistant"): with st.spinner("Analyzing..."): full_prompt = f"Based on the transcript, answer the following question: {prompt}" result = st.session_state.transcript.lemur.task(full_prompt, final_model=aai.LemurModel.claude3_5_sonnet) response = result.response.strip() st.markdown(response) # Add assistant response to chat history st.session_state.messages.append({"role": "assistant", "content": response}) st.markdown('
', unsafe_allow_html=True) def main(): # Sidebar with st.sidebar: # Company branding with logo logo_path = Path("assets/logo.png") if logo_path.exists(): # Convert logo to base64 for better control over positioning with open(logo_path, "rb") as img_file: logo_data = base64.b64encode(img_file.read()).decode() # Show logo with full CSS control for perfect centering st.markdown(f"""
Logo
""", unsafe_allow_html=True) # Add separator st.markdown('
', unsafe_allow_html=True) logo_found = True else: logo_found = False if not logo_found: # Fallback to base64 method with debug info logo_base64 = get_logo_base64() if logo_base64: # Show actual logo only, no text, bigger size st.markdown("""
Logo
""".format(logo_base64), unsafe_allow_html=True) else: # Show placeholder if logo not found - bigger size, no text st.markdown("""
A
""", unsafe_allow_html=True) # Add separator after logo/fallback st.markdown('
', unsafe_allow_html=True) st.markdown('', unsafe_allow_html=True) audio_file = st.file_uploader( "Choose an audio file", type=['wav', 'mp3', 'mp4', 'm4a', 'flac'], help="Upload audio files in WAV, MP3, MP4, M4A, or FLAC format" ) if audio_file is not None: st.success("File uploaded successfully!") st.audio(audio_file) # Add spacing between upload and file details st.markdown("
", unsafe_allow_html=True) # File details st.markdown("### File Details") st.write(f"**Filename:** {audio_file.name}") st.write(f"**Size:** {audio_file.size:,} bytes") # Main content area - simple title st.markdown('

🎵 Audio Analysis Toolkit

', unsafe_allow_html=True) if audio_file is None: # Welcome screen - matching original layout st.markdown('
', unsafe_allow_html=True) st.markdown("""

🎵 Welcome to Audio Analysis Toolkit

Upload an audio file to get started with powerful AI-driven analysis:

""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) # Add feature cards at the bottom st.markdown("""

🎯 Accurate Transcription

High-quality speech-to-text with precise timestamps and speaker detection

😊 Sentiment Analysis

Understand emotional tone and context of conversations

🏷️ Topic Detection

Identify key themes and topics discussed in your audio

""", unsafe_allow_html=True) else: # Process audio and show results with st.spinner('🔄 Processing your audio with AssemblyAI...'): config = aai.TranscriptionConfig( speaker_labels=True, iab_categories=True, speakers_expected=2, sentiment_analysis=True, summarization=True, language_detection=True ) st.session_state.transcriber = aai.Transcriber() st.session_state.transcript = st.session_state.transcriber.transcribe(audio_file, config=config) st.success('✅ Audio processed successfully!') # Create tabs for different sections tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([ "📝 Transcription", "📋 Summary", "👥 Speakers", "😊 Sentiment", "🏷️ Topics", "💬 Chat" ]) with tab1: display_transcription(st.session_state.transcript) with tab2: display_summary(st.session_state.transcript) with tab3: display_speakers(st.session_state.transcript) with tab4: display_sentiment(st.session_state.transcript) with tab5: display_topics(st.session_state.transcript) with tab6: display_chat(st.session_state.transcript) if __name__ == "__main__": main()