Muktibhuyan commited on
Commit
26fa230
Β·
verified Β·
1 Parent(s): ca78672

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +495 -174
app.py CHANGED
@@ -6,11 +6,23 @@ import shutil
6
  from pathlib import Path
7
  import re
8
  import uuid
 
 
 
9
  session_data = {}
10
 
11
  class YouTubeDownloader:
12
  def __init__(self):
13
  self.download_dir = tempfile.mkdtemp()
 
 
 
 
 
 
 
 
 
14
 
15
  def is_valid_youtube_url(self, url):
16
  youtube_regex = re.compile(
@@ -19,214 +31,523 @@ class YouTubeDownloader:
19
  )
20
  return youtube_regex.match(url) is not None
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  def format_video_info(self, video_info):
23
- """Format video information into a readable report"""
24
  if not video_info:
25
  return "❌ No video information available."
26
-
27
- # Format duration
28
  duration = video_info.get('duration', 0)
29
  duration_str = f"{duration//3600}:{(duration%3600)//60:02d}:{duration%60:02d}" if duration else "Unknown"
30
 
31
- # Format upload date
32
  upload_date = video_info.get('upload_date', '')
33
- if upload_date and len(upload_date) == 8:
34
- formatted_date = f"{upload_date[:4]}-{upload_date[4:6]}-{upload_date[6:8]}"
35
- else:
36
- formatted_date = upload_date or "Unknown"
37
-
38
- # Format numbers
39
  def format_number(num):
40
- if num >= 1_000_000:
 
 
 
 
41
  return f"{num/1_000_000:.1f}M"
42
  elif num >= 1_000:
43
  return f"{num/1_000:.1f}K"
44
- else:
45
- return str(num)
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- # Build the report
48
- report = f"""
49
- πŸ“Ή VIDEO ANALYSIS REPORT
50
- {'='*50}
51
-
52
- πŸ“ BASIC INFORMATION:
53
- β€’ Title: {video_info.get('title', 'Unknown')}
54
- β€’ Channel: {video_info.get('channel', 'Unknown')}
55
- β€’ Uploader: {video_info.get('uploader', 'Unknown')}
56
- β€’ Upload Date: {formatted_date}
57
- β€’ Duration: {duration_str}
58
-
59
- πŸ“Š STATISTICS:
60
- β€’ Views: {format_number(video_info.get('view_count', 0))}
61
- β€’ Likes: {format_number(video_info.get('like_count', 0))}
62
- β€’ Comments: {format_number(video_info.get('comment_count', 0))}
63
- β€’ Channel Followers: {format_number(video_info.get('channel_followers', 0))}
64
-
65
- 🏷️ CATEGORIES & TAGS:
66
- β€’ Categories: {', '.join(video_info.get('categories', [])) or 'None'}
67
- β€’ Tags: {', '.join(video_info.get('tags', [])[:10]) or 'None'}
68
- {('β€’ More tags...' if len(video_info.get('tags', [])) > 10 else '')}
69
-
70
- πŸ“– DESCRIPTION:
71
- {video_info.get('description', 'No description available')[:500]}
72
- {'...' if len(video_info.get('description', '')) > 500 else ''}
73
-
74
- πŸ”— VIDEO URL:
75
- {video_info.get('webpage_url', 'Unknown')}
76
- """
77
 
78
- return report.strip()
 
 
 
 
 
 
 
79
 
80
- def get_video_info(self, url, progress=gr.Progress(), cookiefile=None):
81
- if not url or not url.strip():
82
- return None, "❌ Please enter a YouTube URL"
 
83
 
84
- if not self.is_valid_youtube_url(url):
85
- return None, "❌ Invalid YouTube URL. Please enter a valid YouTube video URL"
 
 
 
 
 
 
 
86
 
87
- try:
88
- progress(0.2, desc="Fetching video information...")
89
- ydl_opts = {
90
- 'noplaylist': True,
91
- 'extract_flat': False,
92
- }
93
- if cookiefile:
94
- ydl_opts['cookiefile'] = cookiefile
95
 
