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"""
""", 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("""
""".format(logo_base64), unsafe_allow_html=True)
else:
# Show placeholder if logo not found - bigger size, no text
st.markdown("""
""", 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:
- 🎯 Transcription - Convert speech to text with precise timestamps
- 👥 Speaker Detection - Automatically identify different speakers
- 😊 Sentiment Analysis - Analyze emotional tone and context
- 📋 Summarization - Extract key points and insights
- 🏷️ Topic Detection - Identify main topics and themes
- 💬 Q&A Chat - Ask intelligent questions about your audio
""", 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()