| | """ |
| | Enhanced Video Accent Analyzer |
| | Supports YouTube, Loom, direct MP4 links, and local video files with improved error handling and features. |
| | |
| | """ |
| |
|
| | import os |
| | import sys |
| | import tempfile |
| | import subprocess |
| | import requests |
| | import json |
| | import warnings |
| | import time |
| | from pathlib import Path |
| | from urllib.parse import urlparse |
| | import pandas as pd |
| | import matplotlib.pyplot as plt |
| | import seaborn as sns |
| | try: |
| | from IPython.display import display, HTML, Audio |
| | IPYTHON_AVAILABLE = True |
| | except ImportError: |
| | IPYTHON_AVAILABLE = False |
| | |
| | def display(*args, **kwargs): pass |
| | def HTML(*args, **kwargs): pass |
| | def Audio(*args, **kwargs): pass |
| |
|
| | |
| | warnings.filterwarnings('ignore') |
| |
|
| |
|
| | def install_if_missing(packages): |
| | """Install packages if they're not already available in Kaggle""" |
| | for package in packages: |
| | try: |
| | package_name = package.split('==')[0].replace('-', '_') |
| | if package_name == 'yt_dlp': |
| | package_name = 'yt_dlp' |
| | __import__(package_name) |
| | except ImportError: |
| | print(f"Installing {package}...") |
| | subprocess.check_call([sys.executable, "-m", "pip", "install", package, "--quiet"]) |
| |
|
| |
|
| | |
| | required_packages = [ |
| | "yt-dlp", |
| | "librosa", |
| | "soundfile", |
| | "transformers", |
| | "torch", |
| | "matplotlib", |
| | "seaborn" |
| | ] |
| |
|
| | print("๐ง Setting up environment...") |
| | install_if_missing(required_packages) |
| |
|
| | |
| | import torch |
| | from transformers import Wav2Vec2FeatureExtractor, Wav2Vec2ForSequenceClassification |
| | import librosa |
| | import soundfile as sf |
| | import yt_dlp |
| |
|
| |
|
| | class VideoAccentAnalyzer: |
| | def __init__(self, model_name="dima806/multiple_accent_classification"): |
| | """Initialize the accent analyzer for Kaggle environment""" |
| | self.model_name = model_name |
| | |
| | self.accent_labels = [ |
| | "british", "canadian", "us", "indian", "australian", "neutral" |
| | ] |
| | self.accent_display_names = { |
| | 'british': '๐ฌ๐ง British English', |
| | 'us': '๐บ๐ธ American English', |
| | 'australian': '๐ฆ๐บ Australian English', |
| | 'canadian': '๐จ๐ฆ Canadian English', |
| | 'indian': '๐ฎ๐ณ Indian English', |
| | 'neutral': '๐ Neutral English' |
| | } |
| | self.temp_dir = "/tmp/accent_analyzer" |
| | os.makedirs(self.temp_dir, exist_ok=True) |
| | self.model_loaded = False |
| | self._load_model() |
| |
|
| |
|
| |
|
| |
|
| |
|
| | def _load_model(self): |
| | """Load the accent classification model with error handling""" |
| | print("๐ค Loading accent classification model...") |
| | try: |
| | self.feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(self.model_name) |
| | self.model = Wav2Vec2ForSequenceClassification.from_pretrained(self.model_name) |
| | self.device = "cuda" if torch.cuda.is_available() else "cpu" |
| | self.model.to(self.device) |
| | self.model.eval() |
| | self.model_loaded = True |
| | print(f"โ
Model loaded successfully on {self.device}") |
| | except Exception as e: |
| | print(f"โ Error loading model: {e}") |
| | print("๐ก Tip: Check your internet connection and Kaggle environment setup") |
| | raise |
| |
|
| | def _validate_url(self, url): |
| | """Validate and normalize URL""" |
| | if not url or not isinstance(url, str): |
| | return False, "Invalid URL format" |
| |
|
| | url = url.strip() |
| | if not url.startswith(('http://', 'https://')): |
| | return False, "URL must start with http:// or https://" |
| |
|
| | return True, url |
| |
|
| | def trim_video(self, input_path, output_path, duration): |
| | try: |
| | cmd = ['ffmpeg', '-i', input_path, '-t', str(duration), '-c', 'copy', output_path, '-y'] |
| | result = subprocess.run(cmd, capture_output=True, text=True, timeout=60) |
| | if result.returncode == 0: |
| | print(f"โ๏ธ Trimmed video to {duration} seconds") |
| | return output_path |
| | else: |
| | print(f"โ Trimming failed: {result.stderr}") |
| | return input_path |
| | except Exception as e: |
| | print(f"โ Trimming exception: {e}") |
| | return input_path |
| |
|
| | def _get_youtube_cookies(self): |
| | """Get YouTube cookies from browser""" |
| | import browser_cookie3 |
| |
|
| | try: |
| | |
| | cookies = browser_cookie3.firefox(domain_name='.youtube.com') |
| | except: |
| | try: |
| | |
| | cookies = browser_cookie3.chrome(domain_name='.youtube.com') |
| | except: |
| | print("โ ๏ธ Could not get cookies from browser") |
| | return None |
| |
|
| | return cookies |
| |
|
| |
|
| |
|
| |
|
| |
|
| | def download_video(self, url, max_duration=None): |
| | """Download video using yt-dlp with cookie support""" |
| | is_valid, result = self._validate_url(url) |
| | if not is_valid: |
| | print(f"โ {result}") |
| | return None |
| |
|
| | url = result |
| | output_path = os.path.join(self.temp_dir, "video.%(ext)s") |
| |
|
| | |
| | ydl_opts = { |
| | 'outtmpl': output_path, |
| | 'format': 'worst[ext=mp4]/worst', |
| | 'quiet': False, |
| | 'no_warnings': False, |
| | 'socket_timeout': 60, |
| | 'retries': 5 |
| | } |
| |
|
| | |
| | if 'youtube.com' in url or 'youtu.be' in url: |
| | cookies = self._get_youtube_cookies() |
| | if cookies: |
| | cookie_file = os.path.join(self.temp_dir, 'cookies.txt') |
| | with open(cookie_file, 'w') as f: |
| | f.write('# Netscape HTTP Cookie File\n') |
| | for cookie in cookies: |
| | f.write(f'.youtube.com\tTRUE\t/\tFALSE\t{cookie.expires}\t{cookie.name}\t{cookie.value}\n') |
| | ydl_opts['cookiefile'] = cookie_file |
| |
|
| | if max_duration: |
| | ydl_opts['match_filter'] = lambda info: None if info.get('duration', 0) <= 200000 else "Video too long" |
| |
|
| | try: |
| | with yt_dlp.YoutubeDL(ydl_opts) as ydl: |
| | print(f"๐ฅ Downloading video from: {url}") |
| | start_time = time.time() |
| |
|
| | |
| | try: |
| | info = ydl.extract_info(url, download=False) |
| | print(f"๐บ Found video: {info.get('title', 'Unknown')} ({info.get('duration', 0)}s)") |
| | except Exception as e: |
| | print(f"โ ๏ธ Could not extract video info: {e}") |
| |
|
| | |
| | ydl.download([url]) |
| | download_time = time.time() - start_time |
| |
|
| | |
| | video_path = None |
| | for file in os.listdir(self.temp_dir): |
| | if file.startswith("video.") and os.path.getsize( |
| | os.path.join(self.temp_dir, file)) > 1000: |
| | potential_path = os.path.join(self.temp_dir, file) |
| | print(f"๐ Found downloaded file: {file} ({os.path.getsize(potential_path) / 1024:.1f}KB)") |
| |
|
| | |
| | if self._is_valid_video(potential_path): |
| | print(f"โ
Video validation passed: {file}") |
| | video_path = potential_path |
| | break |
| | else: |
| | print(f"โ ๏ธ Video validation failed, but continuing with: {file}") |
| | video_path = potential_path |
| | break |
| |
|
| | if video_path: |
| | print(f"โ
Downloaded video: {os.path.basename(video_path)} ({download_time:.1f}s)") |
| | return video_path |
| | else: |
| | print("โ No video files found after download") |
| | return None |
| |
|
| | except Exception as e: |
| | print(f"โ ๏ธ yt-dlp failed: {e}") |
| | return self._try_direct_download(url) |
| |
|
| | def _is_valid_video(self, file_path): |
| | """Verify video file has valid structure (more lenient)""" |
| | try: |
| | |
| | if not os.path.exists(file_path) or os.path.getsize(file_path) < 1000: |
| | return False |
| |
|
| | |
| | result = subprocess.run( |
| | ['ffprobe', '-v', 'quiet', '-print_format', 'json', '-show_format', file_path], |
| | capture_output=True, text=True, timeout=15 |
| | ) |
| |
|
| | if result.returncode == 0: |
| | try: |
| | |
| | info = json.loads(result.stdout) |
| | |
| | if 'format' in info and 'duration' in info['format']: |
| | return True |
| | except json.JSONDecodeError: |
| | pass |
| |
|
| | |
| | result2 = subprocess.run( |
| | ['ffmpeg', '-i', file_path, '-t', '1', '-f', 'null', '-', '-v', 'quiet'], |
| | capture_output=True, text=True, timeout=15 |
| | ) |
| |
|
| | return result2.returncode == 0 |
| |
|
| | except subprocess.TimeoutExpired: |
| | print("โ ๏ธ Video validation timed out, assuming valid") |
| | return True |
| | except Exception as e: |
| | print(f"โ ๏ธ Video validation error: {e}, assuming valid") |
| | return True |
| |
|
| | def _try_direct_download(self, url): |
| | """Enhanced fallback for direct video URLs""" |
| | try: |
| | print("๐ Trying direct download...") |
| | headers = { |
| | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' |
| | } |
| |
|
| | response = requests.get(url, stream=True, timeout=60, headers=headers) |
| | response.raise_for_status() |
| |
|
| | content_type = response.headers.get("Content-Type", "") |
| | if "text/html" in content_type: |
| | print("โ ๏ธ Received HTML instead of video - check URL access") |
| | return None |
| |
|
| | video_path = os.path.join(self.temp_dir, "video.mp4") |
| | file_size = 0 |
| |
|
| | with open(video_path, 'wb') as f: |
| | for chunk in response.iter_content(chunk_size=8192): |
| | if chunk: |
| | f.write(chunk) |
| | file_size += len(chunk) |
| |
|
| | print(f"๐ Downloaded {file_size / (1024 * 1024):.1f} MB") |
| |
|
| | if self._is_valid_video(video_path): |
| | print("โ
Direct download successful") |
| | return video_path |
| | else: |
| | print("โ Downloaded file is not a valid video") |
| | return None |
| |
|
| | except Exception as e: |
| | print(f"โ Direct download failed: {e}") |
| | return None |
| |
|
| | def extract_audio(self, video_path, max_duration=None): |
| | """Extract audio with improved error handling and progress""" |
| | audio_path = os.path.join(self.temp_dir, "audio.wav") |
| |
|
| | |
| | cmd = ['ffmpeg', '-i', video_path, '-vn', '-acodec', 'pcm_s16le', |
| | '-ar', '16000', '-ac', '1', '-y', '-v', 'warning'] |
| |
|
| | if max_duration: |
| | cmd.extend(['-t', str(max_duration)]) |
| | cmd.append(audio_path) |
| |
|
| | try: |
| | print(f"๐ต Extracting audio (max {max_duration}s)...") |
| | start_time = time.time() |
| |
|
| | |
| | result = subprocess.run(cmd, capture_output=True, text=True, timeout=180) |
| | extraction_time = time.time() - start_time |
| |
|
| | if result.returncode == 0 and os.path.exists(audio_path) and os.path.getsize(audio_path) > 1000: |
| | file_size = os.path.getsize(audio_path) / (1024 * 1024) |
| | print(f"โ
Audio extracted successfully ({extraction_time:.1f}s, {file_size:.1f}MB)") |
| | return audio_path |
| | else: |
| | print(f"โ FFmpeg stderr: {result.stderr}") |
| | print(f"โ FFmpeg stdout: {result.stdout}") |
| |
|
| | |
| | print("๐ Trying alternative audio extraction...") |
| | cmd_alt = ['ffmpeg', '-i', video_path, '-vn', '-acodec', 'libmp3lame', |
| | '-ar', '16000', '-ac', '1', '-y', '-v', 'warning'] |
| | if max_duration: |
| | cmd_alt.extend(['-t', str(max_duration)]) |
| |
|
| | audio_path_alt = os.path.join(self.temp_dir, "audio.mp3") |
| | cmd_alt.append(audio_path_alt) |
| |
|
| | result_alt = subprocess.run(cmd_alt, capture_output=True, text=True, timeout=180) |
| |
|
| | if result_alt.returncode == 0 and os.path.exists(audio_path_alt): |
| | |
| | cmd_convert = ['ffmpeg', '-i', audio_path_alt, '-ar', '16000', '-ac', '1', |
| | audio_path, '-y', '-v', 'quiet'] |
| | result_convert = subprocess.run(cmd_convert, capture_output=True, text=True, timeout=60) |
| |
|
| | if result_convert.returncode == 0 and os.path.exists(audio_path): |
| | file_size = os.path.getsize(audio_path) / (1024 * 1024) |
| | print(f"โ
Alternative extraction successful ({file_size:.1f}MB)") |
| | return audio_path |
| |
|
| | raise Exception(f"Both extraction methods failed. FFmpeg error: {result.stderr}") |
| |
|
| | except subprocess.TimeoutExpired: |
| | print("โ Audio extraction timed out") |
| | return None |
| | except Exception as e: |
| | print(f"โ Audio extraction failed: {e}") |
| | return None |
| |
|
| | def classify_accent(self, audio_path): |
| | """Enhanced accent classification with better preprocessing""" |
| | if not self.model_loaded: |
| | print("โ Model not loaded properly") |
| | return None |
| |
|
| | try: |
| | print("๐ Loading and preprocessing audio...") |
| | audio, sr = librosa.load(audio_path, sr=16000) |
| |
|
| | |
| | if len(audio) == 0: |
| | print("โ Empty audio file") |
| | return None |
| |
|
| | |
| | audio_trimmed, _ = librosa.effects.trim(audio, top_db=20) |
| |
|
| | |
| | chunk_size = 16000 * 20 |
| | chunks = [] |
| |
|
| | if len(audio_trimmed) > chunk_size: |
| | |
| | step_size = chunk_size // 2 |
| | for i in range(0, len(audio_trimmed) - chunk_size + 1, step_size): |
| | chunks.append(audio_trimmed[i:i + chunk_size]) |
| | if len(audio_trimmed) % step_size != 0: |
| | chunks.append(audio_trimmed[-chunk_size:]) |
| | else: |
| | chunks = [audio_trimmed] |
| |
|
| | print(f"๐ฏ Analyzing {len(chunks)} audio chunk(s)...") |
| |
|
| | all_predictions = [] |
| |
|
| | for i, chunk in enumerate(chunks[:3]): |
| | inputs = self.feature_extractor( |
| | chunk, |
| | sampling_rate=16000, |
| | return_tensors="pt", |
| | padding=True, |
| | max_length=16000 * 20, |
| | truncation=True |
| | ) |
| | inputs = {k: v.to(self.device) for k, v in inputs.items()} |
| |
|
| | with torch.no_grad(): |
| | outputs = self.model(**inputs) |
| | logits = outputs.logits |
| | probabilities = torch.nn.functional.softmax(logits, dim=-1) |
| | all_predictions.append(probabilities[0].cpu().numpy()) |
| |
|
| | |
| | avg_probabilities = sum(all_predictions) / len(all_predictions) |
| | predicted_idx = avg_probabilities.argmax() |
| | predicted_idx = min(predicted_idx, len(self.accent_labels) - 1) |
| |
|
| | |
| | english_accents = ["british", "canadian", "us", "australian", "indian"] |
| | english_confidence = sum( |
| | avg_probabilities[i] * 100 |
| | for i, label in enumerate(self.accent_labels) |
| | if label in english_accents |
| | ) |
| |
|
| | results = { |
| | 'predicted_accent': self.accent_labels[predicted_idx], |
| | 'accent_confidence': avg_probabilities[predicted_idx] * 100, |
| | 'english_confidence': english_confidence, |
| | 'audio_duration': len(audio) / 16000, |
| | 'processed_duration': len(audio_trimmed) / 16000, |
| | 'chunks_analyzed': len(all_predictions), |
| | 'all_probabilities': { |
| | self.accent_labels[i]: avg_probabilities[i] * 100 |
| | for i in range(len(self.accent_labels)) |
| | }, |
| | 'is_english_likely': english_confidence > 60, |
| | 'audio_quality_score': self._assess_audio_quality(audio_trimmed) |
| | } |
| |
|
| | print(f"โ
Classification complete ({results['chunks_analyzed']} chunks)") |
| | return results |
| |
|
| | except Exception as e: |
| | print(f"โ Classification failed: {e}") |
| | return None |
| |
|
| | def _assess_audio_quality(self, audio): |
| | """Assess audio quality for better result interpretation""" |
| | try: |
| | |
| | rms_energy = librosa.feature.rms(y=audio)[0].mean() |
| | zero_crossing_rate = librosa.feature.zero_crossing_rate(audio)[0].mean() |
| |
|
| | |
| | quality_score = min(100, (rms_energy * 1000 + (1 - zero_crossing_rate) * 50)) |
| | return max(0, quality_score) |
| | except: |
| | return 50 |
| |
|
| | def analyze_video_url(self, url, max_duration=30): |
| | """Complete pipeline with enhanced error handling""" |
| | print(f"๐ฌ Starting analysis of: {url}") |
| | print(f"โฑ๏ธ Max duration: {max_duration} seconds") |
| |
|
| | video_path = self.download_video(url, max_duration) |
| | if not video_path: |
| | return {"error": "Failed to download video", "url": url} |
| |
|
| | audio_path = self.extract_audio(video_path, max_duration) |
| | if not audio_path: |
| | return {"error": "Failed to extract audio", "url": url} |
| |
|
| | results = self.classify_accent(audio_path) |
| | if not results: |
| | return {"error": "Failed to classify accent", "url": url} |
| |
|
| | results.update({ |
| | 'source_url': url, |
| | 'video_file': os.path.basename(video_path), |
| | 'audio_file': os.path.basename(audio_path), |
| | 'analysis_timestamp': time.strftime('%Y-%m-%d %H:%M:%S') |
| | }) |
| |
|
| | return results |
| |
|
| | def analyze_local_video(self, file_path, max_duration=30): |
| | """Enhanced local video analysis""" |
| | print(f"๐ฌ Starting analysis of local file: {file_path}") |
| | print(f"โฑ๏ธ Max duration: {max_duration} seconds") |
| |
|
| | if not os.path.isfile(file_path): |
| | return {"error": f"File not found: {file_path}"} |
| |
|
| | |
| | file_size = os.path.getsize(file_path) / (1024 * 1024) |
| | print(f"๐ File size: {file_size:.1f} MB") |
| |
|
| | video_filename = os.path.basename(file_path) |
| | print(f"โ
Using local video: {video_filename}") |
| |
|
| | audio_path = self.extract_audio(file_path, max_duration) |
| | if not audio_path: |
| | return {"error": "Failed to extract audio"} |
| |
|
| | results = self.classify_accent(audio_path) |
| | if not results: |
| | return {"error": "Failed to classify accent"} |
| |
|
| | results.update({ |
| | 'source_file': file_path, |
| | 'video_file': video_filename, |
| | 'audio_file': os.path.basename(audio_path), |
| | 'file_size_mb': file_size, |
| | 'is_local': True, |
| | 'analysis_timestamp': time.strftime('%Y-%m-%d %H:%M:%S') |
| | }) |
| |
|
| | return results |
| |
|
| |
|
| | def display_results(self, results): |
| | """Display results in text format""" |
| | if 'error' in results: |
| | print(f"โ {results['error']}") |
| | return |
| |
|
| | accent = results['predicted_accent'] |
| | confidence = results['accent_confidence'] |
| | english_conf = results['english_confidence'] |
| | duration = results['audio_duration'] |
| | processed_duration = results.get('processed_duration', duration) |
| | quality_score = results.get('audio_quality_score', 50) |
| |
|
| | accent_display = self.accent_display_names.get(accent, accent.title()) |
| |
|
| | print(f"\n=== Accent Analysis Results ===") |
| | print(f"Predicted Accent: {accent_display}") |
| | print(f"Confidence: {confidence:.1f}%") |
| | print(f"English Confidence: {english_conf:.1f}%") |
| | print(f"Audio Duration: {duration:.1f}s") |
| | print(f"Processed Duration: {processed_duration:.1f}s") |
| | print(f"Audio Quality: {quality_score:.0f}/100") |
| | print(f"Chunks Analyzed: {results.get('chunks_analyzed', 1)}") |
| |
|
| |
|
| | def _plot_probabilities(self, probabilities): |
| | """Create a visualization of accent probabilities""" |
| | try: |
| | plt.figure(figsize=(10, 6)) |
| |
|
| | accents = [self.accent_display_names.get(acc, acc.title()) for acc in probabilities.keys()] |
| | probs = list(probabilities.values()) |
| |
|
| | |
| | colors = ['#4CAF50' if p == max(probs) else '#2196F3' if p >= 20 else '#FFC107' if p >= 10 else '#9E9E9E' |
| | for p in probs] |
| |
|
| | bars = plt.bar(accents, probs, color=colors, alpha=0.8, edgecolor='black', linewidth=0.5) |
| |
|
| | plt.title('Accent Classification Probabilities', fontsize=16, fontweight='bold', pad=20) |
| | plt.xlabel('Accent Type', fontsize=12) |
| | plt.ylabel('Probability (%)', fontsize=12) |
| | plt.xticks(rotation=45, ha='right') |
| | plt.grid(axis='y', alpha=0.3) |
| |
|
| | |
| | for bar, prob in zip(bars, probs): |
| | height = bar.get_height() |
| | plt.text(bar.get_x() + bar.get_width() / 2., height + 0.5, |
| | f'{prob:.1f}%', ha='center', va='bottom', fontweight='bold') |
| |
|
| | plt.tight_layout() |
| | plt.show() |
| |
|
| | except Exception as e: |
| | print(f"โ ๏ธ Could not create visualization: {e}") |
| |
|
| |
|
| | def batch_analyze(self, urls, max_duration=30): |
| | """Analyze multiple videos with progress tracking""" |
| | results = [] |
| | failed_count = 0 |
| |
|
| | print(f"๐ Starting batch analysis of {len(urls)} videos") |
| |
|
| | for i, url in enumerate(urls, 1): |
| | print(f"\n{'=' * 60}") |
| | print(f"Processing video {i}/{len(urls)}") |
| |
|
| | result = self.analyze_video_url(url, max_duration) |
| | result['video_index'] = i |
| |
|
| | if 'error' in result: |
| | failed_count += 1 |
| | print(f"โ Failed: {result['error']}") |
| | else: |
| | print(f"โ
Success: {result['predicted_accent']} ({result['accent_confidence']:.1f}%)") |
| |
|
| | results.append(result) |
| | self.display_results(result) |
| |
|
| | |
| | if i < len(urls): |
| | time.sleep(1) |
| |
|
| | |
| | success_count = len(urls) - failed_count |
| | print(f"\n๐ Batch Analysis Summary:") |
| | print(f" โ
Successful: {success_count}/{len(urls)}") |
| | print(f" โ Failed: {failed_count}/{len(urls)}") |
| |
|
| | return results |
| |
|
| |
|
| | def export_results(self, results, filename="accent_analysis_results.json"): |
| | """Export results to JSON file""" |
| | try: |
| | with open(filename, 'w') as f: |
| | json.dump(results, f, indent=2, default=str) |
| | print(f"๐พ Results exported to {filename}") |
| | except Exception as e: |
| | print(f"โ Export failed: {e}") |
| |
|
| |
|
| | def cleanup(self): |
| | """Clean up temporary files""" |
| | try: |
| | import shutil |
| | if os.path.exists(self.temp_dir): |
| | shutil.rmtree(self.temp_dir, ignore_errors=True) |
| | print("๐งน Cleaned up temporary files") |
| | except Exception as e: |
| | print(f"โ ๏ธ Cleanup warning: {e}") |
| |
|
| |
|
| | |
| | def show_examples(): |
| | """Show usage examples""" |
| | examples = { |
| | "Loom": "https://www.loom.com/share/abc123def456", |
| | "Direct MP4": "https://example.com/video.mp4", |
| | "Local File": "/local/input/dataset/video.mp4" |
| | } |
| |
|
| | print("\n๐ฏ Supported Video Formats:") |
| | for platform, example in examples.items(): |
| | print(f" {platform:12}: {example}") |
| |
|
| | print("\n๐ก Usage Tips:") |
| | print(" โข Keep videos under 2 minutes for best results") |
| | print(" โข Ensure clear audio quality") |
| | print(" โข Multiple speakers may affect accuracy") |
| | print(" โข Model works best with sustained speech") |
| |
|
| |
|
| | def quick_test_url(): |
| | """Interactive test for video URLs""" |
| | print("๐ Quick Test Mode for Video URLs") |
| | print("๐ฏ Supported: Loom, Direct MP4 links") |
| | print("๐ก Examples:") |
| | print(" Loom: https://www.loom.com/share/VIDEO_ID") |
| | print(" Direct: https://example.com/video.mp4") |
| |
|
| | url = input("\n๐ Enter your video URL (Loom, MP4 , etc.): ").strip() |
| | if not url: |
| | print("โ No URL provided.") |
| | return None |
| |
|
| | max_duration = input("โฑ๏ธ Max duration in seconds (default 20): ").strip() |
| | try: |
| | max_duration = int(max_duration) if max_duration else 20 |
| | except ValueError: |
| | max_duration = 20 |
| | print(f"โ ๏ธ Invalid duration, using {max_duration} seconds") |
| |
|
| | analyzer = VideoAccentAnalyzer() |
| | try: |
| | print(f"\n๐ Starting analysis...") |
| | results = analyzer.analyze_video_url(url, max_duration=max_duration) |
| | analyzer.display_results(results) |
| | return results |
| | finally: |
| | analyzer.cleanup() |
| |
|
| |
|
| | def demo_analysis(): |
| | """Demo function with example usage""" |
| | print("๐ฌ Video Accent Analyzer Demo") |
| | print("=" * 50) |
| |
|
| | |
| | analyzer = VideoAccentAnalyzer() |
| |
|
| | |
| | example_url = "https://example.com/video.mp4" |
| | print(f"\n๐ฏ Example: Analyzing {example_url}") |
| |
|
| | |
| | |
| | |
| | |
| |
|
| | print("\n๐ To use the analyzer:") |
| | print("1. analyzer = VideoAccentAnalyzer()") |
| | print("2. results = analyzer.analyze_video_url('your-url', max_duration=30)") |
| | print("3. analyzer.display_results(results)") |
| | print("4. analyzer.cleanup() # Clean up temporary files") |
| |
|
| |
|
| | |
| | show_examples() |
| |
|