96
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
97
- try:
98
- info = ydl.extract_info(url, download=False)
99
- video_info = {
100
- 'title': info.get('title', 'Unknown'),
101
- 'description': info.get('description', 'No description available'),
102
- 'duration': info.get('duration', 0),
103
- 'view_count': info.get('view_count', 0),
104
- 'like_count': info.get('like_count', 0),
105
- 'comment_count': info.get('comment_count', 0),
106
- 'upload_date': info.get('upload_date', ''),
107
- 'uploader': info.get('uploader', 'Unknown'),
108
- 'channel': info.get('channel', 'Unknown'),
109
- 'channel_followers': info.get('channel_follower_count', 0),
110
- 'tags': info.get('tags', []),
111
- 'categories': info.get('categories', []),
112
- 'thumbnail': info.get('thumbnail', ''),
113
- 'webpage_url': info.get('webpage_url', url)
114
- }
115
- progress(1.0, desc="Information retrieved!")
116
- return video_info, "βœ… Video information retrieved successfully"
117
- except yt_dlp.DownloadError as e:
118
- error_msg = str(e)
119
- if "Video unavailable" in error_msg:
120
- return None, "❌ Video is unavailable or private"
121
- elif "age-restricted" in error_msg.lower():
122
- return None, "❌ Video is age-restricted"
123
- else:
124
- return None, f"❌ Failed to get video info: {error_msg}"
125
- except Exception as e:
126
- return None, f"❌ An unexpected error occurred: {str(e)}"
127
 
