Aziz3/agent_decoder
0
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()