CoolFace
Apppublic

Aziz3/agent_decoder

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
streamlit_app.py518 linesDownload Raw Back to root
1import streamlit as st2import requests3import tempfile4import os5import subprocess6import speech_recognition as sr7from pydub import AudioSegment8import re9from typing import Dict, Tuple10import time11 12# Configure Streamlit page13st.set_page_config(14    page_title="English Accent Detector | REM Waste",15    page_icon="๐ŸŽค",16    layout="wide",17    initial_sidebar_state="collapsed"18)19 20# Custom CSS for better styling21st.markdown("""22<style>23    .main > div {24        padding-top: 2rem;25    }26    .stButton > button {27        width: 100%;28        border-radius: 10px;29        border: none;30        background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);31        color: white;32        font-weight: bold;33        padding: 0.75rem;34    }35    .metric-container {36        background: #f0f2f6;37        padding: 1rem;38        border-radius: 10px;39        text-align: center;40    }41</style>42""", unsafe_allow_html=True)43 44class AccentDetector:45    """Streamlined accent detection for English speech analysis"""46    47    def __init__(self):48        self.accent_patterns = {49            'American': {50                'keywords': ['gonna', 'wanna', 'gotta', 'kinda', 'sorta', 'yeah', 'awesome', 'dude'],51                'vocabulary': ['elevator', 'apartment', 'garbage', 'vacation', 'cookie', 'candy', 'mom', 'color'],52                'phrases': ['you know', 'like totally', 'for sure', 'right now']53            },54            'British': {55                'keywords': ['brilliant', 'lovely', 'quite', 'rather', 'chap', 'bloody', 'bloke', 'cheers'],56                'vocabulary': ['lift', 'flat', 'rubbish', 'holiday', 'biscuit', 'queue', 'mum', 'colour'],57                'phrases': ['i say', 'good heavens', 'how do you do', 'spot on']58            },59            'Australian': {60                'keywords': ['mate', 'bloody', 'crikey', 'reckon', 'fair dinkum', 'bonkers', 'ripper'],61                'vocabulary': ['arvo', 'brekkie', 'servo', 'bottle-o', 'mozzie', 'barbie', 'ute'],62                'phrases': ['no worries', 'good on ya', 'she\'ll be right', 'too right']63            },64            'Canadian': {65                'keywords': ['eh', 'about', 'house', 'out', 'sorry', 'hoser', 'beauty'],66                'vocabulary': ['toque', 'hydro', 'washroom', 'parkade', 'chesterfield', 'serviette'],67                'phrases': ['you bet', 'take off', 'give\'r', 'double double']68            },69            'South African': {70                'keywords': ['ag', 'man', 'hey', 'lekker', 'eish', 'shame', 'howzit'],71                'vocabulary': ['robot', 'bakkie', 'boerewors', 'biltong', 'braai', 'veld'],72                'phrases': ['just now', 'now now', 'is it', 'sharp sharp']73            }74        }75    76    @st.cache_data77    def download_video(_self, url: str) -> str:78        """Download video with caching, including Loom/YouTube support and debug output"""79        try:80            headers = {81                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'82            }83            # YouTube support (including Shorts)84            if 'youtube.com' in url or 'youtu.be' in url:85                try:86                    import yt_dlp87                except ImportError:88                    raise Exception("yt-dlp is required for YouTube downloads. Please install with 'pip install yt-dlp'.")89                # Use yt-dlp to download best audio to a temp directory, let yt-dlp pick the filename90                tmpdir = tempfile.mkdtemp()91                ydl_opts = {92                    'format': 'bestaudio[ext=m4a]/bestaudio/best',93                    'outtmpl': f'{tmpdir}/%(id)s.%(ext)s',94                    'quiet': True,95                    'noplaylist': True,96                    'postprocessors': [{97                        'key': 'FFmpegExtractAudio',98                        'preferredcodec': 'wav',99                        'preferredquality': '192',100                    }],101                    'ffmpeg_location': '/opt/homebrew/bin/ffmpeg',102                    'overwrites': True,103                }104                try:105                    with yt_dlp.YoutubeDL(ydl_opts) as ydl:106                        info = ydl.extract_info(url, download=True)107                    # Find the resulting .wav file108                    for f in os.listdir(tmpdir):109                        if f.endswith('.wav'):110                            # Move the file to a permanent temp location so it persists after this function111                            final_temp = tempfile.NamedTemporaryFile(delete=False, suffix='.wav')112                            final_temp.close()113                            with open(os.path.join(tmpdir, f), 'rb') as src, open(final_temp.name, 'wb') as dst:114                                dst.write(src.read())115                            return final_temp.name116                    raise Exception("yt-dlp did not produce a valid audio file. Try another video or update yt-dlp/ffmpeg.")117                except Exception as e:118                    raise Exception(f"yt-dlp failed: {str(e)}. Try updating yt-dlp and ffmpeg.")119            # Loom support (fallback: try to extract video from page HTML)120            if 'loom.com' in url:121                resp = requests.get(url, headers=headers, timeout=30)122                if resp.status_code != 200:123                    raise Exception("Failed to fetch Loom page")124                html = resp.text125                import re126                match = re.search(r'src="([^"]+\.mp4)"', html)127                if not match:128                    match = re.search(r'https://cdn\.loom\.com/sessions/[^"\s]+\.mp4', html)129                if not match:130                    raise Exception("Could not extract Loom video stream URL from page HTML")131                video_url = match.group(1)132                url = video_url133            # Download video (Loom or direct)134            response = requests.get(url, headers=headers, stream=True, timeout=60)135            response.raise_for_status()136            with tempfile.NamedTemporaryFile(delete=False, suffix='.mp4') as temp_file:137                for chunk in response.iter_content(chunk_size=8192):138                    if chunk:139                        temp_file.write(chunk)140                return temp_file.name141        except Exception as e:142            raise Exception(f"Download failed: {str(e)}")143    144    def extract_audio_simple(self, video_path: str) -> str:145        """Robust audio extraction: handles mp3, wav, mp4, etc."""146        try:147            import os148            from pydub import AudioSegment149            ext = os.path.splitext(video_path)[1].lower()150            audio_path = video_path.rsplit('.', 1)[0] + '.wav'151            # If already wav, use pydub directly152            if ext == '.wav':153                audio = AudioSegment.from_wav(video_path)154            else:155                audio = AudioSegment.from_file(video_path)156            audio = audio.set_frame_rate(16000).set_channels(1)157            if len(audio) > 120000:158                audio = audio[:120000]159            audio.export(audio_path, format="wav")160            return audio_path161        except Exception as e:162            raise Exception(f"Audio extraction failed: {str(e)}")163    164    def transcribe_audio(self, audio_path: str) -> str:165        """Transcribe with error handling"""166        try:167            r = sr.Recognizer()168            r.energy_threshold = 300169            r.dynamic_energy_threshold = True170            171            with sr.AudioFile(audio_path) as source:172                r.adjust_for_ambient_noise(source, duration=0.5)173                audio_data = r.record(source)174            175            # Try Google Speech Recognition176            text = r.recognize_google(audio_data, language='en-US')177            return text.lower()178            179        except sr.UnknownValueError:180            raise Exception("Could not understand the audio clearly")181        except sr.RequestError as e:182            raise Exception(f"Speech recognition service error: {str(e)}")183        except Exception as e:184            raise Exception(f"Transcription failed: {str(e)}")185    186    def analyze_patterns(self, text: str) -> Dict[str, float]:187        """Enhanced pattern analysis"""188        scores = {}189        words = text.split()190        word_count = max(len(words), 1)191        192        for accent, patterns in self.accent_patterns.items():193            score = 0.0194            total_matches = 0195            196            # Keywords (high weight)197            for keyword in patterns['keywords']:198                if keyword in text:199                    score += 20.0200                    total_matches += 1201            202            # Vocabulary (medium weight)  203            for vocab in patterns['vocabulary']:204                if vocab in text:205                    score += 15.0206                    total_matches += 1207            208            # Phrases (high weight)209            for phrase in patterns['phrases']:210                if phrase in text:211                    score += 25.0212                    total_matches += 1213            214            # Normalize and add base confidence215            if total_matches > 0:216                score = min(score * (total_matches / word_count) * 50, 95.0)217            else:218                score = self._get_base_score(text, accent)219            220            scores[accent] = round(max(score, 5.0), 1)221        222        return scores223    224    def _get_base_score(self, text: str, accent: str) -> float:225        """Base scoring for general patterns"""226        base_scores = {227            'American': 30.0,228            'British': 20.0, 229            'Australian': 15.0,230            'Canadian': 18.0,231            'South African': 12.0232        }233        234        score = base_scores.get(accent, 15.0)235        236        # Spelling adjustments237        if accent == 'British':238            if any(word in text for word in ['colour', 'favour', 'centre', 'theatre']):239                score += 25.0240        elif accent == 'American':241            if any(word in text for word in ['color', 'favor', 'center', 'theater']):242                score += 25.0243        244        return min(score, 45.0)245    246    def classify_accent(self, scores: Dict[str, float]) -> Tuple[str, float, str]:247        """Classify and explain results"""248        if not scores:249            return "Unknown", 0.0, "No speech detected"250        251        # Get top result252        top_accent = max(scores.items(), key=lambda x: x[1])253        accent, confidence = top_accent254        255        # Generate explanation256        if confidence < 25:257            explanation = "Low confidence - speech patterns are not strongly distinctive"258        elif confidence < 50:259            explanation = f"Moderate confidence in {accent} accent based on some linguistic markers"260        elif confidence < 75:261            explanation = f"Good confidence in {accent} accent with clear characteristic patterns"262        else:263            explanation = f"High confidence in {accent} accent with strong linguistic evidence"264        265        return accent, confidence, explanation266 267# Initialize detector268@st.cache_resource269def get_detector():270    return AccentDetector()271 272def main():273    # Header274    st.title("๐ŸŽค English Accent Detection Tool")275    st.markdown("**AI-powered accent analysis for English speech | Built for REM Waste**")276    277    # Description278    with st.expander("โ„น๏ธ How it works", expanded=False):279        st.markdown("""280        1. **Input**: Paste a public video URL (MP4, Loom, YouTube, etc.)281        2. **Processing**: Extract audio โ†’ Transcribe speech โ†’ Analyze patterns282        3. **Output**: Accent classification + confidence score + explanation283        284        **Supported Accents**: American, British, Australian, Canadian, South African285        """)286    287    # Input section288    st.subheader("๐Ÿ“น Video Input")289 290    # File upload option291    uploaded_file = st.file_uploader(292        "Or upload a local video/audio file (MP4, WAV, MP3, etc.):",293        type=["mp4", "mov", "avi", "wav", "mp3", "m4a", "aac", "ogg"],294        help="Upload a file directly if you can't use a public URL."295    )296 297    # Sample URLs for testing298    with st.expander("๐Ÿงช Test with sample videos"):299        st.markdown("""300        **Sample URLs for testing:**301        - `https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4`302        - `https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4`303        - Or any public Loom/YouTube video URL304        """)305    306    video_url = st.text_input(307        "Enter video URL:",308        placeholder="https://example.com/video.mp4",309        help="Must be a publicly accessible video URL"310    )311 312    # Process button313    if st.button("๐Ÿš€ Analyze Accent", type="primary"):314        if not video_url.strip() and not uploaded_file:315            st.error("โš ๏ธ Please enter a video URL or upload a file")316            return317        if video_url and not video_url.startswith(('http://', 'https://')):318            st.error("โš ๏ธ Please enter a valid URL starting with http:// or https://")319            return320        detector = get_detector()321        temp_files = []322        try:323            progress_bar = st.progress(0)324            status_text = st.empty()325            if uploaded_file:326                # Save uploaded file to a temp file327                suffix = os.path.splitext(uploaded_file.name)[1]328                with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as f:329                    f.write(uploaded_file.read())330                    video_path = f.name331                temp_files.append(video_path)332            else:333                status_text.text("๐Ÿ“ฅ Downloading video...")334                progress_bar.progress(20)335                video_path = detector.download_video(video_url)336                temp_files.append(video_path)337            # Step 2: Extract audio338            status_text.text("๐ŸŽต Extracting audio...")339            progress_bar.progress(50)340            audio_path = detector.extract_audio_simple(video_path)341            temp_files.append(audio_path)342            343            # Step 3: Transcribe344            status_text.text("๐ŸŽค Transcribing speech...")345            progress_bar.progress(75)346            transcript = detector.transcribe_audio(audio_path)347            348            # Step 4: Analyze349            status_text.text("๐Ÿ” Analyzing accent patterns...")350            progress_bar.progress(90)351            scores = detector.analyze_patterns(transcript)352            accent, confidence, explanation = detector.classify_accent(scores)353            354            # Complete355            progress_bar.progress(100)356            status_text.text("โœ… Analysis complete!")357            time.sleep(0.5)358            359            # Clear progress indicators360            progress_bar.empty()361            status_text.empty()362            363            # Display results364            st.success("๐ŸŽ‰ **Analysis Complete!**")365            366            # Main metrics367            col1, col2, col3 = st.columns(3)368            369            with col1:370                st.markdown(f"""371                <div class="metric-container">372                    <h3>๐Ÿ—ฃ๏ธ Detected Accent</h3>373                    <h2 style="color: #667eea;">{accent}</h2>374                </div>375                """, unsafe_allow_html=True)376            377            with col2:378                st.markdown(f"""379                <div class="metric-container">380                    <h3>๐ŸŽฏ Confidence</h3>381                    <h2 style="color: #764ba2;">{confidence}%</h2>382                </div>383                """, unsafe_allow_html=True)384            385            with col3:386                # Get transcript length for quality indicator387                word_count = len(transcript.split())388                quality = "High" if word_count > 50 else "Medium" if word_count > 20 else "Low"389                st.markdown(f"""390                <div class="metric-container">391                    <h3>๐Ÿ“Š Data Quality</h3>392                    <h2 style="color: #28a745;">{quality}</h2>393                    <small>{word_count} words</small>394                </div>395                """, unsafe_allow_html=True)396            397            st.markdown("---")398            399            # Explanation400            st.subheader("๐Ÿ“ Analysis Summary")401            st.info(explanation)402            403            # Transcript404            st.subheader("๐Ÿ“„ Transcribed Speech")405            st.text_area(406                "Full transcript:",407                transcript,408                height=120,409                help="This is what the AI heard from the video"410            )411            412            # Detailed scores413            st.subheader("๐Ÿ“Š All Accent Scores")414            415            # Create a more visual representation416            for accent_name, score in sorted(scores.items(), key=lambda x: x[1], reverse=True):417                # Create progress bar for each accent418                col_name, col_bar, col_score = st.columns([2, 6, 1])419                420                with col_name:421                    st.write(f"**{accent_name}**")422                423                with col_bar:424                    st.progress(score / 100)425                426                with col_score:427                    st.write(f"{score}%")428            429            # Additional insights430            if confidence > 60:431                st.success(f"๐ŸŽฏ **Strong Detection**: The {accent} accent markers are clearly present in the speech.")432            elif confidence > 40:433                st.warning(f"โš ๏ธ **Moderate Detection**: Some {accent} patterns detected, but results may vary with longer audio.")434            else:435                st.info("๐Ÿ’ก **Tip**: Longer speech samples (30+ seconds) generally provide more accurate results.")436            437        except Exception as e:438            st.error(f"โŒ **Processing Error**: {str(e)}")439            st.info("""440            **Troubleshooting Tips:**441            - Ensure the video URL is publicly accessible442            - Try a different video format or shorter video443            - Make sure the video contains clear English speech444            - Check your internet connection445            """)446        447        finally:448            # Cleanup temp files449            for temp_file in temp_files:450                try:451                    if os.path.exists(temp_file):452                        os.remove(temp_file)453                except:454                    pass455    456    # Footer information457    st.markdown("---")458    459    col1, col2 = st.columns(2)460    461    with col1:462        st.markdown("""463        **๐Ÿ”ง Technical Details**464        - Audio processing: Up to 2 minutes465        - Speech recognition: Google API466        - Analysis: Pattern matching + linguistics467        - Processing time: ~30-90 seconds468        """)469    470    with col2:471        st.markdown("""472        **๐Ÿ“‹ Requirements**473        - Public video URLs only474        - Clear English speech preferred475        - Supports MP4, MOV, AVI formats476        - Works with Loom, YouTube, direct links477        """)478    479    # API information480    with st.expander("๐Ÿ”— API Usage"):481        st.code("""482# Python API usage example483from accent_detector import AccentDetector484 485detector = AccentDetector()486result = detector.process_video("https://your-video.com/file.mp4")487 488print(f"Accent: {result['accent']}")489print(f"Confidence: {result['confidence']}%")490        """, language="python")491    492    # About section493    with st.expander("โ„น๏ธ About This Tool"):494        st.markdown("""495        **Built for REM Waste Interview Challenge**496        497        This accent detection tool analyzes English speech patterns to classify regional accents. 498        It's designed for hiring automation systems that need to evaluate spoken English proficiency.499        500        **Algorithm Overview:**501        - Extracts audio from video files502        - Transcribes speech using Google Speech Recognition503        - Analyzes linguistic patterns, vocabulary, and pronunciation markers504        - Provides confidence scores based on pattern strength505        506        **Accuracy Notes:**507        - Best results with 30+ seconds of clear speech508        - Confidence scores reflect pattern strength, not absolute accuracy509        - Designed for screening purposes, not definitive classification510        511        **Privacy & Ethics:**512        - No audio/video data is stored permanently513        - Temporary files are automatically deleted514        - Tool is intended for voluntary language assessment only515        """)516 517if __name__ == "__main__":518    main()