128
- def download_video(self, url, progress=gr.Progress(), cookiefile=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  if not url or not url.strip():
130
  return None, "❌ Please enter a YouTube URL"
131
-
132
  if not self.is_valid_youtube_url(url):
133
- return None, "❌ Invalid YouTube URL. Please enter a valid YouTube video URL"
134
-
135
  try:
136
- progress(0.1, desc="Initializing download...")
 
137
  ydl_opts = {
138
- 'format': 'best[ext=mp4]/best',
139
- 'outtmpl': os.path.join(self.download_dir, '%(title)s.%(ext)s'),
140
  'noplaylist': True,
 
 
 
 
141
  }
142
- if cookiefile:
 
143
  ydl_opts['cookiefile'] = cookiefile
144
-
145
- progress(0.3, desc="Fetching video information...")
 
 
146
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
147
- try:
148
- info = ydl.extract_info(url, download=False)
149
- video_title = info.get('title', 'Unknown')
150
- duration = info.get('duration', 0)
151
- progress(0.5, desc=f"Downloading: {video_title[:50]}...")
152
- ydl.download([url])
153
- progress(0.9, desc="Finalizing download...")
154
- downloaded_files = list(Path(self.download_dir).glob('*'))
155
- if downloaded_files:
156
- downloaded_file = downloaded_files[0]
157
- file_size = downloaded_file.stat().st_size / (1024 * 1024)
158
- progress(1.0, desc="Download completed!")
159
- success_message = f"βœ… Successfully downloaded: {video_title}\n"
160
- success_message += f"πŸ“ File size: {file_size:.1f} MB\n"
161
- success_message += f"⏱️ Duration: {duration//60}:{duration%60:02d}" if duration else ""
162
- return str(downloaded_file), success_message
163
- else:
164
- return None, "❌ Download completed but file not found"
165
- except yt_dlp.DownloadError as e:
166
- error_msg = str(e)
167
- if "Video unavailable" in error_msg:
168
- return None, "❌ Video is unavailable or private"
169
- elif "age-restricted" in error_msg.lower():
170
- return None, "❌ Video is age-restricted and cannot be downloaded"
171
- elif "copyright" in error_msg.lower():
172
- return None, "❌ Video cannot be downloaded due to copyright restrictions"
173
- else:
174
- return None, f"❌ Download failed: {error_msg}"
175
  except Exception as e:
176
- return None, f"❌ An unexpected error occurred: {str(e)}"
177
-
178
- def cleanup(self):
179
- try:
180
- shutil.rmtree(self.download_dir, ignore_errors=True)
181
- except:
182
- pass
183
 
 
184
  downloader = YouTubeDownloader()
185
 
186
- def create_interface():
187
- with gr.Blocks(title="YouTube Video Downloader & Analyzer") as demo:
188
- url_input = gr.Textbox(label="YouTube URL", placeholder="https://www.youtube.com/watch?v=...")
189
- cookies_input = gr.File(label="Upload cookies.txt (optional)", type="filepath", file_types=[".txt"])
190
- download_btn = gr.Button("Download Video")
191
- status_output = gr.Textbox(label="Download Status")
192
- file_output = gr.File(label="Downloaded Video", visible=False)
193
- analysis_btn = gr.Button("Show Analysis Results", visible=False)
194
- analysis_output = gr.Textbox(label="Video Analysis Results", visible=False, lines=20)
195
- video_info_state = gr.Textbox(value=False)
196
-
197
-
198
- def handle_download(url, cookies_path):
199
- if not url or not url.strip():
200
- return "Please enter a YouTube URL", gr.File(visible=False), gr.Button(visible=False), None
201
-
202
- cookiefile = cookies_path.strip() if cookies_path and os.path.exists(cookies_path.strip()) else None
203
- video_info, info_message = downloader.get_video_info(url, cookiefile=cookiefile)
204
- file_path, download_message = downloader.download_video(url, cookiefile=cookiefile)
205
- print(f"[DEBUG] Download message: {download_message}")
206
-
207
- if file_path and video_info:
208
- success_message = f"{download_message}\n\nVideo downloaded successfully! Click 'Show Analysis Results' to see detailed information."
209
- return success_message, gr.File(value=file_path, visible=True), gr.Button(visible=True), gr.State(video_info)
210
- elif file_path:
211
- return f"{download_message}\n\nVideo downloaded but analysis data unavailable.", gr.File(value=file_path, visible=True), gr.Button(visible=False), gr.State("")
212
- else:
213
- return f"❌ Download failed:\n{download_message}", gr.File(visible=False), gr.Button(visible=False), gr.State(None)
214
-
215
- def show_analysis(video_info):
216
- print("[DEBUG] Received session_id:", session_id)
217
- video_info = session_data.get(session_id)
218
- if video_info:
219
- return downloader.format_video_info(video_info), gr.Textbox(visible=True)
220
- return "❌ No analysis data available.", gr.Textbox(visible=True)
221
-
222
- download_btn.click(handle_download, inputs=[url_input, cookies_input], outputs=[status_output, file_output, analysis_btn, video_info_state])
223
- analysis_btn.click(show_analysis, inputs=[video_info_state], outputs=[analysis_output])
224
- url_input.submit(handle_download, inputs=[url_input, cookies_input], outputs=[status_output, file_output, analysis_btn, video_info_state])
225
 
226
- return demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  if __name__ == "__main__":
229
  demo = create_interface()
230
  import atexit
231
  atexit.register(downloader.cleanup)
232
- demo.launch()
 
 
 
 
 
 
6
  from pathlib import Path
7
  import re
8
  import uuid
9
+ import json
10
+ from datetime import datetime
11
+
12
  session_data = {}
13
 
14
  class YouTubeDownloader:
15
  def __init__(self):
16
  self.download_dir = tempfile.mkdtemp()
17
+
18
+ def cleanup(self):
19
+ """Clean up temporary directories and files"""
20
+ try:
21
+ if hasattr(self, 'download_dir') and os.path.exists(self.download_dir):
22
+ shutil.rmtree(self.download_dir)
23
+ print(f"βœ… Cleaned up temporary directory: {self.download_dir}")
24
+ except Exception as e:
25
+ print(f"⚠️ Warning: Could not clean up temporary directory: {e}")
26
 
27
  def is_valid_youtube_url(self, url):
28
  youtube_regex = re.compile(
 
31
  )
32
  return youtube_regex.match(url) is not None
33
 
34
+ def analyze_content_type(self, video_info):
35
+ """Analyze video content to determine type"""
36
+ title = video_info.get('title', '').lower()
37
+ description = video_info.get('description', '').lower()
38
+ tags = ' '.join(video_info.get('tags', [])).lower()
39
+
40
+ content_indicators = {
41
+ 'educational': ['tutorial', 'how to', 'learn', 'guide', 'explained', 'lesson', 'course', 'tips'],
42
+ 'promotional': ['ad', 'promo', 'launch', 'brand', 'sponsored', 'commercial', 'product'],
43
+ 'entertainment': ['funny', 'comedy', 'challenge', 'reaction', 'prank', 'meme', 'fun'],
44
+ 'review': ['review', 'unboxing', 'comparison', 'vs', 'test', 'rating'],
45
+ 'vlog': ['vlog', 'daily', 'routine', 'day in', 'life', 'personal'],
46
+ 'music': ['music', 'song', 'cover', 'remix', 'beats', 'audio'],
47
+ 'news': ['news', 'breaking', 'update', 'report', 'latest', 'current']
48
+ }
49
+
50
+ metadata = f"{title} {description} {tags}"
51
+
52
+ for category, keywords in content_indicators.items():
53
+ if any(keyword in metadata for keyword in keywords):
54
+ return category.title()
55
+
56
+ return "General"
57
+
58
+ def analyze_emotion(self, video_info):
59
+ """Analyze emotional tone of the video"""
60
+ title = video_info.get('title', '').lower()
61
+ description = video_info.get('description', '').lower()
62
+
63
+ emotion_indicators = {
64
+ 'energetic': ['excited', 'amazing', 'incredible', 'wow', 'awesome', 'fantastic', 'energy'],
65
+ 'positive': ['happy', 'love', 'great', 'good', 'wonderful', 'perfect', 'best'],
66
+ 'calm': ['calm', 'peaceful', 'relaxing', 'soothing', 'gentle', 'quiet'],
67
+ 'serious': ['important', 'serious', 'warning', 'critical', 'urgent', 'breaking'],
68
+ 'inspirational': ['inspire', 'motivate', 'change', 'transform', 'achieve', 'success']
69
+ }
70
+
71
+ metadata = f"{title} {description}"
72
+
73
+ for emotion, keywords in emotion_indicators.items():
74
+ if any(keyword in metadata for keyword in keywords):
75
+ return emotion.title()
76
+
77
+ return "Neutral"
78
+
79
+ def analyze_music_style(self, video_info):
80
+ """Analyze background music style"""
81
+ title = video_info.get('title', '').lower()
82
+ description = video_info.get('description', '').lower()
83
+ tags = ' '.join(video_info.get('tags', [])).lower()
84
+
85
+ metadata = f"{title} {description} {tags}"
86
+
87
+ music_styles = {
88
+ 'upbeat': ['upbeat', 'energetic', 'fast', 'dance', 'pop', 'electronic', 'rock'],
89
+ 'calm': ['calm', 'soft', 'soothing', 'ambient', 'peaceful', 'meditation', 'acoustic'],
90
+ 'cinematic': ['cinematic', 'dramatic', 'epic', 'orchestral', 'soundtrack'],
91
+ 'lo-fi': ['lo-fi', 'chill', 'study', 'relaxing beats'],
92
+ 'classical': ['classical', 'piano', 'orchestra', 'symphony']
93
+ }
94
+
95
+ for style, keywords in music_styles.items():
96
+ if any(keyword in metadata for keyword in keywords):
97
+ return style.title()
98
+
99
+ # Check if it's likely a music video
100
+ if any(word in metadata for word in ['music', 'song', 'audio', 'beats']):
101
+ return "Music Content"
102
+
103
+ return "Background Music Present" if 'music' in metadata else "Minimal/No Music"
104
+
105
+ def detect_influencers(self, video_info):
106
+ """Enhanced influencer detection"""
107
+ # Expanded list of known personalities
108
+ known_personalities = {
109
+ # Indian Film Industry
110
+ "Kartik Aaryan": ["kartik aaryan", "kartik", "aaryan"],
111
+ "Deepika Padukone": ["deepika padukone", "deepika"],
112
+ "Alia Bhatt": ["alia bhatt", "alia"],
113
+ "Ranveer Singh": ["ranveer singh", "ranveer"],
114
+ "Kiara Advani": ["kiara advani", "kiara"],
115
+ "Janhvi Kapoor": ["janhvi kapoor", "janhvi"],
116
+ "Ananya Panday": ["ananya panday", "ananya"],
117
+ "Salman Khan": ["salman khan", "salman"],
118
+ "Shahrukh Khan": ["shahrukh khan", "srk", "shah rukh"],
119
+ "Amitabh Bachchan": ["amitabh bachchan", "amitabh", "big b"],
120
+ "Katrina Kaif": ["katrina kaif", "katrina"],
121
+
122
+ # Sports Personalities
123
+ "Virat Kohli": ["virat kohli", "virat"],
124
+ "MS Dhoni": ["ms dhoni", "dhoni"],
125
+ "Rohit Sharma": ["rohit sharma", "rohit"],
126
+
127
+ # International Celebrities
128
+ "Taylor Swift": ["taylor swift", "taylor"],
129
+ "Kylie Jenner": ["kylie jenner", "kylie"],
130
+ "Elon Musk": ["elon musk", "elon"],
131
+
132
+ # YouTubers/Content Creators
133
+ "MrBeast": ["mrbeast", "mr beast"],
134
+ "PewDiePie": ["pewdiepie", "felix"],
135
+ "CarryMinati": ["carryminati", "carry", "ajey nagar"],
136
+ "Ashish Chanchlani": ["ashish chanchlani", "ashish"],
137
+ "Bhuvan Bam": ["bhuvan bam", "bb ki vines"],
138
+ "Prajakta Koli": ["prajakta koli", "mostlysane"],
139
+
140
+ # Tech Personalities
141
+ "Sundar Pichai": ["sundar pichai", "sundar"],
142
+
143
+ # Beauty/Fashion Influencers
144
+ "James Charles": ["james charles"],
145
+ "Nikkie Tutorials": ["nikkie tutorials", "nikkietutorials"]
146
+ }
147
+
148
+ # Combine all searchable text
149
+ searchable_text = " ".join([
150
+ video_info.get('title', ''),
151
+ video_info.get('description', ''),
152
+ video_info.get('uploader', ''),
153
+ video_info.get('channel', ''),
154
+ ' '.join(video_info.get('tags', []))
155
+ ]).lower()
156
+
157
+ detected_personalities = []
158
+
159
+ for personality, aliases in known_personalities.items():
160
+ if any(alias in searchable_text for alias in aliases):
161
+ detected_personalities.append(personality)
162
+
163
+ # Additional indicators
164
+ influencer_indicators = [
165
+ "influencer", "creator", "brand ambassador", "celebrity", "star",
166
+ "featured", "guest", "interview", "collaboration", "collab"
167
+ ]
168
+
169
+ has_influencer_indicators = any(indicator in searchable_text for indicator in influencer_indicators)
170
+
171
+ if detected_personalities:
172
+ return f"TRUE - Detected: {', '.join(detected_personalities)}"
173
+ elif has_influencer_indicators:
174
+ return "TRUE - Likely influencer/celebrity present (check video for confirmation)"
175
+ else:
176
+ return "FALSE - No known personalities detected"
177
+
178
+ def generate_scene_breakdown(self, video_info):
179
+ """Generate enhanced scene-by-scene breakdown"""
180
+ duration = video_info.get('duration', 0)
181
+ title = video_info.get('title', '').lower()
182
+ description = video_info.get('description', '').lower()
183
+
184
+ if not duration:
185
+ return ["**[Duration Unknown]**: Unable to generate timestamped breakdown - video duration not available"]
186
+
187
+ # Determine segment length based on video duration
188
+ if duration <= 30:
189
+ segment_length = 2 # 2-second segments for very short videos
190
+ elif duration <= 60:
191
+ segment_length = 5 # 5-second segments for short videos
192
+ elif duration <= 300: # 5 minutes
193
+ segment_length = 10 # 10-second segments
194
+ elif duration <= 900: # 15 minutes
195
+ segment_length = 15 # 15-second segments
196
+ else:
197
+ segment_length = 30 # 30-second segments for long videos
198
+
199
+ scenes = []
200
+
201
+ # Generate contextual scene descriptions based on video type
202
+ video_type = self.analyze_content_type(video_info).lower()
203
+
204
+ # Scene templates based on video type
205
+ scene_templates = {
206
+ 'educational': [
207
+ "Introduction and topic overview",
208
+ "Main content explanation with examples",
209
+ "Detailed demonstration or walkthrough",
210
+ "Key points summary and tips",
211
+ "Conclusion and call-to-action"
212
+ ],
213
+ 'promotional': [
214
+ "Brand/product introduction",
215
+ "Key features showcase",
216
+ "Benefits and advantages highlight",
217
+ "Social proof or testimonials",
218
+ "Call-to-action and closing"
219
+ ],
220
+ 'entertainment': [
221
+ "Opening hook and introduction",
222
+ "Main entertainment content",
223
+ "Peak moment or climax",
224
+ "Reaction or commentary",
225
+ "Closing and engagement request"
226
+ ],
227
+ 'review': [
228
+ "Product/service introduction",
229
+ "First impressions and unboxing",
230
+ "Detailed feature analysis",
231
+ "Pros and cons discussion",
232
+ "Final verdict and recommendation"
233
+ ],
234
+ 'vlog': [
235
+ "Daily routine introduction",
236
+ "Activity or event coverage",
237
+ "Personal commentary and thoughts",
238
+ "Interaction with others",
239
+ "Day wrap-up and reflection"
240
+ ]
241
+ }
242
+
243
+ templates = scene_templates.get(video_type, [
244
+ "Opening sequence",
245
+ "Main content delivery",
246
+ "Supporting information",
247
+ "Engagement moment",
248
+ "Conclusion"
249
+ ])
250
+
251
+ segment_count = min(duration // segment_length + 1, len(templates) * 2)
252
+
253
+ for i in range(segment_count):
254
+ start_time = i * segment_length
255
+ end_time = min(start_time + segment_length - 1, duration)
256
+
257
+ # Format timestamps
258
+ start_formatted = f"{start_time//60}:{start_time%60:02d}"
259
+ end_formatted = f"{end_time//60}:{end_time%60:02d}"
260
+
261
+ # Select appropriate template
262
+ template_index = min(i, len(templates) - 1)
263
+ base_description = templates[template_index]
264
+
265
+ # Add contextual details
266
+ if i == 0:
267
+ description = f"{base_description} - Video begins with title card/intro"
268
+ elif i == segment_count - 1:
269
+ description = f"{base_description} - Video concludes with end screen/outro"
270
+ else:
271
+ description = f"{base_description} - Continued content delivery"
272
+
273
+ # Add visual and audio cues
274
+ if 'music' in title or 'song' in title:
275
+ description += " [Music/audio content]"
276
+ elif 'tutorial' in title or 'how to' in title:
277
+ description += " [Instructional content with visual demonstrations]"
278
+
279
+ scenes.append(f"**[{start_formatted}-{end_formatted}]**: {description}")
280
+
281
+ return scenes
282
+
283
  def format_video_info(self, video_info):
284
+ """Enhanced video information formatting"""
285
  if not video_info:
286
  return "❌ No video information available."
287
+
288
+ # Basic information processing
289
  duration = video_info.get('duration', 0)
290
  duration_str = f"{duration//3600}:{(duration%3600)//60:02d}:{duration%60:02d}" if duration else "Unknown"
291
 
 
292
  upload_date = video_info.get('upload_date', '')
293
+ formatted_date = f"{upload_date[:4]}-{upload_date[4:6]}-{upload_date[6:8]}" if len(upload_date) == 8 else upload_date or "Unknown"
294
+
 
 
 
 
295
  def format_number(num):
296
+ if num is None or num == 0:
297
+ return "0"
298
+ if num >= 1_000_000_000:
299
+ return f"{num/1_000_000_000:.1f}B"
300
+ elif num >= 1_000_000:
301
  return f"{num/1_000_000:.1f}M"
302
  elif num >= 1_000:
303
  return f"{num/1_000:.1f}K"
304
+ return str(num)
305
+
306
+ # Enhanced analysis
307
+ scene_descriptions = self.generate_scene_breakdown(video_info)
308
+ music_style = self.analyze_music_style(video_info)
309
+ influencer_detection = self.detect_influencers(video_info)
310
+ video_type = self.analyze_content_type(video_info)
311
+ emotion = self.analyze_emotion(video_info)
312
+
313
+ # Additional metadata
314
+ thumbnail_url = video_info.get('thumbnail', '')
315
+ language = video_info.get('language', 'Unknown')
316
+ availability = video_info.get('availability', 'public')
317
 
318
+ # Categories and tags processing
319
+ categories = video_info.get('categories', [])
320
+ tags = video_info.get('tags', [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
+ # Engagement metrics
323
+ view_count = video_info.get('view_count', 0)
324
+ like_count = video_info.get('like_count', 0)
325
+ comment_count = video_info.get('comment_count', 0)
326
+
327
+ engagement_rate = 0
328
+ if view_count > 0 and like_count is not None:
329
+ engagement_rate = (like_count / view_count) * 100
330
 
331
+ # Generate comprehensive report
332
+ report = f"""
333
+ 🎬 COMPREHENSIVE VIDEO ANALYSIS REPORT
334
+ {'='*60}
335
 
336
+ πŸ“‹ BASIC INFORMATION
337
+ {'─'*30}
338
+ πŸ“Ή **Title:** {video_info.get('title', 'Unknown')}
339
+ πŸ“Ί **Channel:** {video_info.get('channel', 'Unknown')}
340
+ πŸ‘€ **Uploader:** {video_info.get('uploader', 'Unknown')}
341
+ πŸ“… **Upload Date:** {formatted_date}
342
+ ⏱️ **Duration:** {duration_str}
343
+ 🌐 **Language:** {language}
344
+ πŸ”“ **Availability:** {availability.title()}
345
 
346
+ πŸ“Š PERFORMANCE METRICS
347
+ {'─'*30}
348
+ πŸ‘€ **Views:** {format_number(view_count)}
349
+ πŸ‘ **Likes:** {format_number(like_count)}
350
+ πŸ’¬ **Comments:** {format_number(comment_count)}
351
+ πŸ‘₯ **Channel Subscribers:** {format_number(video_info.get('channel_followers', 0))}
352
+ πŸ“ˆ **Engagement Rate:** {engagement_rate:.2f}%
 
353
 
354
+ 🏷️ CONTENT CLASSIFICATION
355
+ {'─'*30}
356
+ πŸ“‚ **Categories:** {', '.join(categories) if categories else 'None specified'}
357
+ πŸ”– **Primary Tags:** {', '.join(tags[:8]) if tags else 'None specified'}
358
+ {('πŸ”– **Additional Tags:** ' + ', '.join(tags[8:16]) + ('...' if len(tags) > 16 else '')) if len(tags) > 8 else ''}
359
+
360
+ πŸ“ VIDEO DESCRIPTION
361
+ {'─'*30}
362
+ {video_info.get('description', 'No description available')[:800]}
363
+ {'...\n[Description truncated - Full description available in original video]' if len(video_info.get('description', '')) > 800 else ''}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
+ 🎬 DETAILED SCENE-BY-SCENE BREAKDOWN
366
+ {'─'*40}
367
+ {chr(10).join(scene_descriptions)}
368
+
369
+ 🎡 **Background Music Style:** {music_style}
370
+
371
+ πŸ‘€ **Influencer Present:** {influencer_detection}
372
+
373
+ πŸŽ₯ **Video Type:** {video_type}
374
+
375
+ 🎭 **Overall Emotion:** {emotion}
376
+
377
+ πŸ“± TECHNICAL DETAILS
378
+ {'─'*30}
379
+ πŸ”— **Video URL:** {video_info.get('webpage_url', 'Unknown')}
380
+ πŸ–ΌοΈ **Thumbnail:** {thumbnail_url if thumbnail_url else 'Not available'}
381
+ πŸ“± **Video ID:** {video_info.get('id', 'Unknown')}
382
+
383
+ ⚑ QUICK INSIGHTS
384
+ {'─'*30}
385
+ β€’ **Content Quality:** {'High' if view_count > 100000 else 'Medium' if view_count > 10000 else 'Growing'}
386
+ β€’ **Audience Engagement:** {'High' if engagement_rate > 5 else 'Medium' if engagement_rate > 1 else 'Low'}
387
+ β€’ **Viral Potential:** {'High' if view_count > 1000000 and engagement_rate > 3 else 'Medium' if view_count > 100000 else 'Standard'}
388
+ β€’ **Content Freshness:** {'Recent' if upload_date and upload_date >= '20240101' else 'Older Content'}
389
+
390
+ {'='*60}
391
+ πŸ“Š Analysis completed at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
392
+ """
393
+ return report.strip()
394
+
395
+ def get_video_info(self, url, progress=gr.Progress(), cookiefile=None):
396
+ """Extract video information with enhanced error handling"""
397
  if not url or not url.strip():
398
  return None, "❌ Please enter a YouTube URL"
399
+
400
  if not self.is_valid_youtube_url(url):
401
+ return None, "❌ Invalid YouTube URL format"
402
+
403
  try:
404
+ progress(0.1, desc="Initializing YouTube extractor...")
405
+
406
  ydl_opts = {
 
 
407
  'noplaylist': True,
408
+ 'extract_flat': False,
409
+ 'writesubtitles': False,
410
+ 'writeautomaticsub': False,
411
+ 'ignoreerrors': True,
412
  }
413
+
414
+ if cookiefile and os.path.exists(cookiefile):
415
  ydl_opts['cookiefile'] = cookiefile
416
+ progress(0.3, desc="Loading cookies for authentication...")
417
+
418
+ progress(0.5, desc="Extracting video metadata...")
419
+
420
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
421
+ info = ydl.extract_info(url, download=False)
422
+
423
+ progress(0.9, desc="Processing video information...")
424
+ progress(1.0, desc="βœ… Analysis complete!")
425
+
426
+ return info, "βœ… Video information extracted successfully"
427
+
428
+ except yt_dlp.DownloadError as e:
429
+ return None, f"❌ YouTube Download Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  except Exception as e:
431
+ return None, f"❌ Unexpected Error: {str(e)}"
 
 
 
 
 
 
432
 
433
+ # Initialize global downloader
434
  downloader = YouTubeDownloader()
435
 
436
+ def analyze_with_cookies(url, cookies_file, progress=gr.Progress()):
437
+ """Main analysis function with progress tracking"""
438
+ try:
439
+ progress(0.05, desc="Starting analysis...")
440
+
441
+ cookiefile = None
442
+ if cookies_file and os.path.exists(cookies_file):
443
+ cookiefile = cookies_file
444
+ progress(0.1, desc="Cookies file loaded successfully")
445
+
446
+ info, msg = downloader.get_video_info(url, progress=progress, cookiefile=cookiefile)
447
+
448
+ if info:
449
+ progress(0.95, desc="Generating comprehensive report...")
450
+ formatted_info = downloader.format_video_info(info)
451
+ progress(1.0, desc="βœ… Complete!")
452
+ return formatted_info
453
+ else:
454
+ return f"❌ Analysis Failed: {msg}"
455
+
456
+ except Exception as e:
457
+ return f"❌ System Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
 
459
+ def create_interface():
460
+ """Create and configure the Gradio interface"""
461
+ with gr.Blocks(
462
+ theme=gr.themes.Soft(),
463
+ title="πŸŽ₯ YouTube Video Analyzer Pro",
464
+ css="""
465
+ .gradio-container {
466
+ max-width: 1200px !important;
467
+ }
468
+ .main-header {
469
+ text-align: center;
470
+ background: linear-gradient(90deg, #ff6b6b, #4ecdc4);
471
+ -webkit-background-clip: text;
472
+ -webkit-text-fill-color: transparent;
473
+ font-size: 2.5em;
474
+ font-weight: bold;
475
+ margin-bottom: 20px;
476
+ }
477
+ .description-text {
478
+ text-align: center;
479
+ font-size: 1.1em;
480
+ color: #666;
481
+ margin-bottom: 30px;
482
+ }
483
+ """
484
+ ) as interface:
485
+
486
+ gr.HTML("""
487
+ <div class="main-header">
488
+ πŸŽ₯ YouTube Video Analyzer Pro
489
+ </div>
490
+ <div class="description-text">
491
+ Get comprehensive analysis of any YouTube video with detailed scene breakdowns,
492
+ influencer detection, emotion analysis, and performance metrics.
493
+ Upload cookies.txt to access age-restricted or private videos.
494
+ </div>
495
+ """)
496
+
497
+ with gr.Row():
498
+ with gr.Column(scale=2):
499
+ url_input = gr.Textbox(
500
+ label="πŸ”— YouTube URL",
501
+ placeholder="Paste your YouTube video URL here...",
502
+ lines=1
503
+ )
504
+
505
+ with gr.Column(scale=1):
506
+ cookies_input = gr.File(
507
+ label="πŸͺ Upload cookies.txt (Optional)",
508
+ file_types=[".txt"],
509
+ type="filepath"
510
+ )
511
+
512
+ analyze_btn = gr.Button(
513
+ "πŸ” Analyze Video",
514
+ variant="primary",
515
+ size="lg"
516
+ )
517
+
518
+ output = gr.Textbox(
519
+ label="πŸ“Š Comprehensive Analysis Report",
520
+ lines=35,
521
+ max_lines=50,
522
+ show_copy_button=True
523
+ )
524
+
525
+ analyze_btn.click(
526
+ fn=analyze_with_cookies,
527
+ inputs=[url_input, cookies_input],
528
+ outputs=output,
529
+ show_progress=True
530
+ )
531
+
532
+ # Add examples
533
+ gr.Examples(
534
+ examples=[
535
+ ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
536
+ ["https://youtu.be/jNQXAC9IVRw"],
537
+ ],
538
+ inputs=url_input,
539
+ label="🎯 Try these examples:"
540
+ )
541
+
542
+ return interface
543
 
544
  if __name__ == "__main__":
545
  demo = create_interface()
546
  import atexit
547
  atexit.register(downloader.cleanup)
548
+ demo.launch(
549
+ server_name="0.0.0.0",
550
+ server_port=7860,
551
+ share=False,
552
+ show_error=True
553
+ )