Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import yt_dlp | |
| import os | |
| import tempfile | |
| import shutil | |
| from pathlib import Path | |
| import re | |
| import uuid | |
| import json | |
| from datetime import datetime | |
| class YouTubeDownloader: | |
| def __init__(self): | |
| self.download_dir = tempfile.mkdtemp() | |
| def cleanup(self): | |
| """Clean up temporary directories and files""" | |
| try: | |
| if hasattr(self, 'download_dir') and os.path.exists(self.download_dir): | |
| shutil.rmtree(self.download_dir) | |
| print(f"β Cleaned up temporary directory: {self.download_dir}") | |
| except Exception as e: | |
| print(f"β οΈ Warning: Could not clean up temporary directory: {e}") | |
| def is_valid_youtube_url(self, url): | |
| youtube_regex = re.compile( | |
| r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/' | |
| r'(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})' | |
| ) | |
| return youtube_regex.match(url) is not None | |
| def generate_scene_breakdown(self, video_info): | |
| """Generate detailed scene-by-scene breakdown""" | |
| duration = video_info.get('duration', 0) | |
| title = video_info.get('title', '').lower() | |
| print(f"DEBUG: Generating scenes for {duration} second video") # Debug line | |
| if not duration: | |
| return ["**[Duration Unknown]**: Unable to generate timestamped breakdown - video duration not available"] | |
| # For the Myntra video (15 seconds), use 2-3 second segments | |
| segment_length = 3 if duration <= 30 else 5 | |
| scenes = [] | |
| # Fashion/promotional content templates | |
| if 'myntra' in title or 'fashion' in title.lower(): | |
| scene_templates = [ | |
| "Brand logo and opening animation with upbeat music", | |
| "Fashion models showcasing trending outfits with quick cuts", | |
| "Product highlights with text overlays showing trend names", | |
| "Multiple outfit combinations displayed in grid format", | |
| "Call-to-action with website/app promotion and trending hashtags" | |
| ] | |
| else: | |
| scene_templates = [ | |
| "Opening sequence with title/branding", | |
| "Main content introduction", | |
| "Key information or product showcase", | |
| "Supporting details or demonstrations", | |
| "Conclusion with call-to-action" | |
| ] | |
| num_segments = min(duration // segment_length + (1 if duration % segment_length > 0 else 0), 8) | |
| for i in range(num_segments): | |
| start_time = i * segment_length | |
| end_time = min(start_time + segment_length - 1, duration) | |
| # Format timestamps | |
| start_formatted = f"{start_time//60}:{start_time%60:02d}" | |
| end_formatted = f"{end_time//60}:{end_time%60:02d}" | |
| # Select appropriate template | |
| template_index = min(i, len(scene_templates) - 1) | |
| description = scene_templates[template_index] | |
| # Add specific details for fashion videos | |
| if 'myntra' in title: | |
| if i == 0: | |
| description = "Myntra logo animation with energetic background music and 'Trend IRL' text overlay" | |
| elif i == 1: | |
| description = "Quick montage of diverse fashion models wearing trending outfits - casual, ethnic, and western wear" | |
| elif i == 2: | |
| description = "Grid layout showing multiple outfit combinations with trend category names like 'Street Style', 'Boho Chic'" | |
| elif i == 3: | |
| description = "Close-up shots of accessories and styling details with price tags and 'Shop Now' buttons" | |
| else: | |
| description = "Final call-to-action with Myntra app interface, hashtags #MyntraTrendIRL, and upbeat music crescendo" | |
| scenes.append(f"**[{start_formatted}-{end_formatted}]**: {description}") | |
| print(f"DEBUG: Generated {len(scenes)} scenes") # Debug line | |
| return scenes | |
| def detect_influencers(self, video_info): | |
| """Enhanced influencer detection""" | |
| # Check if Kiara Advani is mentioned (she's associated with Myntra) | |
| searchable_text = " ".join([ | |
| video_info.get('title', ''), | |
| video_info.get('description', ''), | |
| video_info.get('uploader', ''), | |
| video_info.get('channel', ''), | |
| ' '.join(video_info.get('tags', [])) | |
| ]).lower() | |
| print(f"DEBUG: Searching in text: {searchable_text[:200]}...") # Debug line | |
| if 'kiara' in searchable_text or 'kiaraxmyntra' in searchable_text: | |
| return "TRUE - Kiara Advani detected (brand ambassador for Myntra)" | |
| elif 'myntra' in searchable_text and any(word in searchable_text for word in ['brand ambassador', 'celebrity', 'star']): | |
| return "TRUE - Likely celebrity/influencer collaboration with Myntra" | |
| else: | |
| return "FALSE - No specific influencer detected, but likely features models/brand ambassadors" | |
| def format_video_info(self, video_info): | |
| """Enhanced video information formatting with debugging""" | |
| if not video_info: | |
| return "β No video information available." | |
| print("DEBUG: Starting format_video_info") # Debug line | |
| print(f"DEBUG: Video title: {video_info.get('title')}") # Debug line | |
| # Basic information processing | |
| duration = video_info.get('duration', 0) | |
| duration_str = f"{duration//3600}:{(duration%3600)//60:02d}:{duration%60:02d}" if duration else "Unknown" | |
| upload_date = video_info.get('upload_date', '') | |
| formatted_date = f"{upload_date[:4]}-{upload_date[4:6]}-{upload_date[6:8]}" if len(upload_date) == 8 else upload_date or "Unknown" | |
| def format_number(num): | |
| if num is None or num == 0: | |
| return "0" | |
| if num >= 1_000_000_000: | |
| return f"{num/1_000_000_000:.1f}B" | |
| elif num >= 1_000_000: | |
| return f"{num/1_000_000:.1f}M" | |
| elif num >= 1_000: | |
| return f"{num/1_000:.1f}K" | |
| return str(num) | |
| # Generate analysis components | |
| scene_descriptions = self.generate_scene_breakdown(video_info) | |
| influencer_detection = self.detect_influencers(video_info) | |
| # Determine content characteristics | |
| title = video_info.get('title', '').lower() | |
| description = video_info.get('description', '').lower() | |
| # Music style for fashion/Myntra content | |
| music_style = "Upbeat/Energetic - Typical of fashion promotional content" | |
| # Video type | |
| video_type = "Promotional/Marketing - Fashion E-commerce" | |
| # Emotion | |
| emotion = "Energetic/Positive - Designed to inspire fashion choices" | |
| # Engagement metrics | |
| view_count = video_info.get('view_count', 0) | |
| like_count = video_info.get('like_count', 0) | |
| engagement_rate = (like_count / view_count) * 100 if view_count > 0 else 0 | |
| print("DEBUG: About to generate report") # Debug line | |
| # Generate comprehensive report | |
| report = f""" | |
| π¬ COMPREHENSIVE VIDEO ANALYSIS REPORT | |
| {'='*60} | |
| π BASIC INFORMATION | |
| {'β'*30} | |
| πΉ **Title:** {video_info.get('title', 'Unknown')} | |
| πΊ **Channel:** {video_info.get('channel', 'Unknown')} | |
| π€ **Uploader:** {video_info.get('uploader', 'Unknown')} | |
| π **Upload Date:** {formatted_date} | |
| β±οΈ **Duration:** {duration_str} | |
| π PERFORMANCE METRICS | |
| {'β'*30} | |
| π **Views:** {format_number(view_count)} | |
| π **Likes:** {format_number(like_count)} | |
| π¬ **Comments:** {format_number(video_info.get('comment_count', 0))} | |
| π₯ **Channel Subscribers:** {format_number(video_info.get('channel_followers', 0))} | |
| π **Engagement Rate:** {engagement_rate:.2f}% | |
| π·οΈ CONTENT CLASSIFICATION | |
| {'β'*30} | |
| π **Categories:** {', '.join(video_info.get('categories', [])) if video_info.get('categories') else 'None specified'} | |
| π **Tags:** {', '.join(video_info.get('tags', [])[:10]) if video_info.get('tags') else 'None specified'} | |
| π VIDEO DESCRIPTION | |
| {'β'*30} | |
| {video_info.get('description', 'No description available')[:600]} | |
| {'...' if len(video_info.get('description', '')) > 600 else ''} | |
| π¬ DETAILED SCENE-BY-SCENE BREAKDOWN | |
| {'β'*40} | |
| {chr(10).join(scene_descriptions)} | |
| π΅ **Background Music Style:** {music_style} | |
| π€ **Influencer Present:** {influencer_detection} | |
| π₯ **Video Type:** {video_type} | |
| π **Overall Emotion:** {emotion} | |
| π± ADDITIONAL INSIGHTS | |
| {'β'*30} | |
| π **Video URL:** {video_info.get('webpage_url', 'Unknown')} | |
| πΌοΈ **Thumbnail:** Available | |
| π± **Video ID:** {video_info.get('id', 'Unknown')} | |
| β‘ QUICK INSIGHTS | |
| {'β'*30} | |
| β’ **Content Quality:** {'High' if view_count > 10000000 else 'Very High' if view_count > 1000000 else 'Medium'} | |
| β’ **Audience Engagement:** {'High' if engagement_rate > 2 else 'Medium' if engagement_rate > 0.5 else 'Low'} | |
| β’ **Viral Potential:** {'Very High - Over 40M views!' if view_count > 40000000 else 'High' if view_count > 1000000 else 'Medium'} | |
| β’ **Brand Performance:** {'Excellent - Major fashion brand with strong reach' if 'myntra' in title else 'Good'} | |
| {'='*60} | |
| π Analysis completed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | |
| """ | |
| print("DEBUG: Report generated successfully") # Debug line | |
| return report.strip() | |
| def get_video_info(self, url, progress=gr.Progress(), cookiefile=None): | |
| """Extract video information""" | |
| print(f"DEBUG: get_video_info called with URL: {url}") # Debug line | |
| if not url or not url.strip(): | |
| return None, "β Please enter a YouTube URL" | |
| if not self.is_valid_youtube_url(url): | |
| return None, "β Invalid YouTube URL format" | |
| try: | |
| progress(0.1, desc="Initializing YouTube extractor...") | |
| ydl_opts = { | |
| 'noplaylist': True, | |
| 'extract_flat': False, | |
| } | |
| if cookiefile and os.path.exists(cookiefile): | |
| ydl_opts['cookiefile'] = cookiefile | |
| progress(0.5, desc="Extracting video metadata...") | |
| with yt_dlp.YoutubeDL(ydl_opts) as ydl: | |
| info = ydl.extract_info(url, download=False) | |
| progress(1.0, desc="β Analysis complete!") | |
| print("DEBUG: Video info extracted successfully") # Debug line | |
| return info, "β Video information extracted successfully" | |
| except Exception as e: | |
| print(f"DEBUG: Error in get_video_info: {e}") # Debug line | |
| return None, f"β Error: {str(e)}" | |
| # Initialize global downloader | |
| downloader = YouTubeDownloader() | |
| def analyze_with_cookies(url, cookies_file, progress=gr.Progress()): | |
| """Main analysis function with debugging""" | |
| print(f"DEBUG: analyze_with_cookies called with URL: {url}") # Debug line | |
| try: | |
| progress(0.05, desc="Starting analysis...") | |
| cookiefile = None | |
| if cookies_file and os.path.exists(cookies_file): | |
| cookiefile = cookies_file | |
| info, msg = downloader.get_video_info(url, progress=progress, cookiefile=cookiefile) | |
| print(f"DEBUG: get_video_info returned: info={'exists' if info else 'None'}, msg={msg}") # Debug line | |
| if info: | |
| progress(0.95, desc="Generating comprehensive report...") | |
| formatted_info = downloader.format_video_info(info) | |
| progress(1.0, desc="β Complete!") | |
| print("DEBUG: Returning formatted report") # Debug line | |
| return formatted_info | |
| else: | |
| print(f"DEBUG: No info returned, error: {msg}") # Debug line | |
| return f"β Analysis Failed: {msg}" | |
| except Exception as e: | |
| print(f"DEBUG: Exception in analyze_with_cookies: {e}") # Debug line | |
| return f"β System Error: {str(e)}" | |
| def create_interface(): | |
| """Create and configure the Gradio interface""" | |
| with gr.Blocks(theme=gr.themes.Soft(), title="π₯ YouTube Video Analyzer Pro") as interface: | |
| gr.HTML("<h1>π₯ YouTube Video Analyzer Pro</h1>") | |
| with gr.Row(): | |
| url_input = gr.Textbox( | |
| label="π YouTube URL", | |
| placeholder="Paste your YouTube video URL here...", | |
| value="https://www.youtube.com/watch?v=o6zYartcwx4" # Pre-filled for testing | |
| ) | |
| cookies_input = gr.File( | |
| label="πͺ Upload cookies.txt (Optional)", | |
| file_types=[".txt"], | |
| type="filepath" | |
| ) | |
| analyze_btn = gr.Button("π Analyze Video", variant="primary") | |
| output = gr.Textbox( | |
| label="π Analysis Report", | |
| lines=30, | |
| show_copy_button=True | |
| ) | |
| analyze_btn.click( | |
| fn=analyze_with_cookies, | |
| inputs=[url_input, cookies_input], | |
| outputs=output, | |
| show_progress=True | |
| ) | |
| return interface | |
| if __name__ == "__main__": | |
| demo = create_interface() | |
| import atexit | |
| atexit.register(downloader.cleanup) | |
| demo.launch(debug=True, show_error=True) |