Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import time | |
| import os | |
| from pathlib import Path | |
| import tempfile | |
| # Import your existing modules | |
| try: | |
| from audio_extractor import prepare_audio | |
| from dialect_predector import analyze_video_accent | |
| except ImportError as e: | |
| st.error(f"Error importing modules: {e}") | |
| st.stop() | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="π€ English Accent Analyzer", | |
| page_icon="π€", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Custom CSS for better styling | |
| st.markdown(""" | |
| <style> | |
| .main-header { | |
| text-align: center; | |
| color: #1f77b4; | |
| font-size: 3rem; | |
| font-weight: bold; | |
| margin-bottom: 2rem; | |
| } | |
| .success-box { | |
| background-color: #d4edda; | |
| border: 1px solid #c3e6cb; | |
| color: #155724; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| margin: 1rem 0; | |
| } | |
| .error-box { | |
| background-color: #f8d7da; | |
| border: 1px solid #f5c6cb; | |
| color: #721c24; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| margin: 1rem 0; | |
| } | |
| .info-box { | |
| background-color: #d1ecf1; | |
| border: 1px solid #bee5eb; | |
| color: #0c5460; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| margin: 1rem 0; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| def initialize_session_state(): | |
| """Initialize session state variables""" | |
| if 'analysis_results' not in st.session_state: | |
| st.session_state.analysis_results = None | |
| if 'processing' not in st.session_state: | |
| st.session_state.processing = False | |
| def save_uploaded_file(uploaded_file): | |
| """Save uploaded file to temporary directory""" | |
| try: | |
| temp_dir = tempfile.mkdtemp() | |
| file_path = os.path.join(temp_dir, uploaded_file.name) | |
| with open(file_path, "wb") as f: | |
| f.write(uploaded_file.getbuffer()) | |
| return file_path | |
| except Exception as e: | |
| st.error(f"Error saving uploaded file: {e}") | |
| return None | |
| def create_confidence_chart(chunk_results): | |
| """Create confidence score chart for 1-minute chunks""" | |
| if not chunk_results: | |
| return None | |
| chunk_data = [] | |
| for i, result in enumerate(chunk_results): | |
| chunk_data.append({ | |
| 'Minute': f"Min {i+1}", | |
| 'Confidence': result['confidence'], | |
| 'Accent': result['accent'], | |
| 'Is Confident': 'β High Confidence' if result['is_confident'] else 'β Low Confidence' | |
| }) | |
| df = pd.DataFrame(chunk_data) | |
| fig = px.bar(df, | |
| x='Minute', | |
| y='Confidence', | |
| color='Is Confident', | |
| hover_data=['Accent'], | |
| title='Confidence Scores by Minute', | |
| color_discrete_map={'β High Confidence': '#28a745', 'β Low Confidence': '#dc3545'}) | |
| fig.update_layout( | |
| xaxis_title="Time Segment", | |
| yaxis_title="Confidence Score", | |
| showlegend=True, | |
| height=400 | |
| ) | |
| return fig | |
| def create_accent_distribution_chart(accent_counts, title="Accent Distribution"): | |
| """Create pie chart for accent distribution""" | |
| if not accent_counts: | |
| return None | |
| accents = list(accent_counts.keys()) | |
| counts = list(accent_counts.values()) | |
| fig = px.pie(values=counts, | |
| names=accents, | |
| title=title, | |
| color_discrete_sequence=px.colors.qualitative.Set3) | |
| fig.update_traces(textposition='inside', textinfo='percent+label') | |
| fig.update_layout(height=400) | |
| return fig | |
| def display_results(results): | |
| """Display analysis results with charts and metrics""" | |
| if not results['success']: | |
| st.markdown(f'<div class="error-box">β <strong>Error:</strong> {results["error"]}</div>', | |
| unsafe_allow_html=True) | |
| return | |
| # Main result | |
| st.markdown(f'<div class="success-box">π€ <strong>Detected Accent:</strong> {results["predicted_accent"]}</div>', | |
| unsafe_allow_html=True) | |
| # Key metrics | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.metric( | |
| label="π― Overall Confidence", | |
| value=f"{results['confidence_score']:.1%}", | |
| help="Overall confidence in the prediction" | |
| ) | |
| with col2: | |
| st.metric( | |
| label="π Minutes Analyzed", | |
| value=f"{results['processed_chunks_count']} min", | |
| delta=f"of {results.get('duration_minutes', 0):.1f} min total" | |
| ) | |
| with col3: | |
| st.metric( | |
| label="β High Confidence Segments", | |
| value=results['confident_chunks_count'], | |
| delta=f"{(results['confident_chunks_count']/results['processed_chunks_count']*100):.0f}%" if results['processed_chunks_count'] > 0 else "0%" | |
| ) | |
| with col4: | |
| st.metric( | |
| label="β±οΈ Processing Time", | |
| value=f"{results['processing_time']:.1f}s", | |
| help="Time taken to analyze the audio" | |
| ) | |
| # Detailed Analysis | |
| st.subheader("π Detailed Analysis") | |
| # Create two columns for charts | |
| chart_col1, chart_col2 = st.columns(2) | |
| # Confidence chart | |
| with chart_col1: | |
| confidence_chart = create_confidence_chart(results['chunk_results']) | |
| if confidence_chart: | |
| st.plotly_chart(confidence_chart, use_container_width=True) | |
| # Accent distribution for confident predictions | |
| with chart_col2: | |
| confident_chart = create_accent_distribution_chart( | |
| results['confident_accent_counts'], | |
| "High Confidence Predictions" | |
| ) | |
| if confident_chart: | |
| st.plotly_chart(confident_chart, use_container_width=True) | |
| # Detailed results table | |
| with st.expander("π View Minute-by-Minute Results"): | |
| if results['chunk_results']: | |
| chunk_df = pd.DataFrame(results['chunk_results']) | |
| chunk_df.index = [f"Minute {i+1}" for i in range(len(chunk_df))] | |
| st.dataframe(chunk_df, use_container_width=True) | |
| # Summary statistics | |
| with st.expander("π Summary Statistics"): | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.write("**High Confidence Predictions:**") | |
| if results['confident_accent_counts']: | |
| for accent, count in results['confident_accent_counts'].items(): | |
| percentage = (count / results['confident_chunks_count']) * 100 | |
| st.write(f"β’ {accent}: {count} segments ({percentage:.1f}%)") | |
| else: | |
| st.write("No high confidence predictions") | |
| with col2: | |
| st.write("**All Predictions:**") | |
| if results['all_accent_counts']: | |
| for accent, count in results['all_accent_counts'].items(): | |
| percentage = (count / results['processed_chunks_count']) * 100 | |
| st.write(f"β’ {accent}: {count} segments ({percentage:.1f}%)") | |
| def main(): | |
| """Main Streamlit application""" | |
| initialize_session_state() | |
| # Header | |
| st.markdown('<h1 class="main-header">π€ English Accent Analyzer</h1>', unsafe_allow_html=True) | |
| st.markdown("Analyze English accents from video files, Loom videos, or direct media URLs. Audio is processed in 1-minute segments for detailed analysis.") | |
| # Sidebar configuration | |
| st.sidebar.header("βοΈ Configuration") | |
| confidence_threshold = st.sidebar.slider( | |
| "Confidence Threshold", | |
| min_value=0.1, | |
| max_value=0.9, | |
| value=0.6, | |
| step=0.05, | |
| help="Only predictions above this threshold are considered high confidence" | |
| ) | |
| # Input section | |
| st.header("π₯ Input Source") | |
| input_method = st.radio( | |
| "Choose input method:", | |
| ["URL (Loom or Direct Link)", "Upload File"], | |
| horizontal=True | |
| ) | |
| source = None | |
| if input_method == "URL (Loom or Direct Link)": | |
| source = st.text_input( | |
| "Enter video URL:", | |
| placeholder="https://www.loom.com/share/...", | |
| help="Supports Loom videos and direct media URLs" | |
| ) | |
| # URL examples | |
| with st.expander("π Supported URL Examples"): | |
| st.write("β’ **Loom:** `https://www.loom.com/share/VIDEO_ID`") | |
| st.write("β’ **Direct MP4:** `https://example.com/video.mp4`") | |
| st.write("β’ **Direct audio:** `https://example.com/audio.mp3`") | |
| st.markdown('<div class="info-box">π <strong>Note:</strong> YouTube URLs are not supported to avoid authentication issues in deployment.</div>', unsafe_allow_html=True) | |
| else: # Upload File | |
| uploaded_file = st.file_uploader( | |
| "Choose a video or audio file", | |
| type=['mp4', 'webm', 'avi', 'mov', 'mkv', 'm4v', 'mp3', 'wav', 'm4a', 'aac', 'ogg', 'flac'], | |
| help="Upload video or audio files for accent analysis" | |
| ) | |
| if uploaded_file is not None: | |
| # Save uploaded file | |
| with st.spinner("Saving uploaded file..."): | |
| source = save_uploaded_file(uploaded_file) | |
| if source: | |
| st.success(f"β File uploaded: {uploaded_file.name}") | |
| file_size = len(uploaded_file.getbuffer()) / 1024 / 1024 | |
| st.info(f"π File size: {file_size:.1f}MB") | |
| else: | |
| st.error("β Failed to save uploaded file") | |
| # Analysis button | |
| analyze_button = st.button( | |
| "π Start Accent Analysis", | |
| type="primary", | |
| disabled=not source or st.session_state.processing, | |
| use_container_width=True | |
| ) | |
| # Process analysis | |
| if analyze_button and source: | |
| st.session_state.processing = True | |
| # Progress tracking | |
| progress_bar = st.progress(0) | |
| status_text = st.empty() | |
| try: | |
| status_text.text("π΅ Extracting audio...") | |
| progress_bar.progress(25) | |
| status_text.text("π§© Creating 1-minute segments...") | |
| progress_bar.progress(50) | |
| status_text.text("π§ Analyzing accent patterns...") | |
| progress_bar.progress(75) | |
| # Run analysis with the confidence threshold | |
| results = analyze_video_accent(source, confidence_threshold=confidence_threshold) | |
| progress_bar.progress(100) | |
| status_text.text("β Analysis complete!") | |
| # Store results in session state | |
| st.session_state.analysis_results = results | |
| # Clean up progress indicators | |
| time.sleep(1) | |
| progress_bar.empty() | |
| status_text.empty() | |
| except Exception as e: | |
| st.error(f"β Analysis failed: {str(e)}") | |
| progress_bar.empty() | |
| status_text.empty() | |
| finally: | |
| st.session_state.processing = False | |
| # Display results | |
| if st.session_state.analysis_results: | |
| st.header("π Analysis Results") | |
| display_results(st.session_state.analysis_results) | |
| # Information section | |
| with st.expander("βΉοΈ About This Tool"): | |
| st.markdown(""" | |
| **English Accent Analyzer** uses advanced machine learning models to identify English accents from speech. | |
| **Key Features:** | |
| - π― **1-minute segments:** Audio is processed in 1-minute chunks for detailed analysis | |
| - π€ **Accent detection:** Identifies British, American, Australian, and other English accents | |
| - π **Confidence scoring:** Provides reliability scores for each prediction | |
| - π **Multiple sources:** Supports Loom videos, direct URLs, and file uploads | |
| **Supported Formats:** | |
| - **Video:** MP4, WebM, AVI, MOV, MKV, M4V | |
| - **Audio:** MP3, WAV, M4A, AAC, OGG, FLAC | |
| - **URLs:** Loom videos, direct media links | |
| **How it works:** | |
| 1. Audio is extracted from your source | |
| 2. Audio is split into 1-minute segments | |
| 3. Each segment is analyzed for accent characteristics | |
| 4. Results are combined with confidence weighting | |
| 5. Final accent prediction is provided | |
| **Best Results:** | |
| - Use clear speech audio | |
| - Longer videos provide more accurate results | |
| - Multiple speakers may affect accuracy | |
| """) | |
| # Footer | |
| st.markdown("---") | |
| st.markdown("π **Deployment Ready:** Optimized for Hugging Face Spaces deployment") | |
| if __name__ == "__main__": | |
| main() |